From 77948a247720cb41f615931db8a52fbd7c137f92 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Mon, 23 Feb 2026 05:25:24 -0800 Subject: [PATCH 1/5] metro-file-map: Make plugin.bulkUpdate synchronous Summary: There's no need for this to be async and making it synchronous simplifies some upcoming work Changelog: Internal Reviewed By: vzaidman Differential Revision: D92007667 fbshipit-source-id: 7e4f764d5750de45041b189e5518e5c081ee8cf4 --- .../cache/__tests__/DiskCacheManager-test.js | 2 +- packages/metro-file-map/src/flow-types.js | 2 +- packages/metro-file-map/src/index.js | 25 ++++++++----------- .../src/plugins/DependencyPlugin.js | 2 +- .../metro-file-map/src/plugins/HastePlugin.js | 2 +- .../metro-file-map/src/plugins/MockPlugin.js | 4 +-- .../haste/__tests__/HastePlugin-test.js | 4 +-- packages/metro-file-map/types/flow-types.d.ts | 4 +-- .../types/plugins/DependencyPlugin.d.ts | 2 +- .../types/plugins/HastePlugin.d.ts | 2 +- .../types/plugins/MockPlugin.d.ts | 2 +- 11 files changed, 22 insertions(+), 29 deletions(-) diff --git a/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js b/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js index 09c75d17d6..7732a69a6c 100644 --- a/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js +++ b/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js @@ -105,7 +105,7 @@ describe('cacheManager', () => { let pluginCacheKey = 'foo'; const plugin: FileMapPlugin<> = { name: 'foo', - async bulkUpdate() {}, + bulkUpdate() {}, async initialize() {}, assertValid() {}, getSerializableSnapshot() { diff --git a/packages/metro-file-map/src/flow-types.js b/packages/metro-file-map/src/flow-types.js index 63c9ad75b9..7b57120045 100644 --- a/packages/metro-file-map/src/flow-types.js +++ b/packages/metro-file-map/src/flow-types.js @@ -213,7 +213,7 @@ export interface FileMapPlugin< initOptions: FileMapPluginInitOptions, ): Promise; assertValid(): void; - bulkUpdate(delta: FileMapDelta): Promise; + bulkUpdate(delta: FileMapDelta): void; getSerializableSnapshot(): SerializableState; onRemovedFile(relativeFilePath: string, pluginData: ?PerFileData): void; onNewOrModifiedFile(relativeFilePath: string, pluginData: ?PerFileData): void; diff --git a/packages/metro-file-map/src/index.js b/packages/metro-file-map/src/index.js index 012a7d83b9..2f1c6507c5 100644 --- a/packages/metro-file-map/src/index.js +++ b/packages/metro-file-map/src/index.js @@ -667,21 +667,16 @@ export default class FileMap extends EventEmitter { this.#startupPerfLogger?.point('applyFileDelta_add_end'); this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_start'); - - await Promise.all([ - plugins.map(({plugin, dataIdx}) => { - const mapFn: ( - [CanonicalPath, FileMetadata], - ) => [CanonicalPath, unknown] = - dataIdx != null - ? ([relativePath, fileData]) => [relativePath, fileData[dataIdx]] - : ([relativePath, fileData]) => [relativePath, null]; - return plugin.bulkUpdate({ - addedOrModified: mapIterator(changedFiles.entries(), mapFn), - removed: mapIterator(removed.values(), mapFn), - }); - }), - ]); + plugins.forEach(({plugin, dataIdx}) => { + const mapFn: ([CanonicalPath, FileMetadata]) => [CanonicalPath, unknown] = + dataIdx != null + ? ([relativePath, fileData]) => [relativePath, fileData[dataIdx]] + : ([relativePath, fileData]) => [relativePath, null]; + plugin.bulkUpdate({ + addedOrModified: mapIterator(changedFiles.entries(), mapFn), + removed: mapIterator(removed.values(), mapFn), + }); + }); this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_end'); this.#startupPerfLogger?.point('applyFileDelta_end'); } diff --git a/packages/metro-file-map/src/plugins/DependencyPlugin.js b/packages/metro-file-map/src/plugins/DependencyPlugin.js index a92330db72..469624ce83 100644 --- a/packages/metro-file-map/src/plugins/DependencyPlugin.js +++ b/packages/metro-file-map/src/plugins/DependencyPlugin.js @@ -64,7 +64,7 @@ export default class DependencyPlugin return null; } - async bulkUpdate(delta: FileMapDelta>): Promise { + bulkUpdate(delta: FileMapDelta>): void { // No-op: Worker already populated plugin data // Plugin data is write-only from worker } diff --git a/packages/metro-file-map/src/plugins/HastePlugin.js b/packages/metro-file-map/src/plugins/HastePlugin.js index 6f11c93877..89b9b56a0f 100644 --- a/packages/metro-file-map/src/plugins/HastePlugin.js +++ b/packages/metro-file-map/src/plugins/HastePlugin.js @@ -237,7 +237,7 @@ export default class HastePlugin ); } - async bulkUpdate(delta: FileMapDelta): Promise { + bulkUpdate(delta: FileMapDelta): void { // Process removals first so that moves aren't treated as duplicates. for (const [normalPath, maybeHasteId] of delta.removed) { this.onRemovedFile(normalPath, maybeHasteId); diff --git a/packages/metro-file-map/src/plugins/MockPlugin.js b/packages/metro-file-map/src/plugins/MockPlugin.js index 68a22c7793..bf0baaa995 100644 --- a/packages/metro-file-map/src/plugins/MockPlugin.js +++ b/packages/metro-file-map/src/plugins/MockPlugin.js @@ -79,7 +79,7 @@ export default class MockPlugin this.#raw = pluginState; } else { // Otherwise, traverse all files to rebuild - await this.bulkUpdate({ + this.bulkUpdate({ addedOrModified: [ ...files.fileIterator({ includeNodeModules: false, @@ -102,7 +102,7 @@ export default class MockPlugin ); } - async bulkUpdate(delta: FileMapDelta<>): Promise { + bulkUpdate(delta: FileMapDelta<>): void { // Process removals first so that moves aren't treated as duplicates. for (const [relativeFilePath] of delta.removed) { this.onRemovedFile(relativeFilePath); diff --git a/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js b/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js index 9228e0341f..b1d8fa8e28 100644 --- a/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js +++ b/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js @@ -141,11 +141,11 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { expect(hasteMap.getModule('Bar')).not.toBeNull(); }); - test('fixes duplicates, adds and removes modules', async () => { + test('fixes duplicates, adds and removes modules', () => { expect(() => hasteMap.getModule('Duplicate')).toThrow( DuplicateHasteCandidatesError, ); - await hasteMap.bulkUpdate({ + hasteMap.bulkUpdate({ removed: [ [p('project/Duplicate.js'), 'Duplicate'], [p('project/Foo.js'), 'NameForFoo'], diff --git a/packages/metro-file-map/types/flow-types.d.ts b/packages/metro-file-map/types/flow-types.d.ts index 9788284280..342a9a7fc1 100644 --- a/packages/metro-file-map/types/flow-types.d.ts +++ b/packages/metro-file-map/types/flow-types.d.ts @@ -176,9 +176,7 @@ export interface FileMapPlugin< initOptions: FileMapPluginInitOptions, ): Promise; assertValid(): void; - bulkUpdate( - delta: FileMapDelta, - ): Promise; + bulkUpdate(delta: FileMapDelta): void; getSerializableSnapshot(): SerializableState; onRemovedFile( relativeFilePath: string, diff --git a/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts b/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts index 5ae1f31886..051c254179 100644 --- a/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts +++ b/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts @@ -34,7 +34,7 @@ declare class DependencyPlugin getSerializableSnapshot(): null; bulkUpdate( delta: FileMapDelta>, - ): Promise; + ): void; onNewOrModifiedFile( relativeFilePath: string, pluginData: null | undefined | ReadonlyArray, diff --git a/packages/metro-file-map/types/plugins/HastePlugin.d.ts b/packages/metro-file-map/types/plugins/HastePlugin.d.ts index 9b8d01581f..a41575a696 100644 --- a/packages/metro-file-map/types/plugins/HastePlugin.d.ts +++ b/packages/metro-file-map/types/plugins/HastePlugin.d.ts @@ -52,7 +52,7 @@ declare class HastePlugin platform: null | undefined | string, _supportsNativePlatform?: null | undefined | boolean, ): null | undefined | Path; - bulkUpdate(delta: FileMapDelta): Promise; + bulkUpdate(delta: FileMapDelta): void; onNewOrModifiedFile( relativeFilePath: string, id: null | undefined | string, diff --git a/packages/metro-file-map/types/plugins/MockPlugin.d.ts b/packages/metro-file-map/types/plugins/MockPlugin.d.ts index acfaf0514e..a43cf0a56e 100644 --- a/packages/metro-file-map/types/plugins/MockPlugin.d.ts +++ b/packages/metro-file-map/types/plugins/MockPlugin.d.ts @@ -32,7 +32,7 @@ declare class MockPlugin implements FileMapPlugin, IMockMap { constructor($$PARAM_0$$: MockMapOptions); initialize($$PARAM_0$$: FileMapPluginInitOptions): Promise; getMockModule(name: string): null | undefined | Path; - bulkUpdate(delta: FileMapDelta): Promise; + bulkUpdate(delta: FileMapDelta): void; onNewOrModifiedFile(relativeFilePath: Path): void; onRemovedFile(relativeFilePath: Path): void; getSerializableSnapshot(): RawMockMap; From 5dc4bfecaaa9170ba3ec07fcbf9df9f464eca773 Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Mon, 23 Feb 2026 11:34:59 -0800 Subject: [PATCH 2/5] sync bundling progress between Metro cli and app hints Summary: Changelog: [Fix] sync bundling progress between Metro cli and app hints Reviewed By: hoxyq Differential Revision: D93737737 fbshipit-source-id: 683c70b1c73c6dfc2e20a9369ba5ca33f7c52f0b --- packages/metro/src/Server.js | 19 +++++----- packages/metro/src/lib/TerminalReporter.js | 21 ++++------- packages/metro/src/lib/bundleProgressUtils.js | 35 +++++++++++++++++++ .../metro/types/lib/TerminalReporter.d.ts | 8 ----- .../metro/types/lib/bundleProgressUtils.d.ts | 26 ++++++++++++++ 5 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 packages/metro/src/lib/bundleProgressUtils.js create mode 100644 packages/metro/types/lib/bundleProgressUtils.d.ts diff --git a/packages/metro/src/Server.js b/packages/metro/src/Server.js index 71940a4d68..e2b030b499 100644 --- a/packages/metro/src/Server.js +++ b/packages/metro/src/Server.js @@ -52,6 +52,7 @@ import getRamBundleInfo from './DeltaBundler/Serializers/getRamBundleInfo'; import {sourceMapStringNonBlocking} from './DeltaBundler/Serializers/sourceMapString'; import IncrementalBundler from './IncrementalBundler'; import ResourceNotFoundError from './IncrementalBundler/ResourceNotFoundError'; +import {calculateBundleProgressRatio} from './lib/bundleProgressUtils'; import bundleToString from './lib/bundleToString'; import formatBundlingError from './lib/formatBundlingError'; import getGraphId from './lib/getGraphId'; @@ -857,25 +858,23 @@ export default class Server { const mres = MultipartResponse.wrapIfSupported(req, res); let onProgress = null; - let lastProgress = -1; + let lastRatio = -1; if (this._config.reporter) { onProgress = (transformedFileCount: number, totalFileCount: number) => { - const currentProgress = parseInt( - (transformedFileCount / totalFileCount) * 100, - 10, + const newRatio = calculateBundleProgressRatio( + transformedFileCount, + totalFileCount, + lastRatio, ); - // We want to throttle the updates so that we only show meaningful - // UI updates slow enough for the client to actually handle them. For - // that, we check the percentage, and only send percentages that are - // actually different and that have increased from the last one we sent. - if (currentProgress > lastProgress || totalFileCount < 10) { + if (newRatio > lastRatio) { if (mres instanceof MultipartResponse) { mres.writeChunk( {'Content-Type': 'application/json'}, JSON.stringify({ done: transformedFileCount, total: totalFileCount, + percent: Math.floor(newRatio * 100), }), ); } @@ -891,7 +890,7 @@ export default class Server { res.socket.uncork(); } - lastProgress = currentProgress; + lastRatio = newRatio; } this._reporter.update({ diff --git a/packages/metro/src/lib/TerminalReporter.js b/packages/metro/src/lib/TerminalReporter.js index 85f4f2c15e..11d7016030 100644 --- a/packages/metro/src/lib/TerminalReporter.js +++ b/packages/metro/src/lib/TerminalReporter.js @@ -13,6 +13,7 @@ import type {BundleDetails, ReportableEvent} from './reporting'; import type {Terminal} from 'metro-core'; import type {HealthCheckResult, WatcherStatus} from 'metro-file-map'; +import {calculateBundleProgressRatio} from './bundleProgressUtils'; import logToConsole from './logToConsole'; import * as reporting from './reporting'; import chalk from 'chalk'; @@ -132,7 +133,7 @@ export default class TerminalReporter { chalk.bgWhite.white( LIGHT_BLOCK_CHAR.repeat(MAX_PROGRESS_BAR_CHAR_WIDTH - filledBar), ) + - chalk.bold(` ${(100 * ratio).toFixed(1)}% `) + + chalk.bold(` ${Math.floor(100 * ratio)}% `) + chalk.dim(`(${transformedFileCount}/${totalFileCount})`) : ''; @@ -352,14 +353,6 @@ export default class TerminalReporter { }); } - /** - * Because we know the `totalFileCount` is going to progressively increase - * starting with 1: - * - We use Math.max(totalFileCount, 10) to prevent the ratio to raise too - * quickly when the total file count is low. (e.g 1/2 5/6) - * - We prevent the ratio from going backwards. - * - Instead, we use Math.pow(ratio, 2) to as a conservative measure of progress. - */ _updateBundleProgress({ buildID, transformedFileCount, @@ -375,12 +368,10 @@ export default class TerminalReporter { return; } - const ratio = Math.min( - Math.max( - Math.pow(transformedFileCount / Math.max(totalFileCount, 10), 2), - currentProgress.ratio, - ), - 0.999, // make sure not to go above 99.9% to not get rounded to 100%, + const ratio = calculateBundleProgressRatio( + transformedFileCount, + totalFileCount, + currentProgress.ratio, ); // $FlowFixMe[unsafe-object-assign] diff --git a/packages/metro/src/lib/bundleProgressUtils.js b/packages/metro/src/lib/bundleProgressUtils.js new file mode 100644 index 0000000000..4336cceed7 --- /dev/null +++ b/packages/metro/src/lib/bundleProgressUtils.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +/** + * Calculates a conservative progress ratio for bundle building. + * + * Because we know the `totalFileCount` is going to progressively increase + * starting with 1: + * - We use Math.max(totalFileCount, 10) to prevent the ratio from raising too + * quickly when the total file count is low. (e.g 1/2 5/6) + * - We use Math.pow(ratio, 2) as a conservative measure of progress. + * - The ratio is capped at 0.999 to ensure we don't display 100% until done. + * - If previousRatio is provided, the ratio will not go backwards. + */ +export function calculateBundleProgressRatio( + transformedFileCount: number, + totalFileCount: number, + previousRatio?: number, +): number { + const baseRatio = Math.pow( + transformedFileCount / Math.max(totalFileCount, 10), + 2, + ); + const ratio = + previousRatio != null ? Math.max(baseRatio, previousRatio) : baseRatio; + return Math.min(ratio, 0.999); +} diff --git a/packages/metro/types/lib/TerminalReporter.d.ts b/packages/metro/types/lib/TerminalReporter.d.ts index 7061b283c7..b02f034d4b 100644 --- a/packages/metro/types/lib/TerminalReporter.d.ts +++ b/packages/metro/types/lib/TerminalReporter.d.ts @@ -89,14 +89,6 @@ declare class TerminalReporter { */ _logBundlingError(error: SnippetError): void; _logWorkerChunk(origin: 'stdout' | 'stderr', chunk: string): void; - /** - * Because we know the `totalFileCount` is going to progressively increase - * starting with 1: - * - We use Math.max(totalFileCount, 10) to prevent the ratio to raise too - * quickly when the total file count is low. (e.g 1/2 5/6) - * - We prevent the ratio from going backwards. - * - Instead, we use Math.pow(ratio, 2) to as a conservative measure of progress. - */ _updateBundleProgress($$PARAM_0$$: { buildID: string; transformedFileCount: number; diff --git a/packages/metro/types/lib/bundleProgressUtils.d.ts b/packages/metro/types/lib/bundleProgressUtils.d.ts new file mode 100644 index 0000000000..a0521c5671 --- /dev/null +++ b/packages/metro/types/lib/bundleProgressUtils.d.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall react_native + */ + +/** + * Calculates a conservative progress ratio for bundle building. + * + * Because we know the `totalFileCount` is going to progressively increase + * starting with 1: + * - We use Math.max(totalFileCount, 10) to prevent the ratio from raising too + * quickly when the total file count is low. (e.g 1/2 5/6) + * - We use Math.pow(ratio, 2) as a conservative measure of progress. + * - The ratio is capped at 0.999 to ensure we don't display 100% until done. + * - If previousRatio is provided, the ratio will not go backwards. + */ +export declare function calculateBundleProgressRatio( + transformedFileCount: number, + totalFileCount: number, + previousRatio?: number, +): number; From 37edad69ba9909511403434c50e8bf07fe3522b0 Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Tue, 24 Feb 2026 00:57:54 -0800 Subject: [PATCH 3/5] make metro accept tls server config (#1657) Summary: X-link: https://github.com/facebook/react-native/pull/55701 Pull Request resolved: https://github.com/facebook/metro/pull/1657 Changelog: [Feature] `config.server.tls` now sets Metro to be exposed as an https server Reviewed By: robhogan, huntie Differential Revision: D93857257 fbshipit-source-id: 56ff661c4ddf9cd5d4bb32756b9cb600bb032a1c --- docs/Configuration.md | 19 ++ .../src/__tests__/mergeConfig-test.js | 168 +++++++++++++- packages/metro-config/src/defaults/index.js | 1 + packages/metro-config/src/loadConfig.js | 15 +- packages/metro-config/src/types.js | 8 + packages/metro-config/types/types.d.ts | 8 + packages/metro/package.json | 1 + packages/metro/src/index.flow.js | 35 ++- .../__tests__/server-endpoints-test.js | 198 ++++++++++++++++ .../__tests__/server-tls-test.js | 213 ++++++++++++++++++ yarn.lock | 193 +++++++++++++++- 11 files changed, 846 insertions(+), 13 deletions(-) create mode 100644 packages/metro/src/integration_tests/__tests__/server-endpoints-test.js create mode 100644 packages/metro/src/integration_tests/__tests__/server-tls-test.js diff --git a/docs/Configuration.md b/docs/Configuration.md index 74588a91f8..8e521e5d78 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -657,6 +657,25 @@ Type: `boolean` Enable forwarding of `client_log` events (when client logs are [configured](https://github.com/facebook/metro/blob/614ad14a85b22958129ee94e04376b096f03ccb1/packages/metro/src/lib/createWebsocketServer.js#L20)) to the reporter. Defaults to `true`. +#### `tls` + +Type: `false | object` + +If not provided or is `false` Metro will start an HTTP server with WS WebSocket endpoints. + +If an object, Metro will start an HTTPS server with WSS WebSocket endpoints using the passed TLS options: + +```ts +ca?: string | Buffer, // Certificate authority (contents, not path) +cert?: string | Buffer, // Server certificate (contents, not path) +key?: string | Buffer, // Private key (contents, not path) +requestCert?: boolean, // Whether to authenticate the remote peer by requesting a certificate +``` + +Notice that when overriding the base config, object tls configs extend the base tls config, false overrides the base tls configs, and `null` and `undefined` are ignored. + +When running Metro with `Metro.runServer` with the `secureServerOptions` property Metro will likewise start an HTTPS server merging with the `config.server.tls` object if provided, overriding it. + --- ### Watcher Options diff --git a/packages/metro-config/src/__tests__/mergeConfig-test.js b/packages/metro-config/src/__tests__/mergeConfig-test.js index 23586fab05..569d3541b3 100644 --- a/packages/metro-config/src/__tests__/mergeConfig-test.js +++ b/packages/metro-config/src/__tests__/mergeConfig-test.js @@ -4,11 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow * @format * @oncall react_native */ +import type {InputConfigT} from '../types'; + import {mergeConfig} from '../loadConfig'; describe('mergeConfig', () => { @@ -26,4 +28,168 @@ describe('mergeConfig', () => { }, }); }); + + describe('server.tls merging', () => { + describe('override IS applied when tls is false or object', () => { + test('override tls: object replaces base tls: false', () => { + const base: InputConfigT = {server: {tls: false}}; + const override: InputConfigT = { + server: {tls: {key: 'key', cert: 'cert'}}, + }; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'}); + }); + + test('override tls: false replaces base tls: object', () => { + const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}}; + const override: InputConfigT = {server: {tls: false}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(false); + }); + + test('override tls: false sets tls when base is undefined', () => { + const base: InputConfigT = {server: {}}; + const override: InputConfigT = {server: {tls: false}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(false); + }); + + test('override tls: object sets tls when base is undefined', () => { + const base: InputConfigT = {server: {}}; + const override: InputConfigT = { + server: {tls: {key: 'key', cert: 'cert'}}, + }; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'}); + }); + + test('override tls: object deep merges with base tls: object', () => { + const base: InputConfigT = { + server: {tls: {key: 'baseKey', cert: 'baseCert', ca: 'baseCa'}}, + }; + const override: InputConfigT = { + server: {tls: {key: 'newKey', cert: 'newCert'}}, + }; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({ + key: 'newKey', + cert: 'newCert', + ca: 'baseCa', + }); + }); + + test('override tls: object adds new properties to base tls: object', () => { + const base: InputConfigT = { + server: {tls: {key: 'baseKey', cert: 'baseCert'}}, + }; + const override: InputConfigT = { + server: {tls: {ca: 'newCa'}}, + }; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({ + key: 'baseKey', + cert: 'baseCert', + ca: 'newCa', + }); + }); + + test('override tls: object with same properties overrides base values', () => { + const base: InputConfigT = { + server: {tls: {key: 'baseKey', cert: 'baseCert'}}, + }; + const override: InputConfigT = { + server: {tls: {key: 'newKey', cert: 'newCert'}}, + }; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({ + key: 'newKey', + cert: 'newCert', + }); + }); + + test('other server properties are preserved when tls is overridden', () => { + const base: InputConfigT = {server: {port: 8081, tls: false}}; + const override: InputConfigT = { + server: {tls: {key: 'key', cert: 'cert'}}, + }; + const result = mergeConfig(base, override); + expect(result.server).toStrictEqual({ + port: 8081, + tls: {key: 'key', cert: 'cert'}, + }); + }); + + test('override tls: null replaces base tls: undefined', () => { + const base: InputConfigT = {server: {}}; + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const override: InputConfigT = {server: {tls: null}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(null); + }); + }); + + describe('override is NOT applied when tls is null or undefined', () => { + test('override tls: undefined keeps base tls: object', () => { + const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}}; + const override: InputConfigT = {server: {}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'}); + }); + + test('override tls: undefined (explicit) keeps base tls: object', () => { + const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}}; + // $FlowExpectedError[incompatible-type] - testing explicit undefined + const override: InputConfigT = {server: {tls: undefined}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'}); + }); + + test('override tls: undefined keeps base tls: false', () => { + const base: InputConfigT = {server: {tls: false}}; + const override: InputConfigT = {server: {}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(false); + }); + + test('override tls: undefined (explicit) keeps base tls: false', () => { + const base: InputConfigT = {server: {tls: false}}; + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const override: InputConfigT = {server: {tls: undefined}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(false); + }); + + test('override tls: null keeps base tls: object', () => { + const base: InputConfigT = {server: {tls: {key: 'key', cert: 'cert'}}}; + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const override: InputConfigT = {server: {tls: null}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toStrictEqual({key: 'key', cert: 'cert'}); + }); + + test('override tls: null keeps base tls: false', () => { + const base: InputConfigT = {server: {tls: false}}; + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const override: InputConfigT = {server: {tls: null}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBe(false); + }); + + test('both tls undefined results in no tls property', () => { + const base: InputConfigT = {server: {}}; + const override: InputConfigT = {server: {}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBeUndefined(); + }); + + test('both tls undefined (explicit) results in no tls property', () => { + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const base: InputConfigT = {server: {tls: undefined}}; + // $FlowExpectedError[incompatible-type] - testing untyped runtime behavior + const override: InputConfigT = {server: {tls: undefined}}; + const result = mergeConfig(base, override); + expect(result.server?.tls).toBeUndefined(); + }); + }); + }); }); diff --git a/packages/metro-config/src/defaults/index.js b/packages/metro-config/src/defaults/index.js index f9d0c8ce79..2d7df87df2 100644 --- a/packages/metro-config/src/defaults/index.js +++ b/packages/metro-config/src/defaults/index.js @@ -80,6 +80,7 @@ const getDefaultValues = (projectRoot: ?string): ConfigT => ({ unstable_serverRoot: null, useGlobalHotkey: true, verifyConnections: false, + tls: false, }, symbolicator: { diff --git a/packages/metro-config/src/loadConfig.js b/packages/metro-config/src/loadConfig.js index 3ec6265472..93721d249f 100644 --- a/packages/metro-config/src/loadConfig.js +++ b/packages/metro-config/src/loadConfig.js @@ -152,8 +152,21 @@ function mergeConfigObjects( }, server: { ...base.server, - // $FlowFixMe[exponential-spread] ...overrides.server, + // $FlowFixMe[exponential-spread] + ...(base.server?.tls != null ? {tls: base.server?.tls} : null), + // only override base tls config with false or an object + ...(overrides.server?.tls === false + ? {tls: false} + : overrides.server?.tls != null && + typeof overrides.server?.tls === 'object' + ? { + tls: { + ...(base.server?.tls || {}), + ...overrides.server?.tls, + }, + } + : null), }, symbolicator: { ...base.symbolicator, diff --git a/packages/metro-config/src/types.js b/packages/metro-config/src/types.js index 07fa15dd98..79141c4427 100644 --- a/packages/metro-config/src/types.js +++ b/packages/metro-config/src/types.js @@ -184,6 +184,14 @@ type ServerConfigT = { unstable_serverRoot: ?string, useGlobalHotkey: boolean, verifyConnections: boolean, + tls: + | false + | { + ca?: string | Buffer, + cert?: string | Buffer, + key?: string | Buffer, + requestCert?: boolean, + }, }; type SymbolicatorConfigT = { diff --git a/packages/metro-config/types/types.d.ts b/packages/metro-config/types/types.d.ts index 09dee75120..7b11efd0ee 100644 --- a/packages/metro-config/types/types.d.ts +++ b/packages/metro-config/types/types.d.ts @@ -179,6 +179,14 @@ type ServerConfigT = { unstable_serverRoot: null | undefined | string; useGlobalHotkey: boolean; verifyConnections: boolean; + tls: + | false + | { + ca?: string | Buffer; + cert?: string | Buffer; + key?: string | Buffer; + requestCert?: boolean; + }; }; type SymbolicatorConfigT = { customizeFrame: ($$PARAM_0$$: { diff --git a/packages/metro/package.json b/packages/metro/package.json index 8583d55979..9eb8283c80 100644 --- a/packages/metro/package.json +++ b/packages/metro/package.json @@ -76,6 +76,7 @@ "metro-memory-fs": "*", "mock-req": "^0.2.0", "mock-res": "^0.6.0", + "selfsigned": "^5.5.0", "stack-trace": "^0.0.10" }, "license": "MIT", diff --git a/packages/metro/src/index.flow.js b/packages/metro/src/index.flow.js index 3ff377df4d..aae037ad54 100644 --- a/packages/metro/src/index.flow.js +++ b/packages/metro/src/index.flow.js @@ -280,7 +280,7 @@ export const runServer = async ( chalk.inverse.yellow.bold(' DEPRECATED '), 'The `secure`, `secureCert`, and `secureKey` options are now deprecated. ' + 'Please use the `secureServerOptions` object instead to pass options to ' + - "Metro's https development server.", + "Metro's https development server, or `config.server.tls` in Metro's configuration", ); } // Lazy require @@ -307,15 +307,30 @@ export const runServer = async ( let httpServer; - if (secure === true || secureServerOptions != null) { - let options = secureServerOptions; - if (typeof secureKey === 'string' && typeof secureCert === 'string') { - options = { - key: fs.readFileSync(secureKey), - cert: fs.readFileSync(secureCert), - ...secureServerOptions, - }; - } + const {tls} = config.server; + if ( + secure === true || + secureServerOptions != null || + typeof tls === 'object' + ) { + const options = { + key: + typeof tls === 'object' + ? tls.key + : typeof secureKey === 'string' + ? fs.readFileSync(secureKey) + : undefined, + cert: + typeof tls === 'object' + ? tls.cert + : typeof secureCert === 'string' + ? fs.readFileSync(secureCert) + : undefined, + ca: typeof tls === 'object' ? tls.ca : undefined, + requestCert: typeof tls === 'object' ? tls.requestCert : undefined, + ...(secureServerOptions ?? {}), + }; + // $FlowFixMe[incompatible-type] 'http' and 'https' Flow types do not match httpServer = https.createServer(options, serverApp); } else { diff --git a/packages/metro/src/integration_tests/__tests__/server-endpoints-test.js b/packages/metro/src/integration_tests/__tests__/server-endpoints-test.js new file mode 100644 index 0000000000..d44f6e4809 --- /dev/null +++ b/packages/metro/src/integration_tests/__tests__/server-endpoints-test.js @@ -0,0 +1,198 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall react_native + */ + +'use strict'; + +const Metro = require('../../..'); +const http = require('http'); +const https = require('https'); +const selfsigned = require('selfsigned'); +const WebSocket = require('ws'); + +jest.useRealTimers(); +jest.setTimeout(60 * 1000); + +function checkHttpEndpoint( + port: number, + secure: boolean, +): Promise<{ + success: boolean, + status: number, + url: string, +}> { + const protocol = secure ? 'https' : 'http'; + const url = `${protocol}://localhost:${port}/TestBundle.bundle?platform=ios&dev=true&minify=false`; + const client = secure ? https : http; + const options = secure ? {rejectUnauthorized: false} : {}; + + return new Promise((resolve, reject) => { + const req = client.get(url, options, res => { + // Consume the response body to properly close the connection + res.on('data', () => {}); + res.on('end', () => { + if (res.statusCode === 200) { + resolve({success: true, status: res.statusCode, url}); + } else { + reject(new Error(`Metro returned status ${res.statusCode}`)); + } + }); + }); + + req.on('error', err => { + reject(new Error(`Failed to connect to ${url}: ${err.message}`)); + }); + + req.setTimeout(30000, () => { + req.destroy(); + reject(new Error(`Request to ${url} timed out`)); + }); + }); +} + +function checkWebSocket( + port: number, + secure: boolean, +): Promise<{ + success: boolean, + message: string, + url: string, +}> { + const protocol = secure ? 'wss' : 'ws'; + const url = `${protocol}://localhost:${port}/hot`; + + return new Promise((resolve, reject) => { + const ws = new WebSocket(url, { + rejectUnauthorized: false, + }); + + const timeout = setTimeout(() => { + ws.close(); + reject(new Error(`WebSocket connection to ${url} timed out`)); + }, 5000); + + ws.on('open', () => { + clearTimeout(timeout); + ws.close(); + resolve({ + success: true, + message: 'WebSocket connection established', + url, + }); + }); + + ws.on('error', err => { + clearTimeout(timeout); + reject( + new Error(`WebSocket connection to ${url} failed: ${err.message}`), + ); + }); + }); +} + +describe('Metro development server endpoints', () => { + let httpServer; + let serverClosedPromise; + let port; + + afterEach(async () => { + if (httpServer) { + httpServer.close(); + await serverClosedPromise; + httpServer = null; + } + }); + + describe('HTTP server (no TLS)', () => { + beforeEach(async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + onClose: () => { + onCloseResolve(); + }, + })); + + port = httpServer.address().port; + }); + + test('HTTP endpoint is reachable', async () => { + const result = await checkHttpEndpoint(port, false); + expect(result.success).toBe(true); + expect(result.status).toBe(200); + }); + + test('WS /hot endpoint is reachable', async () => { + const result = await checkWebSocket(port, false); + expect(result.success).toBe(true); + }); + + test('HTTPS endpoint is not reachable on HTTP server', async () => { + await expect(checkHttpEndpoint(port, true)).rejects.toThrow(); + }); + + test('WSS /hot endpoint is not reachable on HTTP server', async () => { + await expect(checkWebSocket(port, true)).rejects.toThrow(); + }); + }); + + describe('HTTPS server (with TLS)', () => { + beforeEach(async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + const selfSignedPems = await selfsigned.generate( + [{name: 'commonName', value: 'localhost'}], + {days: 1}, + ); + config.server.tls = { + key: selfSignedPems.private, + cert: selfSignedPems.cert, + }; + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + onClose: () => { + onCloseResolve(); + }, + })); + + port = httpServer.address().port; + }); + + test('HTTPS endpoint is reachable', async () => { + const result = await checkHttpEndpoint(port, true); + expect(result.success).toBe(true); + expect(result.status).toBe(200); + }); + + test('WSS /hot endpoint is reachable', async () => { + const result = await checkWebSocket(port, true); + expect(result.success).toBe(true); + }); + + test('HTTP endpoint is not reachable on HTTPS server', async () => { + await expect(checkHttpEndpoint(port, false)).rejects.toThrow(); + }); + + test('WS /hot endpoint is not reachable on HTTPS server', async () => { + await expect(checkWebSocket(port, false)).rejects.toThrow(); + }); + }); +}); diff --git a/packages/metro/src/integration_tests/__tests__/server-tls-test.js b/packages/metro/src/integration_tests/__tests__/server-tls-test.js new file mode 100644 index 0000000000..6fe8e07fcd --- /dev/null +++ b/packages/metro/src/integration_tests/__tests__/server-tls-test.js @@ -0,0 +1,213 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall react_native + */ + +'use strict'; + +const Metro = require('../../..'); +const fs = require('fs'); +const http = require('http'); +const https = require('https'); +const os = require('os'); +const path = require('path'); +const selfsigned = require('selfsigned'); + +jest.useRealTimers(); +jest.setTimeout(60 * 1000); + +describe('Metro development server with TLS configuration', () => { + let httpServer; + let serverClosedPromise; + let tempDir; + let keyFile; + let certFile; + let keyContent; + let certContent; + + beforeAll(async () => { + // Create temp directory for cert files + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'metro-tls-test-')); + keyFile = path.join(tempDir, 'key.pem'); + certFile = path.join(tempDir, 'cert.pem'); + + const selfSignedPems = await selfsigned.generate( + [{name: 'commonName', value: 'localhost'}], + {days: 1}, + ); + + keyContent = selfSignedPems.private; + certContent = selfSignedPems.cert; + + fs.writeFileSync(keyFile, keyContent, 'utf8'); + fs.writeFileSync(certFile, selfSignedPems.cert, 'utf8'); + }); + + afterAll(() => { + // Cleanup temp files + if (tempDir) { + fs.rmSync(tempDir, {recursive: true, force: true}); + } + }); + + afterEach(async () => { + if (httpServer) { + httpServer.close(); + await serverClosedPromise; + httpServer = null; + } + }); + + test('should create HTTP server when no TLS config is provided', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + onClose: () => { + onCloseResolve(); + }, + })); + + expect(httpServer).toBeInstanceOf(http.Server); + expect(httpServer).not.toBeInstanceOf(https.Server); + }); + + test('should create HTTPS server when tls config with key/cert strings is provided', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + config.server.tls = {key: keyContent, cert: certContent}; + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + onClose: () => { + onCloseResolve(); + }, + })); + + expect(httpServer).toBeInstanceOf(https.Server); + }); + + test('should create HTTPS server with secureServerOptions', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + secureServerOptions: {key: keyContent, cert: certContent}, + onClose: () => { + onCloseResolve(); + }, + })); + + expect(httpServer).toBeInstanceOf(https.Server); + }); + + test('should create HTTPS server with deprecated secureKey/secureCert file paths', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + // Suppress deprecation warning for test + const originalWarn = console.warn; + console.warn = jest.fn(); + + try { + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + secure: true, + secureKey: keyFile, + secureCert: certFile, + onClose: () => { + onCloseResolve(); + }, + })); + + expect(httpServer).toBeInstanceOf(https.Server); + expect(console.warn).toHaveBeenCalled(); + } finally { + console.warn = originalWarn; + } + }); + + test('tls config should take precedence over secureKey/secureCert', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + // Set tls config with valid certs + config.server.tls = {key: keyContent, cert: certContent}; + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + // Suppress deprecation warning for test + const originalWarn = console.warn; + console.warn = jest.fn(); + + try { + // Pass invalid file paths - if tls config takes precedence, these won't be read + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + secure: true, + secureKey: '/nonexistent/key.pem', + secureCert: '/nonexistent/cert.pem', + onClose: () => { + onCloseResolve(); + }, + })); + + // Server should start successfully using tls config + expect(httpServer).toBeInstanceOf(https.Server); + } finally { + console.warn = originalWarn; + } + }); + + test('secureServerOptions should merge with tls config', async () => { + const config = await Metro.loadConfig({ + config: require.resolve('../metro.config.js'), + }); + + // Set tls config with key/cert + config.server.tls = {key: keyContent, cert: certContent}; + + let onCloseResolve; + serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve)); + + ({httpServer} = await Metro.runServer(config, { + reporter: {update() {}}, + // secureServerOptions should be spread into the options + secureServerOptions: { + // This option should be merged in + rejectUnauthorized: false, + }, + onClose: () => { + onCloseResolve(); + }, + })); + + expect(httpServer).toBeInstanceOf(https.Server); + }); +}); diff --git a/yarn.lock b/yarn.lock index 82d86582ea..b54c8071de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1097,6 +1097,11 @@ "@jsonjoy.com/buffers" "^1.0.0" "@jsonjoy.com/codegen" "^1.0.0" +"@noble/hashes@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1118,6 +1123,129 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@peculiar/asn1-cms@^2.6.0", "@peculiar/asn1-cms@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz#cb5445c1bad9197d176073bf142a5c035b460640" + integrity sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-csr@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz#9629d403bc5a61254f28ed0b90e99cee61c0e8be" + integrity sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-ecc@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz#d29c4af671508a9934edc78e7c9419fbf7bc9870" + integrity sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pfx@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz#75cddd14d43ef875109e91ea150377d679c8fbc1" + integrity sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-rsa" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs8@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz#bd56b4bb9e8a3702369049713a89134c87c6931a" + integrity sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-pkcs9@^2.6.0": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz#ddc5222952f25b59a0562a6f8cabdb72f586a496" + integrity sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw== + dependencies: + "@peculiar/asn1-cms" "^2.6.1" + "@peculiar/asn1-pfx" "^2.6.1" + "@peculiar/asn1-pkcs8" "^2.6.1" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + "@peculiar/asn1-x509-attr" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-rsa@^2.6.0", "@peculiar/asn1-rsa@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz#2cdf9f9ea6d6fdbaae214b9fed6de0534b654437" + integrity sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-schema@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz#0dca1601d5b0fed2a72fed7a5f1d0d7dbe3a6f82" + integrity sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg== + dependencies: + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509-attr@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz#6425008b8099476010aace5b8ae9f9cbc41db0ab" + integrity sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.1" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/asn1-x509@^2.6.0", "@peculiar/asn1-x509@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz#4e8995659e16178e0e90fe90519aa269045af262" + integrity sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA== + dependencies: + "@peculiar/asn1-schema" "^2.6.0" + asn1js "^3.0.6" + pvtsutils "^1.3.6" + tslib "^2.8.1" + +"@peculiar/x509@^1.14.2": + version "1.14.3" + resolved "https://registry.yarnpkg.com/@peculiar/x509/-/x509-1.14.3.tgz#2c44c2b89474346afec38a0c2803ec4fb8ce959e" + integrity sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA== + dependencies: + "@peculiar/asn1-cms" "^2.6.0" + "@peculiar/asn1-csr" "^2.6.0" + "@peculiar/asn1-ecc" "^2.6.0" + "@peculiar/asn1-pkcs9" "^2.6.0" + "@peculiar/asn1-rsa" "^2.6.0" + "@peculiar/asn1-schema" "^2.6.0" + "@peculiar/asn1-x509" "^2.6.0" + pvtsutils "^1.3.6" + reflect-metadata "^0.2.2" + tslib "^2.8.1" + tsyringe "^4.10.0" + "@react-native/babel-plugin-codegen@0.78.0": version "0.78.0" resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.78.0.tgz#c2b0e320042c9e780e857d7bed18127a1e90c6a2" @@ -1755,6 +1883,15 @@ arraybuffer.prototype.slice@^1.0.4: get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" +asn1js@^3.0.6: + version "3.0.7" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.7.tgz#15f1f2f59e60f80d5b43ef14047a294a969f824f" + integrity sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ== + dependencies: + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -1966,6 +2103,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +bytestreamjs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bytestreamjs/-/bytestreamjs-2.0.1.tgz#a32947c7ce389a6fa11a09a9a563d0a45889535e" + integrity sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ== + call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -4643,6 +4785,18 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkijs@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-3.3.3.tgz#b3f04d7b2eaacb05c81675f882be374e591626ec" + integrity sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw== + dependencies: + "@noble/hashes" "1.4.0" + asn1js "^3.0.6" + bytestreamjs "^2.0.1" + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + possible-typed-array-names@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" @@ -4704,6 +4858,18 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== +pvtsutils@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.6.tgz#ec46e34db7422b9e4fdc5490578c1883657d6001" + integrity sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg== + dependencies: + tslib "^2.8.1" + +pvutils@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.5.tgz#84b0dea4a5d670249aa9800511804ee0b7c2809c" + integrity sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -4760,6 +4926,11 @@ recast@^0.23.9: tiny-invariant "^1.3.3" tslib "^2.0.1" +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" @@ -4943,6 +5114,14 @@ scheduler@^0.27.0: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== +selfsigned@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-5.5.0.tgz#4c9ab7c7c9f35f18fb6a9882c253eb0e6bd6557b" + integrity sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew== + dependencies: + "@peculiar/x509" "^1.14.2" + pkijs "^3.3.3" + semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -5382,11 +5561,23 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.0.1: +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tsyringe@^4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.10.0.tgz#d0c95815d584464214060285eaaadd94aa03299c" + integrity sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw== + dependencies: + tslib "^1.9.3" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From ee0435880c8162aeaf3f401bfe9c4da1e86e2afa Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Fri, 27 Feb 2026 09:30:22 -0800 Subject: [PATCH 4/5] Restore publishing of `.d.ts` files (#1662) Summary: Pull Request resolved: https://github.com/facebook/metro/pull/1662 https://github.com/facebook/metro/pull/1627 inadvertently broke publishing of `.d.ts` files, which are meant to be copied to the build directory alongside `.js` files prior to publishing, because `tinyglobby`, unlike `glob`, defaults to producing relative paths, breaking a `filePath.replace()`. This affected 0.83.4, 0.84.0 and 0.84.1. This restores it, we'll backport to 0.83. Changelog: ``` - **[Fix]**: Restore accidentally removed publication of TypeScript types ``` Reviewed By: huntie Differential Revision: D94666464 fbshipit-source-id: 338fe716858e7f65ca12cbef614aaaec9e829bc2 --- scripts/build.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/build.js b/scripts/build.js index 8cbff2cb61..e4116336e3 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -72,8 +72,10 @@ function buildPackage(p /*: string */) { const typesDir = path.resolve(p, TYPES_DIR); const buildDir = path.resolve(p, BUILD_DIR); const pattern = path.resolve(srcDir, '**/*'); - const files = globSync(pattern, {onlyFiles: true}); - const typescriptDefs = globSync(path.join(typesDir, '**/*.d.ts')); + const files = globSync(pattern, {absolute: true, onlyFiles: true}); + const typescriptDefs = globSync(path.join(typesDir, '**/*.d.ts'), { + absolute: true, + }); process.stdout.write(fixedWidth(`${path.basename(p)}\n`)); From dce1b7701acb5823a5da92422b68f16f2bed049b Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Fri, 27 Feb 2026 21:27:36 -0500 Subject: [PATCH 5/5] Publish 0.83.5 --- packages/buck-worker-tool/package.json | 2 +- packages/metro-babel-register/package.json | 2 +- packages/metro-babel-transformer/package.json | 2 +- packages/metro-cache-key/package.json | 2 +- packages/metro-cache/package.json | 4 +-- packages/metro-config/package.json | 12 ++++---- packages/metro-core/package.json | 4 +-- packages/metro-file-map/package.json | 2 +- packages/metro-minify-terser/package.json | 2 +- packages/metro-resolver/package.json | 4 +-- packages/metro-runtime/package.json | 2 +- packages/metro-source-map/package.json | 6 ++-- packages/metro-symbolicate/package.json | 4 +-- packages/metro-transform-plugins/package.json | 4 +-- packages/metro-transform-worker/package.json | 16 +++++------ packages/metro/package.json | 28 +++++++++---------- packages/ob1/package.json | 2 +- 17 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/buck-worker-tool/package.json b/packages/buck-worker-tool/package.json index a39d3dfe00..c3ccd30b61 100644 --- a/packages/buck-worker-tool/package.json +++ b/packages/buck-worker-tool/package.json @@ -1,6 +1,6 @@ { "name": "buck-worker-tool", - "version": "0.83.4", + "version": "0.83.5", "description": "Implementation of the Buck worker protocol for Node.js.", "license": "MIT", "repository": { diff --git a/packages/metro-babel-register/package.json b/packages/metro-babel-register/package.json index e12e25ec73..1616d110d7 100644 --- a/packages/metro-babel-register/package.json +++ b/packages/metro-babel-register/package.json @@ -1,6 +1,6 @@ { "name": "metro-babel-register", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 babel/register configuration for Metro.", "main": "src/babel-register.js", "exports": { diff --git a/packages/metro-babel-transformer/package.json b/packages/metro-babel-transformer/package.json index 2607ac44c9..e415b99a49 100644 --- a/packages/metro-babel-transformer/package.json +++ b/packages/metro-babel-transformer/package.json @@ -1,6 +1,6 @@ { "name": "metro-babel-transformer", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Base Babel transformer for Metro.", "main": "src/index.js", "exports": { diff --git a/packages/metro-cache-key/package.json b/packages/metro-cache-key/package.json index 621409ac29..8c6b97036b 100644 --- a/packages/metro-cache-key/package.json +++ b/packages/metro-cache-key/package.json @@ -1,6 +1,6 @@ { "name": "metro-cache-key", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Cache key utility.", "main": "src/index.js", "exports": { diff --git a/packages/metro-cache/package.json b/packages/metro-cache/package.json index efeaeace7d..d0a1ab2082 100644 --- a/packages/metro-cache/package.json +++ b/packages/metro-cache/package.json @@ -1,6 +1,6 @@ { "name": "metro-cache", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Cache layers for Metro.", "main": "src/index.js", "exports": { @@ -21,7 +21,7 @@ "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.4" + "metro-core": "0.83.5" }, "devDependencies": { "memfs": "^4.38.2" diff --git a/packages/metro-config/package.json b/packages/metro-config/package.json index bc709185a5..87ab49aac9 100644 --- a/packages/metro-config/package.json +++ b/packages/metro-config/package.json @@ -1,6 +1,6 @@ { "name": "metro-config", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Config parser for Metro.", "main": "src/index.js", "exports": { @@ -22,15 +22,15 @@ "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.4", - "metro-cache": "0.83.4", - "metro-core": "0.83.4", - "metro-runtime": "0.83.4", + "metro": "0.83.5", + "metro-cache": "0.83.5", + "metro-core": "0.83.5", + "metro-runtime": "0.83.5", "yaml": "^2.6.1" }, "devDependencies": { "@types/connect": "^3.4.35", - "metro-babel-register": "0.83.4", + "metro-babel-register": "0.83.5", "pretty-format": "^29.7.0" }, "engines": { diff --git a/packages/metro-core/package.json b/packages/metro-core/package.json index be77ffbaa4..febb8180ea 100644 --- a/packages/metro-core/package.json +++ b/packages/metro-core/package.json @@ -1,6 +1,6 @@ { "name": "metro-core", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Metro's core package.", "main": "src/index.js", "exports": { @@ -20,7 +20,7 @@ "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.4" + "metro-resolver": "0.83.5" }, "license": "MIT", "engines": { diff --git a/packages/metro-file-map/package.json b/packages/metro-file-map/package.json index fd41547788..7730cdab56 100644 --- a/packages/metro-file-map/package.json +++ b/packages/metro-file-map/package.json @@ -1,6 +1,6 @@ { "name": "metro-file-map", - "version": "0.83.4", + "version": "0.83.5", "description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro", "main": "src/index.js", "exports": { diff --git a/packages/metro-minify-terser/package.json b/packages/metro-minify-terser/package.json index ffff2fd166..0c28bb0821 100644 --- a/packages/metro-minify-terser/package.json +++ b/packages/metro-minify-terser/package.json @@ -1,6 +1,6 @@ { "name": "metro-minify-terser", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Minifier for Metro based on Terser.", "main": "src/index.js", "exports": { diff --git a/packages/metro-resolver/package.json b/packages/metro-resolver/package.json index f573d87dbc..e0e06c87bd 100644 --- a/packages/metro-resolver/package.json +++ b/packages/metro-resolver/package.json @@ -1,6 +1,6 @@ { "name": "metro-resolver", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Implementation of Metro's resolution logic.", "main": "src/index.js", "exports": { @@ -22,7 +22,7 @@ "node": ">=20.19.4" }, "devDependencies": { - "metro": "0.83.4" + "metro": "0.83.5" }, "dependencies": { "flow-enums-runtime": "^0.0.6" diff --git a/packages/metro-runtime/package.json b/packages/metro-runtime/package.json index da8e7ddaba..578f47ddbb 100644 --- a/packages/metro-runtime/package.json +++ b/packages/metro-runtime/package.json @@ -1,6 +1,6 @@ { "name": "metro-runtime", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Module required for evaluating Metro bundles.", "exports": { "./package.json": "./package.json", diff --git a/packages/metro-source-map/package.json b/packages/metro-source-map/package.json index ea5e9bca6b..35d79b332a 100644 --- a/packages/metro-source-map/package.json +++ b/packages/metro-source-map/package.json @@ -1,6 +1,6 @@ { "name": "metro-source-map", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Source map generator for Metro.", "main": "src/source-map.js", "exports": { @@ -22,9 +22,9 @@ "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.4", + "metro-symbolicate": "0.83.5", "nullthrows": "^1.1.1", - "ob1": "0.83.4", + "ob1": "0.83.5", "source-map": "^0.5.6", "vlq": "^1.0.0" }, diff --git a/packages/metro-symbolicate/package.json b/packages/metro-symbolicate/package.json index 03d0de5f31..268bc22b17 100644 --- a/packages/metro-symbolicate/package.json +++ b/packages/metro-symbolicate/package.json @@ -1,6 +1,6 @@ { "name": "metro-symbolicate", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 A tool to find the source location from JS bundles and stack traces.", "license": "MIT", "main": "./src/index.js", @@ -25,7 +25,7 @@ "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.4", + "metro-source-map": "0.83.5", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" diff --git a/packages/metro-transform-plugins/package.json b/packages/metro-transform-plugins/package.json index 84ee61d721..7daeeb59e1 100644 --- a/packages/metro-transform-plugins/package.json +++ b/packages/metro-transform-plugins/package.json @@ -1,6 +1,6 @@ { "name": "metro-transform-plugins", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Transform plugins for Metro.", "main": "src/index.js", "exports": { @@ -33,7 +33,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/types": "^7.29.0", "babel-plugin-tester": "^6.0.1", - "metro": "0.83.4" + "metro": "0.83.5" }, "engines": { "node": ">=20.19.4" diff --git a/packages/metro-transform-worker/package.json b/packages/metro-transform-worker/package.json index 3d6058f5b4..9cb9a868fd 100644 --- a/packages/metro-transform-worker/package.json +++ b/packages/metro-transform-worker/package.json @@ -1,6 +1,6 @@ { "name": "metro-transform-worker", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 Transform worker for Metro.", "main": "src/index.js", "exports": { @@ -24,13 +24,13 @@ "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.4", - "metro-babel-transformer": "0.83.4", - "metro-cache": "0.83.4", - "metro-cache-key": "0.83.4", - "metro-minify-terser": "0.83.4", - "metro-source-map": "0.83.4", - "metro-transform-plugins": "0.83.4", + "metro": "0.83.5", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-minify-terser": "0.83.5", + "metro-source-map": "0.83.5", + "metro-transform-plugins": "0.83.5", "nullthrows": "^1.1.1" }, "devDependencies": { diff --git a/packages/metro/package.json b/packages/metro/package.json index 9eb8283c80..1e255acabb 100644 --- a/packages/metro/package.json +++ b/packages/metro/package.json @@ -1,6 +1,6 @@ { "name": "metro", - "version": "0.83.4", + "version": "0.83.5", "description": "🚇 The JavaScript bundler for React Native.", "main": "src/index.js", "bin": "src/cli.js", @@ -40,18 +40,18 @@ "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.4", - "metro-cache": "0.83.4", - "metro-cache-key": "0.83.4", - "metro-config": "0.83.4", - "metro-core": "0.83.4", - "metro-file-map": "0.83.4", - "metro-resolver": "0.83.4", - "metro-runtime": "0.83.4", - "metro-source-map": "0.83.4", - "metro-symbolicate": "0.83.4", - "metro-transform-plugins": "0.83.4", - "metro-transform-worker": "0.83.4", + "metro-babel-transformer": "0.83.5", + "metro-cache": "0.83.5", + "metro-cache-key": "0.83.5", + "metro-config": "0.83.5", + "metro-core": "0.83.5", + "metro-file-map": "0.83.5", + "metro-resolver": "0.83.5", + "metro-runtime": "0.83.5", + "metro-source-map": "0.83.5", + "metro-symbolicate": "0.83.5", + "metro-transform-plugins": "0.83.5", + "metro-transform-worker": "0.83.5", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", @@ -72,7 +72,7 @@ "dedent": "^0.7.0", "jest-snapshot": "^29.7.0", "jest-snapshot-serializer-raw": "^1.2.0", - "metro-babel-register": "0.83.4", + "metro-babel-register": "0.83.5", "metro-memory-fs": "*", "mock-req": "^0.2.0", "mock-res": "^0.6.0", diff --git a/packages/ob1/package.json b/packages/ob1/package.json index c6165fa3f7..5c715fd194 100644 --- a/packages/ob1/package.json +++ b/packages/ob1/package.json @@ -1,6 +1,6 @@ { "name": "ob1", - "version": "0.83.4", + "version": "0.83.5", "description": "A small library for working with 0- and 1-based offsets in a type-checked way.", "main": "src/ob1.js", "exports": {