From cf6e12274d055502c3e983b27bfb05b839850c2a Mon Sep 17 00:00:00 2001 From: Durvesh Pilankar Date: Mon, 29 Jun 2026 21:51:55 -0700 Subject: [PATCH 1/4] Fix RAM bundle passing the deps function (not its result) to getTransformOptions getRamBundleInfo gives custom getTransformOptions a callback to look up a module's transitive dependencies, but it called `Array.from(getDependencies)` on the function itself instead of `Array.from(getDependencies(x))`. Since the closure has arity 1, Array.from treated it as array-like and returned [undefined], so any getTransformOptions relying on this callback received garbage regardless of the file queried. Invoke getDependencies(x). Existing tests never exercised the callback, which masked this. Adds a regression test that calls the callback and asserts the real transitive deps (fails before / passes after). --- .../__tests__/getRamBundleInfo-test.js | 41 +++++++++++++++++++ .../Serializers/getRamBundleInfo.js | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js index c988afebe4..6fc33a61d2 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js @@ -112,6 +112,47 @@ test('should return the RAM bundle info', async () => { ).toMatchSnapshot(); }); +test('passes a working transitive-dependency lookup to getTransformOptions', async () => { + let resolvedDeps; + await getRamBundleInfo( + '/root/entry.js', + pre, + {...graph, entryPoints: new Set(['/root/entry.js'])}, + { + asyncRequireModulePath: '', + // $FlowFixMe[incompatible-type] createModuleId assumes numeric IDs - is this too strict? + createModuleId: path => path, + dev: true, + excludeSource: false, + getRunModuleStatement, + getTransformOptions: async (entryPoints, opts, getDependenciesOf) => { + resolvedDeps = await getDependenciesOf('/root/foo.js'); + return {preloadedModules: {}, ramGroups: []}; + }, + globalPrefix: '', + includeAsyncPaths: false, + inlineSourceMap: false, + modulesOnly: false, + platform: null, + processModuleFilter: module => true, + projectRoot: '/root', + runBeforeMainModule: [], + runModule: true, + serverRoot: '/root', + shouldAddToIgnoreList: () => false, + sourceMapUrl: 'http://localhost/bundle.map', + sourceUrl: null, + getSourceUrl: null, + }, + ); + // foo depends on bar, baz, qux — the callback must return those, not [undefined]. + expect([...resolvedDeps].sort()).toEqual([ + '/root/bar.js', + '/root/baz.js', + '/root/qux.js', + ]); +}); + test('emits x_google_ignoreList based on shouldAddToIgnoreList', async () => { expect( await getRamBundleInfo( diff --git a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js index 9b8047ec08..997f6561cc 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js +++ b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js @@ -160,7 +160,7 @@ async function _getRamOptions( /* $FlowFixMe[incompatible-type](>=0.99.0 site=react_native_fb) This comment suppresses an * error found when Flow v0.99 was deployed. To see the error, delete this * comment and run Flow. */ - async (x: string) => Array.from(getDependencies), + async (x: string) => Array.from(getDependencies(x)), ); return { From 502e8f442ab08f357fa289be7b086985034fd56b Mon Sep 17 00:00:00 2001 From: Rob Hogan <2590098+robhogan@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:00:21 +0100 Subject: [PATCH 2/4] Fix Flow error in the new getRamBundleInfo regression test `resolvedDeps` was declared without a type or initialiser, so spreading it fails `flow check` with "possibly uninitialized variable is incompatible with $Iterable". Annotate it and assert it was assigned before spreading, which also gives a clearer failure if `getTransformOptions` is never called with a dependency lookup. Changelog: [Internal] --- .../Serializers/__tests__/getRamBundleInfo-test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js index 6fc33a61d2..1921f670b2 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js @@ -113,7 +113,7 @@ test('should return the RAM bundle info', async () => { }); test('passes a working transitive-dependency lookup to getTransformOptions', async () => { - let resolvedDeps; + let resolvedDeps: ?Array; await getRamBundleInfo( '/root/entry.js', pre, @@ -146,6 +146,9 @@ test('passes a working transitive-dependency lookup to getTransformOptions', asy }, ); // foo depends on bar, baz, qux — the callback must return those, not [undefined]. + if (resolvedDeps == null) { + throw new Error('getTransformOptions was not called with a dependency lookup'); + } expect([...resolvedDeps].sort()).toEqual([ '/root/bar.js', '/root/baz.js', From 68a7cfc444461acc98500dbe9df6dd35c96de866 Mon Sep 17 00:00:00 2001 From: Rob Hogan <2590098+robhogan@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:00:35 +0100 Subject: [PATCH 3/4] Remove stale $FlowFixMe above the getTransformOptions dependency callback The suppression dates to Flow v0.99 in 2019 and no longer suppresses anything: `flow check` reports no error on this line with it removed, either with the fix in this stack or with the original `Array.from(getDependencies)`. Worth noting Flow did not catch this bug in either form, for the same reason it was silent at runtime - `Array.from` accepts array-likes, and a function satisfies that via `.length`. Changelog: [Internal] --- .../metro/src/DeltaBundler/Serializers/getRamBundleInfo.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js index 002dbebf63..dbfddf6585 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js +++ b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js @@ -157,9 +157,6 @@ async function _getRamOptions( const {preloadedModules, ramGroups} = await getTransformOptions( [entryFile], {dev: options.dev, hot: true, platform: options.platform}, - /* $FlowFixMe[incompatible-type](>=0.99.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.99 was deployed. To see the error, delete this - * comment and run Flow. */ async (x: string) => Array.from(getDependencies(x)), ); From 3dc81136cfdb9df19d99c13cd270ebe5df1b2faf Mon Sep 17 00:00:00 2001 From: Rob Hogan <2590098+robhogan@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:03:42 +0100 Subject: [PATCH 4/4] Apply Prettier formatting to the regression test The guard added in the previous commit exceeded the print width, failing `prettier --check` and so `yarn lint`. Changelog: [Internal] --- .../Serializers/__tests__/getRamBundleInfo-test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js index 1921f670b2..d60b196831 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js @@ -147,7 +147,9 @@ test('passes a working transitive-dependency lookup to getTransformOptions', asy ); // foo depends on bar, baz, qux — the callback must return those, not [undefined]. if (resolvedDeps == null) { - throw new Error('getTransformOptions was not called with a dependency lookup'); + throw new Error( + 'getTransformOptions was not called with a dependency lookup', + ); } expect([...resolvedDeps].sort()).toEqual([ '/root/bar.js',