diff --git a/packages/metro-runtime/src/polyfills/require.js b/packages/metro-runtime/src/polyfills/require.js index 5f1db314b5..088f0e68a8 100644 --- a/packages/metro-runtime/src/polyfills/require.js +++ b/packages/metro-runtime/src/polyfills/require.js @@ -17,7 +17,17 @@ declare var __DEV__: boolean; declare var __METRO_GLOBAL_PREFIX__: string; -type DependencyMap = Array; +// A simpler $ArrayLike. Not iterable and doesn't have a `length`. +// This is compatible with actual arrays as well as with objects that look like +// {0: 'value', 1: '...'} +type ArrayIndexable = interface { + +[indexer: number]: T, +}; +type DependencyMap = $ReadOnly< + ArrayIndexable & { + paths?: {[id: ModuleID]: string}, + }, +>; type Exports = any; type FactoryFn = ( global: Object, diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js index 6e05d90023..c3d8860856 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/baseJSBundle-test.js @@ -297,9 +297,11 @@ it('adds lazy imports at the end of a bundle', () => { ['/root/foo', fooModule], ['/root/bar', barModule], ]), + entryPoints: ['foo'], importBundleNames: new Set(['/path/to/async/module.js']), }, + { asyncRequireModulePath: '/asyncRequire.js', processModuleFilter: () => true, @@ -325,8 +327,7 @@ it('adds lazy imports at the end of a bundle', () => { "__d(function() {/* code for bar */},\\"bar\\",[],\\"bar\\");", ], ], - "post": "(function(){var $$=require(\\"asyncRequire.js\\");$$.addImportBundleNames({\\"module.js\\":\\"../path/to/async/module\\"})})(); - require(\\"foo\\"); + "post": "require(\\"foo\\"); //# sourceMappingURL=http://localhost/bundle.map", "pre": "__d(function() {/* code for polyfill */});", } @@ -343,9 +344,11 @@ it('lazy imports are relative to serverRoot if it differs from projectRoot', () ['/root/foo', fooModule], ['/root/bar', barModule], ]), + entryPoints: ['foo'], importBundleNames: new Set(['/path/to/async/module.js']), }, + { asyncRequireModulePath: '/asyncRequire.js', processModuleFilter: () => true, @@ -371,8 +374,7 @@ it('lazy imports are relative to serverRoot if it differs from projectRoot', () "__d(function() {/* code for bar */},\\"bar\\",[],\\"bar\\");", ], ], - "post": "(function(){var $$=require(\\"asyncRequire.js\\");$$.addImportBundleNames({\\"module.js\\":\\"path/to/async/module\\"})})(); - require(\\"foo\\"); + "post": "require(\\"foo\\"); //# sourceMappingURL=http://localhost/bundle.map", "pre": "__d(function() {/* code for polyfill */});", } diff --git a/packages/metro/src/DeltaBundler/Serializers/baseBytecodeBundle.js b/packages/metro/src/DeltaBundler/Serializers/baseBytecodeBundle.js index 1adf6020c5..a9b913bf65 100644 --- a/packages/metro/src/DeltaBundler/Serializers/baseBytecodeBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/baseBytecodeBundle.js @@ -37,7 +37,9 @@ function baseBytecodeBundle( filter: options.processModuleFilter, createModuleId: options.createModuleId, dev: options.dev, + includeAsyncPaths: options.includeAsyncPaths, projectRoot: options.projectRoot, + serverRoot: options.serverRoot, }; // Do not prepend polyfills or the require runtime when only modules are requested @@ -53,23 +55,16 @@ function baseBytecodeBundle( const {compile} = require('metro-hermes-compiler'); const post = processBytecodeModules( - getAppendScripts( - entryPoint, - [...preModules, ...modules], - graph.importBundleNames, - { - asyncRequireModulePath: options.asyncRequireModulePath, - createModuleId: options.createModuleId, - getRunModuleStatement: options.getRunModuleStatement, - inlineSourceMap: options.inlineSourceMap, - projectRoot: options.projectRoot, - runBeforeMainModule: options.runBeforeMainModule, - runModule: options.runModule, - serverRoot: options.serverRoot, - sourceMapUrl: options.sourceMapUrl, - sourceUrl: options.sourceUrl, - }, - ).map(module => { + getAppendScripts(entryPoint, [...preModules, ...modules], { + asyncRequireModulePath: options.asyncRequireModulePath, + createModuleId: options.createModuleId, + getRunModuleStatement: options.getRunModuleStatement, + inlineSourceMap: options.inlineSourceMap, + runBeforeMainModule: options.runBeforeMainModule, + runModule: options.runModule, + sourceMapUrl: options.sourceMapUrl, + sourceUrl: options.sourceUrl, + }).map(module => { return { ...module, output: [ diff --git a/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js index 0d18a60e12..f7946959db 100644 --- a/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js @@ -36,7 +36,9 @@ function baseJSBundle( filter: options.processModuleFilter, createModuleId: options.createModuleId, dev: options.dev, + includeAsyncPaths: options.includeAsyncPaths, projectRoot: options.projectRoot, + serverRoot: options.serverRoot, }; // Do not prepend polyfills or the require runtime when only modules are requested @@ -54,23 +56,16 @@ function baseJSBundle( ); const postCode = processModules( - getAppendScripts( - entryPoint, - [...preModules, ...modules], - graph.importBundleNames, - { - asyncRequireModulePath: options.asyncRequireModulePath, - createModuleId: options.createModuleId, - getRunModuleStatement: options.getRunModuleStatement, - inlineSourceMap: options.inlineSourceMap, - projectRoot: options.projectRoot, - runBeforeMainModule: options.runBeforeMainModule, - runModule: options.runModule, - serverRoot: options.serverRoot, - sourceMapUrl: options.sourceMapUrl, - sourceUrl: options.sourceUrl, - }, - ), + getAppendScripts(entryPoint, [...preModules, ...modules], { + asyncRequireModulePath: options.asyncRequireModulePath, + createModuleId: options.createModuleId, + getRunModuleStatement: options.getRunModuleStatement, + inlineSourceMap: options.inlineSourceMap, + runBeforeMainModule: options.runBeforeMainModule, + runModule: options.runModule, + sourceMapUrl: options.sourceMapUrl, + sourceUrl: options.sourceUrl, + }), processModulesOptions, ) .map(([_, code]) => code) diff --git a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js index cc19cebcb8..e3432e76c2 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js +++ b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js @@ -26,12 +26,12 @@ const {sourceMapObject} = require('./sourceMapObject'); const nullthrows = require('nullthrows'); const path = require('path'); -type Options = { +type Options = $ReadOnly<{ ...SerializerOptions, - +excludeSource: boolean, - +getTransformOptions: ?GetTransformOptions, - +platform: ?string, -}; + excludeSource: boolean, + getTransformOptions: ?GetTransformOptions, + platform: ?string, +}>; export type RamBundleInfo = { getDependencies: string => Set, @@ -50,9 +50,7 @@ async function getRamBundleInfo( ...pre, ...graph.dependencies.values(), ]; - modules = modules.concat( - getAppendScripts(entryPoint, modules, graph.importBundleNames, options), - ); + modules = modules.concat(getAppendScripts(entryPoint, modules, options)); modules.forEach((module: Module<>) => options.createModuleId(module.path)); diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/bytecode-test.js b/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/bytecode-test.js index 458cdaa1dc..bcf2463700 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/bytecode-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/bytecode-test.js @@ -9,14 +9,13 @@ * @oncall react_native */ -'use strict'; import type {Dependency} from '../../../types.flow'; import CountingSet from '../../../../lib/CountingSet'; -const createModuleIdFactory = require('../../../../lib/createModuleIdFactory'); -const {wrapModule} = require('../bytecode'); -const {compile, validateBytecodeModule} = require('metro-hermes-compiler'); +import createModuleIdFactory from '../../../../lib/createModuleIdFactory'; +import {wrapModule} from '../bytecode'; +import {compile, validateBytecodeModule} from 'metro-hermes-compiler'; let myModule, bytecode; @@ -70,7 +69,9 @@ it('produces a bytecode header buffer for each module', () => { const buffers = wrapModule(myModule, { createModuleId: createModuleIdFactory(), dev: true, + includeAsyncPaths: false, projectRoot: '/root', + serverRoot: '/root', }); expect(buffers.length).toBe(2); expect(() => validateBytecodeModule(buffers[0], 0)).not.toThrow(); @@ -83,7 +84,9 @@ it('does not produce a bytecode header buffer for a script', () => { const buffers = wrapModule(myModule, { createModuleId: createModuleIdFactory(), dev: true, + includeAsyncPaths: false, projectRoot: '/root', + serverRoot: '/root', }); expect(buffers.length).toBe(1); expect(buffers[0]).toBe(bytecode); diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js b/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js index f3df4e7c3d..14cf4244c6 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js @@ -9,16 +9,19 @@ * @oncall react_native */ -'use strict'; import type {Dependency} from '../../../types.flow'; import CountingSet from '../../../../lib/CountingSet'; -const createModuleIdFactory = require('../../../../lib/createModuleIdFactory'); -const {wrapModule} = require('../js'); +import {wrap as raw} from 'jest-snapshot-serializer-raw'; +import createModuleIdFactory from '../../../../lib/createModuleIdFactory'; +import {wrapModule} from '../js'; +import nullthrows from 'nullthrows'; let myModule; +expect.addSnapshotSerializer(require('jest-snapshot-serializer-raw')); + beforeEach(() => { myModule = { path: '/root/foo.js', @@ -26,14 +29,14 @@ beforeEach(() => { [ 'bar', { - absolutePath: '/bar', + absolutePath: '/bar.js', data: {data: {asyncType: null, locs: [], key: 'bar'}, name: 'bar'}, }, ], [ 'baz', { - absolutePath: '/baz', + absolutePath: '/baz.js', data: {data: {asyncType: null, locs: [], key: 'baz'}, name: 'baz'}, }, ], @@ -57,46 +60,106 @@ beforeEach(() => { describe('wrapModule()', () => { it('Should wrap a module in nondev mode', () => { expect( - wrapModule(myModule, { - createModuleId: createModuleIdFactory(), - dev: false, - projectRoot: '/root', - }), - ).toEqual('__d(function() { console.log("foo") },0,[1,2]);'); + raw( + wrapModule(myModule, { + createModuleId: createModuleIdFactory(), + dev: false, + includeAsyncPaths: false, + projectRoot: '/root', + serverRoot: '/root', + }), + ), + ).toMatchInlineSnapshot(`__d(function() { console.log("foo") },0,[1,2]);`); }); it('Should wrap a module in dev mode', () => { expect( - wrapModule(myModule, { - createModuleId: createModuleIdFactory(), - dev: true, - projectRoot: '/root', - }), - ).toEqual('__d(function() { console.log("foo") },0,[1,2],"foo.js");'); + raw( + wrapModule(myModule, { + createModuleId: createModuleIdFactory(), + dev: true, + includeAsyncPaths: false, + projectRoot: '/root', + serverRoot: '/root', + }), + ), + ).toMatchInlineSnapshot( + `__d(function() { console.log("foo") },0,[1,2],"foo.js");`, + ); }); it('should not wrap a script', () => { myModule.output[0].type = 'js/script'; expect( - wrapModule(myModule, { - createModuleId: createModuleIdFactory(), - dev: true, - projectRoot: '/root', - }), - ).toEqual(myModule.output[0].data.code); + raw( + wrapModule(myModule, { + createModuleId: createModuleIdFactory(), + dev: true, + includeAsyncPaths: false, + projectRoot: '/root', + serverRoot: '/root', + }), + ), + ).toMatchInlineSnapshot(`__d(function() { console.log("foo") });`); }); it('should use custom createModuleId param', () => { // Just use a createModuleId that returns the same path. expect( - wrapModule(myModule, { - createModuleId: (path: string) => path, - dev: false, - projectRoot: '/root', - }), - ).toEqual( - '__d(function() { console.log("foo") },"/root/foo.js",["/bar","/baz"]);', + raw( + wrapModule(myModule, { + createModuleId: (path: string) => path, + dev: false, + includeAsyncPaths: false, + projectRoot: '/root', + serverRoot: '/root', + }), + ), + ).toMatchInlineSnapshot( + `__d(function() { console.log("foo") },"/root/foo.js",["/bar.js","/baz.js"]);`, + ); + }); + + it('includes the paths of async dependencies when requested', () => { + const dep = nullthrows(myModule.dependencies.get('bar')); + myModule.dependencies.set('bar', { + ...dep, + data: {...dep.data, data: {...dep.data.data, asyncType: 'async'}}, + }); + expect( + raw( + wrapModule(myModule, { + createModuleId: createModuleIdFactory(), + dev: false, + includeAsyncPaths: true, + projectRoot: '/root', + serverRoot: '/root', + }), + ), + ).toMatchInlineSnapshot( + `__d(function() { console.log("foo") },0,{"0":1,"1":2,"paths":{"1":"../bar"}});`, + ); + }); + + it('async dependency paths respect serverRoot', () => { + const dep = nullthrows(myModule.dependencies.get('bar')); + myModule.dependencies.set('bar', { + ...dep, + data: {...dep.data, data: {...dep.data.data, asyncType: 'async'}}, + }); + expect( + raw( + wrapModule(myModule, { + createModuleId: createModuleIdFactory(), + dev: false, + includeAsyncPaths: true, + projectRoot: '/root', + serverRoot: '/', + }), + ), + ).toMatchInlineSnapshot( + `__d(function() { console.log("foo") },0,{"0":1,"1":2,"paths":{"1":"bar"}});`, ); }); }); diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/bytecode.js b/packages/metro/src/DeltaBundler/Serializers/helpers/bytecode.js index 88c3724293..bd40ecdf64 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/bytecode.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/bytecode.js @@ -14,15 +14,10 @@ import type {Module} from '../../types.flow'; import type {BytecodeOutput} from 'metro-transform-worker'; -const invariant = require('invariant'); -const path = require('path'); +import type {Options} from './js'; +const {getModuleParams} = require('./js'); -export type Options = { - +createModuleId: string => number | string, - +dev: boolean, - +projectRoot: string, - ... -}; +const invariant = require('invariant'); function wrapModule(module: Module<>, options: Options): Array { const output = getBytecodeOutput(module); @@ -31,26 +26,10 @@ function wrapModule(module: Module<>, options: Options): Array { return [output.data.bytecode]; } - const params = [ - options.createModuleId(module.path), - '[' + - Array.from(module.dependencies.values()) - .map(dependency => options.createModuleId(dependency.absolutePath)) - .join(',') + - ']', - ]; - - if (options.dev) { - // Add the relative path of the module to make debugging easier. - // This is mapped to `module.verboseName` in `require.js`. - params.push( - JSON.stringify(path.relative(options.projectRoot, module.path)), - ); - } - + const params = getModuleParams(module, options); const {compile} = require('metro-hermes-compiler'); - const headerCode = `globalThis.$$METRO_D=[${params.join(',')}];`; + const headerCode = `globalThis.$$METRO_D=[${JSON.stringify(params)}];`; return [ compile(headerCode, { sourceURL: module.path + '-virtual.js', diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/js.js b/packages/metro/src/DeltaBundler/Serializers/helpers/js.js index a54708fd14..a2aec82834 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/js.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/js.js @@ -18,12 +18,14 @@ const invariant = require('invariant'); const {addParamsToDefineCall} = require('metro-transform-plugins'); const path = require('path'); -export type Options = { - +createModuleId: string => number | string, - +dev: boolean, - +projectRoot: string, +export type Options = $ReadOnly<{ + createModuleId: string => number | string, + dev: boolean, + includeAsyncPaths: boolean, + projectRoot: string, + serverRoot: string, ... -}; +}>; function wrapModule(module: Module<>, options: Options): string { const output = getJsOutput(module); @@ -32,12 +34,44 @@ function wrapModule(module: Module<>, options: Options): string { return output.data.code; } + const params = getModuleParams(module, options); + return addParamsToDefineCall(output.data.code, ...params); +} + +function getModuleParams(module: Module<>, options: Options): Array { const moduleId = options.createModuleId(module.path); + + const paths: {[moduleID: number | string]: mixed} = {}; + let hasPaths = false; + const dependencyMapArray = Array.from(module.dependencies.values()).map( + dependency => { + const id = options.createModuleId(dependency.absolutePath); + if (options.includeAsyncPaths && dependency.data.data.asyncType != null) { + hasPaths = true; + const bundlePath = path.relative( + options.serverRoot, + dependency.absolutePath, + ); + // TODO: Eventually this slicing should be asyncRequire's responsibility + // Strip the file extension + paths[id] = path.join( + path.dirname(bundlePath), + path.basename(bundlePath, path.extname(bundlePath)), + ); + } + return id; + }, + ); + const params = [ moduleId, - Array.from(module.dependencies.values()).map(dependency => - options.createModuleId(dependency.absolutePath), - ), + hasPaths + ? { + // $FlowIgnore[not-an-object] Intentionally spreading an array into an object + ...dependencyMapArray, + paths, + } + : dependencyMapArray, ]; if (options.dev) { @@ -46,7 +80,7 @@ function wrapModule(module: Module<>, options: Options): string { params.push(path.relative(options.projectRoot, module.path)); } - return addParamsToDefineCall(output.data.code, ...params); + return params; } function getJsOutput( @@ -87,6 +121,7 @@ function isJsOutput(output: MixedOutput): boolean %checks { module.exports = { getJsOutput, + getModuleParams, isJsModule, wrapModule, }; diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js b/packages/metro/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js index 04c841e16d..e3abc8db0b 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js @@ -21,13 +21,17 @@ function processBytecodeModules( filter = () => true, createModuleId, dev, + includeAsyncPaths, projectRoot, - }: { - +filter?: (module: Module<>) => boolean, - +createModuleId: string => number, - +dev: boolean, - +projectRoot: string, - }, + serverRoot, + }: $ReadOnly<{ + filter?: (module: Module<>) => boolean, + createModuleId: string => number, + dev: boolean, + includeAsyncPaths: boolean, + projectRoot: string, + serverRoot: string, + }>, ): $ReadOnlyArray<[Module<>, Array]> { return [...modules] .filter(isBytecodeModule) @@ -37,7 +41,9 @@ function processBytecodeModules( wrapModule(module, { createModuleId, dev, + includeAsyncPaths, projectRoot, + serverRoot, }), ]); } diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js b/packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js index 63d1a658e1..52474df3f4 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js @@ -21,13 +21,17 @@ function processModules( filter = () => true, createModuleId, dev, + includeAsyncPaths, projectRoot, - }: { - +filter?: (module: Module<>) => boolean, - +createModuleId: string => number, - +dev: boolean, - +projectRoot: string, - }, + serverRoot, + }: $ReadOnly<{ + filter?: (module: Module<>) => boolean, + createModuleId: string => number, + dev: boolean, + includeAsyncPaths: boolean, + projectRoot: string, + serverRoot: string, + }>, ): $ReadOnlyArray<[Module<>, string]> { return [...modules] .filter(isJsModule) @@ -37,7 +41,9 @@ function processModules( wrapModule(module, { createModuleId, dev, + includeAsyncPaths, projectRoot, + serverRoot, }), ]); } diff --git a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js index eaf9b478db..409032f03d 100644 --- a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js @@ -20,12 +20,14 @@ const {addParamsToDefineCall} = require('metro-transform-plugins'); const path = require('path'); const url = require('url'); -type Options = { +type Options = $ReadOnly<{ +clientUrl: EntryPointURL, +createModuleId: string => number, + +includeAsyncPaths: boolean, +projectRoot: string, + +serverRoot: string, ... -}; +}>; function generateModules( sourceModules: Iterable>, diff --git a/packages/metro/src/DeltaBundler/types.flow.js b/packages/metro/src/DeltaBundler/types.flow.js index 5d78d82aaa..344859c945 100644 --- a/packages/metro/src/DeltaBundler/types.flow.js +++ b/packages/metro/src/DeltaBundler/types.flow.js @@ -144,18 +144,19 @@ export type DeltaResult = { +reset: boolean, }; -export type SerializerOptions = { - +asyncRequireModulePath: string, - +createModuleId: string => number, - +dev: boolean, - +getRunModuleStatement: (number | string) => string, - +inlineSourceMap: ?boolean, - +modulesOnly: boolean, - +processModuleFilter: (module: Module<>) => boolean, - +projectRoot: string, - +runBeforeMainModule: $ReadOnlyArray, - +runModule: boolean, - +serverRoot: string, - +sourceMapUrl: ?string, - +sourceUrl: ?string, -}; +export type SerializerOptions = $ReadOnly<{ + asyncRequireModulePath: string, + createModuleId: string => number, + dev: boolean, + getRunModuleStatement: (number | string) => string, + includeAsyncPaths: boolean, + inlineSourceMap: ?boolean, + modulesOnly: boolean, + processModuleFilter: (module: Module<>) => boolean, + projectRoot: string, + runBeforeMainModule: $ReadOnlyArray, + runModule: boolean, + serverRoot: string, + sourceMapUrl: ?string, + sourceUrl: ?string, +}>; diff --git a/packages/metro/src/HmrServer.js b/packages/metro/src/HmrServer.js index 415770811c..16c9befec4 100644 --- a/packages/metro/src/HmrServer.js +++ b/packages/metro/src/HmrServer.js @@ -358,10 +358,12 @@ class HmrServer { logger?.point('serialize_start'); const hmrUpdate = hmrJSBundle(delta, revision.graph, { + clientUrl: group.clientUrl, createModuleId: this._createModuleId, - projectRoot: + includeAsyncPaths: this._config.server.experimentalImportBundleSupport, + projectRoot: this._config.projectRoot, + serverRoot: this._config.server.unstable_serverRoot ?? this._config.projectRoot, - clientUrl: group.clientUrl, }); logger?.point('serialize_end'); diff --git a/packages/metro/src/ModuleGraph/worker/__tests__/collectDependencies-test.js b/packages/metro/src/ModuleGraph/worker/__tests__/collectDependencies-test.js index 6d0daa2ea3..eb5da60086 100644 --- a/packages/metro/src/ModuleGraph/worker/__tests__/collectDependencies-test.js +++ b/packages/metro/src/ModuleGraph/worker/__tests__/collectDependencies-test.js @@ -815,7 +815,7 @@ it('collects asynchronous dependencies', () => { ]); expect(codeFromAst(ast)).toEqual( comparableCode(` - require(${dependencyMapName}[1], "asyncRequire")(${dependencyMapName}[0], "some/async/module").then(foo => {}); + require(${dependencyMapName}[1], "asyncRequire")(${dependencyMapName}[0], "some/async/module", _dependencyMap.paths).then(foo => {}); `), ); }); @@ -834,7 +834,7 @@ it('distinguishes sync and async dependencies on the same module', () => { expect(codeFromAst(ast)).toEqual( comparableCode(` const a = require(${dependencyMapName}[0], "some/async/module"); - require(${dependencyMapName}[2], "asyncRequire")(${dependencyMapName}[1], "some/async/module").then(foo => {}); + require(${dependencyMapName}[2], "asyncRequire")(${dependencyMapName}[1], "some/async/module", _dependencyMap.paths).then(foo => {}); `), ); }); @@ -852,7 +852,7 @@ it('distinguishes sync and async dependencies on the same module; reverse order' ]); expect(codeFromAst(ast)).toEqual( comparableCode(` - require(${dependencyMapName}[1], "asyncRequire")(${dependencyMapName}[0], "some/async/module").then(foo => {}); + require(${dependencyMapName}[1], "asyncRequire")(${dependencyMapName}[0], "some/async/module", _dependencyMap.paths).then(foo => {}); const a = require(${dependencyMapName}[2], "some/async/module"); `), ); @@ -869,7 +869,7 @@ it('collects __jsResource calls', () => { ]); expect(codeFromAst(ast)).toEqual( comparableCode(` - require(${dependencyMapName}[1], "asyncRequire").resource(${dependencyMapName}[0], "some/async/module"); + require(${dependencyMapName}[1], "asyncRequire").resource(${dependencyMapName}[0], "some/async/module", _dependencyMap.paths); `), ); }); @@ -901,7 +901,7 @@ describe('import() prefetching', () => { ]); expect(codeFromAst(ast)).toEqual( comparableCode(` - require(${dependencyMapName}[1], "asyncRequire").prefetch(${dependencyMapName}[0], "some/async/module"); + require(${dependencyMapName}[1], "asyncRequire").prefetch(${dependencyMapName}[0], "some/async/module", _dependencyMap.paths); `), ); }); diff --git a/packages/metro/src/ModuleGraph/worker/collectDependencies.js b/packages/metro/src/ModuleGraph/worker/collectDependencies.js index d21d6c390f..a13740a63d 100644 --- a/packages/metro/src/ModuleGraph/worker/collectDependencies.js +++ b/packages/metro/src/ModuleGraph/worker/collectDependencies.js @@ -632,15 +632,15 @@ const dynamicRequireErrorTemplate = template.statement(` * "require(...)" call to the asyncRequire specified. */ const makeAsyncRequireTemplate = template.statement(` - require(ASYNC_REQUIRE_MODULE_PATH)(MODULE_ID, MODULE_NAME) + require(ASYNC_REQUIRE_MODULE_PATH)(MODULE_ID, MODULE_NAME, DEPENDENCY_MAP.paths) `); const makeAsyncPrefetchTemplate = template.statement(` - require(ASYNC_REQUIRE_MODULE_PATH).prefetch(MODULE_ID, MODULE_NAME) + require(ASYNC_REQUIRE_MODULE_PATH).prefetch(MODULE_ID, MODULE_NAME, DEPENDENCY_MAP.paths) `); const makeJSResourceTemplate = template.statement(` - require(ASYNC_REQUIRE_MODULE_PATH).resource(MODULE_ID, MODULE_NAME) + require(ASYNC_REQUIRE_MODULE_PATH).resource(MODULE_ID, MODULE_NAME, DEPENDENCY_MAP.paths) `); const DefaultDependencyTransformer: DependencyTransformer = { @@ -674,6 +674,7 @@ const DefaultDependencyTransformer: DependencyTransformer = { ), MODULE_ID: createModuleIDExpression(dependency, state), MODULE_NAME: createModuleNameLiteral(dependency), + DEPENDENCY_MAP: nullthrows(state.dependencyMapIdentifier), }), ); }, @@ -690,6 +691,7 @@ const DefaultDependencyTransformer: DependencyTransformer = { ), MODULE_ID: createModuleIDExpression(dependency, state), MODULE_NAME: createModuleNameLiteral(dependency), + DEPENDENCY_MAP: nullthrows(state.dependencyMapIdentifier), }), ); }, @@ -706,6 +708,7 @@ const DefaultDependencyTransformer: DependencyTransformer = { ), MODULE_ID: createModuleIDExpression(dependency, state), MODULE_NAME: createModuleNameLiteral(dependency), + DEPENDENCY_MAP: nullthrows(state.dependencyMapIdentifier), }), ); }, diff --git a/packages/metro/src/Server.js b/packages/metro/src/Server.js index 0065000c81..77d66cdf34 100644 --- a/packages/metro/src/Server.js +++ b/packages/metro/src/Server.js @@ -225,6 +225,7 @@ class Server { createModuleId: this._createModuleId, getRunModuleStatement: this._config.serializer.getRunModuleStatement, dev: transformOptions.dev, + includeAsyncPaths: this._config.server.experimentalImportBundleSupport, projectRoot: this._config.projectRoot, modulesOnly: serializerOptions.modulesOnly, runBeforeMainModule: @@ -307,6 +308,7 @@ class Server { excludeSource: serializerOptions.excludeSource, getRunModuleStatement: this._config.serializer.getRunModuleStatement, getTransformOptions: this._config.transformer.getTransformOptions, + includeAsyncPaths: this._config.server.experimentalImportBundleSupport, platform: transformOptions.platform, projectRoot: this._config.projectRoot, modulesOnly: serializerOptions.modulesOnly, @@ -865,6 +867,8 @@ class Server { processModuleFilter: this._config.serializer.processModuleFilter, createModuleId: this._createModuleId, getRunModuleStatement: this._config.serializer.getRunModuleStatement, + includeAsyncPaths: + this._config.server.experimentalImportBundleSupport, dev: transformOptions.dev, projectRoot: this._config.projectRoot, modulesOnly: serializerOptions.modulesOnly, @@ -996,6 +1000,8 @@ class Server { processModuleFilter: this._config.serializer.processModuleFilter, createModuleId: this._createModuleId, getRunModuleStatement: this._config.serializer.getRunModuleStatement, + includeAsyncPaths: + this._config.server.experimentalImportBundleSupport, dev: transformOptions.dev, projectRoot: this._config.projectRoot, modulesOnly: serializerOptions.modulesOnly, diff --git a/packages/metro/src/lib/getAppendScripts.js b/packages/metro/src/lib/getAppendScripts.js index cbc2a44b7c..23d56b1957 100644 --- a/packages/metro/src/lib/getAppendScripts.js +++ b/packages/metro/src/lib/getAppendScripts.js @@ -10,9 +10,8 @@ */ 'use strict'; -import type {Dependency} from '../DeltaBundler/types.flow'; - import type {Module} from '../DeltaBundler'; +import type {Dependency} from '../DeltaBundler/types.flow'; import CountingSet from './CountingSet'; @@ -20,15 +19,12 @@ const getInlineSourceMappingURL = require('../DeltaBundler/Serializers/helpers/g const sourceMapString = require('../DeltaBundler/Serializers/sourceMapString'); const countLines = require('./countLines'); const nullthrows = require('nullthrows'); -const path = require('path'); type Options = { +asyncRequireModulePath: string, +createModuleId: string => T, +getRunModuleStatement: T => string, +inlineSourceMap: ?boolean, - +projectRoot: string, - +serverRoot: string, +runBeforeMainModule: $ReadOnlyArray, +runModule: boolean, +sourceMapUrl: ?string, @@ -39,42 +35,10 @@ type Options = { function getAppendScripts( entryPoint: string, modules: $ReadOnlyArray>, - importBundleNames: $ReadOnlySet, options: Options, ): $ReadOnlyArray> { const output = []; - if (importBundleNames.size) { - const importBundleNamesObject = Object.create(null); - importBundleNames.forEach(absolutePath => { - const bundlePath = path.relative(options.serverRoot, absolutePath); - // $FlowFixMe[prop-missing] - importBundleNamesObject[options.createModuleId(absolutePath)] = - bundlePath.slice(0, -path.extname(bundlePath).length); - }); - const code = `(function(){var $$=${options.getRunModuleStatement( - options.createModuleId(options.asyncRequireModulePath), - )}$$.addImportBundleNames(${String( - JSON.stringify(importBundleNamesObject), - )})})();`; - output.push({ - path: '$$importBundleNames', - dependencies: new Map(), - getSource: (): Buffer => Buffer.from(''), - inverseDependencies: new CountingSet(), - output: [ - { - type: 'js/script/virtual', - data: { - code, - lineCount: countLines(code), - map: [], - }, - }, - ], - }); - } - if (options.runModule) { const paths = [...options.runBeforeMainModule, entryPoint];