From 5c8b86aed5ce7789d1a72ed0847d13c102b6fde9 Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Fri, 11 May 2018 15:05:05 -0700 Subject: [PATCH] Change the Graph data structure to support multiple transform output types Reviewed By: davidaurelio Differential Revision: D7877461 fbshipit-source-id: 02a7bf2273768fed567aa931ecb8e65b3dd74502 --- .../getRamBundleInfo-test.js.snap | 16 ++--- .../__tests__/deltaJSBundle-test.js | 4 +- .../Serializers/__tests__/getAllFiles-test.js | 20 +++--- .../Serializers/__tests__/getAssets-test.js | 10 +-- .../__tests__/getRamBundleInfo-test.js | 3 +- .../__tests__/plainJSBundle-test.js | 18 +++-- .../__tests__/sourceMapString-test.js | 48 ++++++++----- .../DeltaBundler/Serializers/deltaJSBundle.js | 27 ++++--- .../DeltaBundler/Serializers/getAllFiles.js | 28 +++++--- .../src/DeltaBundler/Serializers/getAssets.js | 22 +++--- .../Serializers/getRamBundleInfo.js | 10 +-- .../Serializers/helpers/__tests__/js-test.js | 21 +++--- .../DeltaBundler/Serializers/helpers/js.js | 27 ++++++- .../DeltaBundler/Serializers/hmrJSBundle.js | 6 +- .../DeltaBundler/Serializers/plainJSBundle.js | 3 +- .../Serializers/sourceMapObject.js | 16 +++-- .../Serializers/sourceMapString.js | 16 +++-- .../traverseDependencies-test.js.snap | 64 ++++++++++------- .../__tests__/traverseDependencies-test.js | 16 +++-- .../src/DeltaBundler/traverseDependencies.js | 35 ++++------ .../src/HmrServer/__tests__/HmrServer-test.js | 12 ++-- .../metro/src/Server/__tests__/Server-test.js | 70 +++++++++++-------- packages/metro/src/lib/getAppendScripts.js | 34 +++++---- packages/metro/src/lib/getPrependedScripts.js | 16 +++-- packages/metro/src/lib/transformHelpers.js | 30 ++++---- 25 files changed, 345 insertions(+), 227 deletions(-) diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/__snapshots__/getRamBundleInfo-test.js.snap b/packages/metro/src/DeltaBundler/Serializers/__tests__/__snapshots__/getRamBundleInfo-test.js.snap index 02ab5214..c74c9678 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/__snapshots__/getRamBundleInfo-test.js.snap +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/__snapshots__/getRamBundleInfo-test.js.snap @@ -17,7 +17,7 @@ Object { "version": 3, }, "name": "entry.js", - "source": undefined, + "source": "source of entry", "sourcePath": "/root/entry.js", "type": "js/module", }, @@ -33,7 +33,7 @@ Object { "version": 3, }, "name": "entry2.js", - "source": undefined, + "source": "source of entry2", "sourcePath": "/root/entry2.js", "type": "js/module", }, @@ -49,7 +49,7 @@ Object { "version": 3, }, "name": "foo2.js", - "source": undefined, + "source": "source of foo2", "sourcePath": "/root/foo2.js", "type": "js/module", }, @@ -65,7 +65,7 @@ Object { "version": 3, }, "name": "foo.js", - "source": undefined, + "source": "source of foo", "sourcePath": "/root/foo.js", "type": "js/module", }, @@ -81,7 +81,7 @@ Object { "version": 3, }, "name": "baz.js", - "source": undefined, + "source": "source of baz", "sourcePath": "/root/baz.js", "type": "js/module/asset", }, @@ -97,7 +97,7 @@ Object { "version": 3, }, "name": "bar.js", - "source": undefined, + "source": "source of bar", "sourcePath": "/root/bar.js", "type": "js/module", }, @@ -113,7 +113,7 @@ Object { "version": 3, }, "name": "qux.js", - "source": undefined, + "source": "source of qux", "sourcePath": "/root/qux.js", "type": "js/module", }, @@ -131,7 +131,7 @@ Object { "version": 3, }, "name": "pre.js", - "source": undefined, + "source": "source of pre", "sourcePath": "/root/pre.js", "type": "js/script", }, diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/deltaJSBundle-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/deltaJSBundle-test.js index 66a4acbc..969f44b1 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/deltaJSBundle-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/deltaJSBundle-test.js @@ -13,7 +13,7 @@ const createModuleIdFactory = require('../../../lib/createModuleIdFactory'); const deltaJSBundle = require('../deltaJSBundle'); -function createModule(name, dependencies, type = 'module') { +function createModule(name, dependencies, type = 'js/module') { return [ `/root/${name}.js`, { @@ -24,7 +24,7 @@ function createModule(name, dependencies, type = 'module') { {absolutePath: `/root/${dep}.js`, data: {isAsync: false, name: dep}}, ]), ), - output: {type, code: `__d(function() {${name}()});`}, + output: [{type, data: {code: `__d(function() {${name}()});`}}], }, ]; } diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getAllFiles-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getAllFiles-test.js index c288c2d6..86ec5a6d 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getAllFiles-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getAllFiles-test.js @@ -27,16 +27,16 @@ describe('getOrderedDependencyPaths', () => { it('Should return all module dependencies correctly', async () => { const graph = { dependencies: new Map([ - [1, {path: '/tmp/1.js', output: {type: 'js/module'}}], - [2, {path: '/tmp/2.js', output: {type: 'js/module'}}], - [3, {path: '/tmp/3.js', output: {type: 'js/module'}}], - [4, {path: '/tmp/4.js', output: {type: 'js/module'}}], + [1, {path: '/tmp/1.js', output: [{type: 'js/module'}]}], + [2, {path: '/tmp/2.js', output: [{type: 'js/module'}]}], + [3, {path: '/tmp/3.js', output: [{type: 'js/module'}]}], + [4, {path: '/tmp/4.js', output: [{type: 'js/module'}]}], ]), }; expect( await getAllFiles( - [{path: '/tmp/0.js', output: {type: 'js/module'}}], + [{path: '/tmp/0.js', output: [{type: 'js/module'}]}], graph, {}, ), @@ -52,11 +52,11 @@ describe('getOrderedDependencyPaths', () => { it('Should add assets data dependencies correctly', async () => { const graph = { dependencies: new Map([ - [1, {path: '/tmp/1.js', output: {type: 'js/module'}}], - [2, {path: '/tmp/2.png', output: {type: 'js/module/asset'}}], - [3, {path: '/tmp/3.js', output: {type: 'js/module'}}], - [4, {path: '/tmp/4.png', output: {type: 'js/module/asset'}}], - [5, {path: '/tmp/5.js', output: {type: 'js/module'}}], + [1, {path: '/tmp/1.js', output: [{type: 'js/module'}]}], + [2, {path: '/tmp/2.png', output: [{type: 'js/module/asset'}]}], + [3, {path: '/tmp/3.js', output: [{type: 'js/module'}]}], + [4, {path: '/tmp/4.png', output: [{type: 'js/module/asset'}]}], + [5, {path: '/tmp/5.js', output: [{type: 'js/module'}]}], ]), }; diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getAssets-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getAssets-test.js index 32184410..5e8b157c 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getAssets-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getAssets-test.js @@ -26,11 +26,11 @@ beforeEach(() => { it('should return the bundle assets', async () => { const graph = { dependencies: new Map([ - ['/tmp/1.js', {path: '/tmp/1.js', output: {type: 'js/module'}}], - ['/tmp/2.js', {path: '/tmp/2.js', output: {type: 'js/module'}}], - ['/tmp/3.png', {path: '/tmp/3.png', output: {type: 'js/module/asset'}}], - ['/tmp/4.js', {path: '/tmp/2.js', output: {type: 'js/module'}}], - ['/tmp/5.mov', {path: '/tmp/5.mov', output: {type: 'js/module/asset'}}], + ['/tmp/1.js', {path: '/tmp/1.js', output: [{type: 'js/module'}]}], + ['/tmp/2.js', {path: '/tmp/2.js', output: [{type: 'js/module'}]}], + ['/tmp/3.png', {path: '/tmp/3.png', output: [{type: 'js/module/asset'}]}], + ['/tmp/4.js', {path: '/tmp/2.js', output: [{type: 'js/module'}]}], + ['/tmp/5.mov', {path: '/tmp/5.mov', output: [{type: 'js/module/asset'}]}], ]), }; diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js index 0e1fb9be..17ff78bb 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/getRamBundleInfo-test.js @@ -23,7 +23,8 @@ function createModule(name, dependencies, type = 'js/module') { {absolutePath: `/root/${dep}.js`, data: {isAsync: false, name: dep}}, ]), ), - output: {type, code: `__d(function() {${name}()});`}, + getSource: () => `source of ${name}`, + output: [{type, data: {code: `__d(function() {${name}()});`}}], }, ]; } diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/plainJSBundle-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/plainJSBundle-test.js index f34ca1a5..696e517a 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/plainJSBundle-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/plainJSBundle-test.js @@ -14,22 +14,28 @@ const createModuleIdFactory = require('../../../lib/createModuleIdFactory'); const plainJSBundle = require('../plainJSBundle'); const polyfill = { - output: { - type: 'js/script', - code: '__d(function() {/* code for polyfill */});', - }, + output: [ + { + type: 'js/script', + data: {code: '__d(function() {/* code for polyfill */});'}, + }, + ], }; const fooModule = { path: 'foo', dependencies: new Map([['./bar', {absolutePath: 'bar', data: {}}]]), - output: {type: 'js/module', code: '__d(function() {/* code for foo */});'}, + output: [ + {type: 'js/module', data: {code: '__d(function() {/* code for foo */});'}}, + ], }; const barModule = { path: 'bar', dependencies: new Map(), - output: {type: 'js/module', code: '__d(function() {/* code for bar */});'}, + output: [ + {type: 'js/module', data: {code: '__d(function() {/* code for bar */});'}}, + ], }; const getRunModuleStatement = moduleId => diff --git a/packages/metro/src/DeltaBundler/Serializers/__tests__/sourceMapString-test.js b/packages/metro/src/DeltaBundler/Serializers/__tests__/sourceMapString-test.js index 5e8265f7..4171d4d0 100644 --- a/packages/metro/src/DeltaBundler/Serializers/__tests__/sourceMapString-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/__tests__/sourceMapString-test.js @@ -14,34 +14,46 @@ const sourceMapString = require('../sourceMapString'); const polyfill = { path: '/root/pre.js', - output: { - type: 'js/script', - code: '__d(function() {/* code for polyfill */});', - map: [], - source: 'source pre', - }, + getSource: () => 'source pre', + output: [ + { + type: 'js/script', + data: { + code: '__d(function() {/* code for polyfill */});', + map: [], + }, + }, + ], }; const fooModule = { path: '/root/foo.js', dependencies: new Map([['./bar', 'bar']]), - output: { - type: 'js/module', - code: '__d(function() {/* code for foo */});', - map: [], - source: 'source foo', - }, + getSource: () => 'source foo', + output: [ + { + type: 'js/module', + data: { + code: '__d(function() {/* code for foo */});', + map: [], + }, + }, + ], }; const barModule = { path: '/root/bar.js', dependencies: new Map(), - output: { - type: 'js/module', - code: '__d(function() {/* code for bar */});', - map: [], - source: 'source bar', - }, + getSource: () => 'source bar', + output: [ + { + type: 'js/module', + data: { + code: '__d(function() {/* code for bar */});', + map: [], + }, + }, + ], }; it('should serialize a very simple bundle', () => { diff --git a/packages/metro/src/DeltaBundler/Serializers/deltaJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/deltaJSBundle.js index 1c1c3b70..060ff696 100644 --- a/packages/metro/src/DeltaBundler/Serializers/deltaJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/deltaJSBundle.js @@ -13,6 +13,7 @@ const getAppendScripts = require('../../lib/getAppendScripts'); const {wrapModule} = require('./helpers/js'); +const {getJsOutput, isJsModule} = require('./helpers/js'); import type {Delta, Graph} from '../../DeltaBundler'; import type {Module} from '../traverseDependencies'; @@ -39,10 +40,12 @@ function deltaJSBundle( const outputDelta = []; for (const module of delta.modified.values()) { - outputDelta.push([ - options.createModuleId(module.path), - wrapModule(module, options), - ]); + if (isJsModule(module)) { + outputDelta.push([ + options.createModuleId(module.path), + wrapModule(module, options), + ]); + } } for (const path of delta.deleted) { @@ -53,17 +56,21 @@ function deltaJSBundle( let i = -1; for (const module of pre) { - outputPre.push([i, module.output.code]); - i--; + if (isJsModule(module)) { + outputPre.push([i, getJsOutput(module).data.code]); + i--; + } } const appendScripts = getAppendScripts(entryPoint, graph, options).values(); for (const module of appendScripts) { - outputPost.push([ - options.createModuleId(module.path), - module.output.code, - ]); + if (isJsModule(module)) { + outputPost.push([ + options.createModuleId(module.path), + getJsOutput(module).data.code, + ]); + } } } diff --git a/packages/metro/src/DeltaBundler/Serializers/getAllFiles.js b/packages/metro/src/DeltaBundler/Serializers/getAllFiles.js index e6a1a75e..3cc52727 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getAllFiles.js +++ b/packages/metro/src/DeltaBundler/Serializers/getAllFiles.js @@ -11,6 +11,7 @@ 'use strict'; const {getAssetFiles} = require('../../Assets'); +const {getJsOutput, isJsModule} = require('./helpers/js'); import type {Graph} from '../DeltaCalculator'; import type {Module} from '../traverseDependencies'; @@ -26,16 +27,25 @@ async function getAllFiles( ): Promise<$ReadOnlyArray> { const modules = graph.dependencies; - const dependencies = await Promise.all( - [...pre, ...modules.values()].map(async module => { - if (module.output.type !== 'js/module/asset') { - return [module.path]; - } else { - return await getAssetFiles(module.path, options.platform); - } - }), - ); + const promises = []; + for (const module of pre) { + promises.push([module.path]); + } + + for (const module of modules.values()) { + if (!isJsModule(module)) { + continue; + } + + if (getJsOutput(module).type === 'js/module/asset') { + promises.push(getAssetFiles(module.path, options.platform)); + } else { + promises.push([module.path]); + } + } + + const dependencies = await Promise.all(promises); const output = []; for (const dependencyArray of dependencies) { diff --git a/packages/metro/src/DeltaBundler/Serializers/getAssets.js b/packages/metro/src/DeltaBundler/Serializers/getAssets.js index 0f1cf595..3d900985 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getAssets.js +++ b/packages/metro/src/DeltaBundler/Serializers/getAssets.js @@ -13,6 +13,7 @@ const toLocalPath = require('../../node-haste/lib/toLocalPath'); const {getAssetData} = require('../../Assets'); +const {getJsOutput, isJsModule} = require('./helpers/js'); import type {AssetData} from '../../Assets'; import type {Graph} from '../DeltaCalculator'; @@ -27,21 +28,22 @@ async function getAssets( graph: Graph, options: Options, ): Promise<$ReadOnlyArray> { - const assets = await Promise.all( - Array.from(graph.dependencies.values()).map(async module => { - if (module.output.type === 'js/module/asset') { - return getAssetData( + const promises = []; + + for (const module of graph.dependencies.values()) { + if (isJsModule(module) && getJsOutput(module).type === 'js/module/asset') { + promises.push( + getAssetData( module.path, toLocalPath(options.projectRoots, module.path), options.assetPlugins, options.platform, - ); - } - return null; - }), - ); + ), + ); + } + } - return assets.filter(Boolean); + return await Promise.all(promises); } module.exports = getAssets; diff --git a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js index dba0b103..6207f0d6 100644 --- a/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js +++ b/packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js @@ -13,10 +13,11 @@ const fullSourceMapObject = require('./sourceMapObject'); const getAppendScripts = require('../../lib/getAppendScripts'); const getTransitiveDependencies = require('./helpers/getTransitiveDependencies'); +const nullthrows = require('fbjs/lib/nullthrows'); const path = require('path'); const {createRamBundleGroups} = require('../../Bundler/util'); -const {wrapModule} = require('./helpers/js'); +const {isJsModule, wrapModule} = require('./helpers/js'); import type {GetTransformOptions} from '../../Bundler'; import type {ModuleTransportLike} from '../../shared/types.flow'; @@ -56,7 +57,7 @@ async function getRamBundleInfo( modules.forEach(module => options.createModuleId(module.path)); - const ramModules = modules.map(module => ({ + const ramModules = modules.filter(isJsModule).map(module => ({ id: options.createModuleId(module.path), code: wrapModule(module, options), map: fullSourceMapObject( @@ -68,8 +69,9 @@ async function getRamBundleInfo( ), name: path.basename(module.path), sourcePath: module.path, - source: module.output.source, - type: module.output.type, + source: module.getSource(), + type: nullthrows(module.output.find(({type}) => type.startsWith('js'))) + .type, })); const {preloadedModules, ramGroups} = await _getRamOptions( 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 a897e2a5..52ae8433 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/__tests__/js-test.js @@ -24,13 +24,18 @@ beforeEach(() => { ['bar', {absolutePath: '/bar', data: {isAsync: false, name: 'bar'}}], ['baz', {absolutePath: '/baz', data: {isAsync: false, name: 'baz'}}], ]), + getSource: () => '', inverseDependencies: new Set(), - output: { - code: '__d(function() { console.log("foo") });', - map: [], - source: '', - type: 'js/module', - }, + output: [ + { + data: { + code: '__d(function() { console.log("foo") });', + map: [], + }, + + type: 'js/module', + }, + ], }; }); @@ -54,14 +59,14 @@ describe('wrapModule()', () => { }); it('should not wrap a script', () => { - myModule.output.type = 'js/script'; + myModule.output[0].type = 'js/script'; expect( wrapModule(myModule, { createModuleId: createModuleIdFactory(), dev: true, }), - ).toEqual(myModule.output.code); + ).toEqual(myModule.output[0].data.code); }); it('should use custom createModuleId param', () => { diff --git a/packages/metro/src/DeltaBundler/Serializers/helpers/js.js b/packages/metro/src/DeltaBundler/Serializers/helpers/js.js index 043876e4..484abd80 100644 --- a/packages/metro/src/DeltaBundler/Serializers/helpers/js.js +++ b/packages/metro/src/DeltaBundler/Serializers/helpers/js.js @@ -11,6 +11,7 @@ 'use strict'; const addParamsToDefineCall = require('../../../lib/addParamsToDefineCall'); +const invariant = require('fbjs/lib/invariant'); const path = require('path'); import type {Module} from '../../traverseDependencies'; @@ -26,8 +27,9 @@ export type Options = { const PASS_MODULE_PATHS_TO_DEFINE = false; function wrapModule(module: Module, options: Options) { - if (module.output.type.startsWith('js/script')) { - return module.output.code; + const output = getJsOutput(module); + if (output.type.startsWith('js/script')) { + return output.data.code; } const moduleId = options.createModuleId(module.path); @@ -49,9 +51,28 @@ function wrapModule(module: Module, options: Options) { } } - return addParamsToDefineCall(module.output.code, ...params); + return addParamsToDefineCall(output.data.code, ...params); +} + +function getJsOutput(module: Module) { + const jsModules = module.output.filter(({type}) => type.startsWith('js/')); + + invariant( + jsModules.length === 1, + `Modules must have exactly one JS output, but ${module.path} has ${ + jsModules.length + } JS outputs.`, + ); + + return jsModules[0]; +} + +function isJsModule(module: Module) { + return module.output.some(output => output.type.startsWith('js/')); } module.exports = { + getJsOutput, + isJsModule, wrapModule, }; diff --git a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js index b58006b4..5ae73439 100644 --- a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js @@ -12,7 +12,7 @@ const addParamsToDefineCall = require('../../lib/addParamsToDefineCall'); -const {wrapModule} = require('./helpers/js'); +const {isJsModule, wrapModule} = require('./helpers/js'); import type {Delta, Graph} from '../../DeltaBundler'; import type {Module} from '../traverseDependencies'; @@ -34,7 +34,9 @@ function hmrJSBundle(delta: Delta, graph: Graph, options: Options): Result { const modules = []; for (const module of delta.modified.values()) { - modules.push(_prepareModule(module, graph, options)); + if (isJsModule(module)) { + modules.push(_prepareModule(module, graph, options)); + } } return { diff --git a/packages/metro/src/DeltaBundler/Serializers/plainJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/plainJSBundle.js index 242316eb..a11d2be4 100644 --- a/packages/metro/src/DeltaBundler/Serializers/plainJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/plainJSBundle.js @@ -12,7 +12,7 @@ const getAppendScripts = require('../../lib/getAppendScripts'); -const {wrapModule} = require('./helpers/js'); +const {isJsModule, wrapModule} = require('./helpers/js'); import type {Graph} from '../DeltaCalculator'; import type {Module} from '../traverseDependencies'; @@ -41,6 +41,7 @@ function plainJSBundle( ...graph.dependencies.values(), ...getAppendScripts(entryPoint, graph, options), ] + .filter(isJsModule) .map(module => wrapModule(module, options)) .join('\n'); } diff --git a/packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js b/packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js index 89d6b7e6..79efa1db 100644 --- a/packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js +++ b/packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js @@ -10,6 +10,7 @@ 'use strict'; +const {isJsModule, getJsOutput} = require('./helpers/js'); const {fromRawMappings} = require('metro-source-map'); import type {Graph} from '../DeltaCalculator'; @@ -21,12 +22,15 @@ function fullSourceMapObject( graph: Graph, options: {|+excludeSource: boolean|}, ): BabelSourceMap { - const modules = [...pre, ...graph.dependencies.values()].map(module => { - return { - ...module.output, - path: module.path, - }; - }); + const modules = [...pre, ...graph.dependencies.values()] + .filter(isJsModule) + .map(module => { + return { + ...getJsOutput(module).data, + path: module.path, + source: options.excludeSource ? '' : module.getSource(), + }; + }); return fromRawMappings(modules).toMap(undefined, { excludeSource: options.excludeSource, diff --git a/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js b/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js index dd92b7e6..13ff7614 100644 --- a/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js +++ b/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js @@ -10,6 +10,7 @@ 'use strict'; +const {isJsModule, getJsOutput} = require('./helpers/js'); const {fromRawMappings} = require('metro-source-map'); import type {Graph} from '../DeltaCalculator'; @@ -20,12 +21,15 @@ function fullSourceMap( graph: Graph, options: {|+excludeSource: boolean|}, ): string { - const modules = [...pre, ...graph.dependencies.values()].map(module => { - return { - ...module.output, - path: module.path, - }; - }); + const modules = [...pre, ...graph.dependencies.values()] + .filter(isJsModule) + .map(module => { + return { + ...getJsOutput(module).data, + path: module.path, + source: options.excludeSource ? '' : module.getSource(), + }; + }); return fromRawMappings(modules).toString(undefined, { excludeSource: options.excludeSource, diff --git a/packages/metro/src/DeltaBundler/__tests__/__snapshots__/traverseDependencies-test.js.snap b/packages/metro/src/DeltaBundler/__tests__/__snapshots__/traverseDependencies-test.js.snap index 070d8fbc..8a45718b 100644 --- a/packages/metro/src/DeltaBundler/__tests__/__snapshots__/traverseDependencies-test.js.snap +++ b/packages/metro/src/DeltaBundler/__tests__/__snapshots__/traverseDependencies-test.js.snap @@ -13,13 +13,17 @@ Object { }, }, }, + "getSource": [Function], "inverseDependencies": Set {}, - "output": Object { - "code": "// code", - "map": Array [], - "source": "// source", - "type": "js/module", - }, + "output": Array [ + Object { + "data": Object { + "code": "// code", + "map": Array [], + }, + "type": "js/module", + }, + ], "path": "/bundle", }, "/foo" => Object { @@ -39,41 +43,53 @@ Object { }, }, }, + "getSource": [Function], "inverseDependencies": Set { "/bundle", }, - "output": Object { - "code": "// code", - "map": Array [], - "source": "// source", - "type": "js/module", - }, + "output": Array [ + Object { + "data": Object { + "code": "// code", + "map": Array [], + }, + "type": "js/module", + }, + ], "path": "/foo", }, "/bar" => Object { "dependencies": Map {}, + "getSource": [Function], "inverseDependencies": Set { "/foo", }, - "output": Object { - "code": "// code", - "map": Array [], - "source": "// source", - "type": "js/module", - }, + "output": Array [ + Object { + "data": Object { + "code": "// code", + "map": Array [], + }, + "type": "js/module", + }, + ], "path": "/bar", }, "/baz" => Object { "dependencies": Map {}, + "getSource": [Function], "inverseDependencies": Set { "/foo", }, - "output": Object { - "code": "// code", - "map": Array [], - "source": "// source", - "type": "js/module", - }, + "output": Array [ + Object { + "data": Object { + "code": "// code", + "map": Array [], + }, + "type": "js/module", + }, + ], "path": "/baz", }, }, diff --git a/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-test.js b/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-test.js index aafdbc45..52736d98 100644 --- a/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-test.js +++ b/packages/metro/src/DeltaBundler/__tests__/traverseDependencies-test.js @@ -136,12 +136,16 @@ beforeEach(async () => { name: dep.name, isAsync: false, })), - output: { - code: '// code', - map: [], - source: '// source', - type: 'js/module', - }, + getSource: () => '// source', + output: [ + { + data: { + code: '// code', + map: [], + }, + type: 'js/module', + }, + ], }; }, onProgress: null, diff --git a/packages/metro/src/DeltaBundler/traverseDependencies.js b/packages/metro/src/DeltaBundler/traverseDependencies.js index e70a7806..28f08646 100644 --- a/packages/metro/src/DeltaBundler/traverseDependencies.js +++ b/packages/metro/src/DeltaBundler/traverseDependencies.js @@ -13,8 +13,6 @@ import type {TransformResultDependency} from '../ModuleGraph/types.flow'; import type {MetroSourceMapSegmentTuple} from 'metro-source-map'; -export type DependencyType = string; - export type Dependency = {| absolutePath: string, data: TransformResultDependency, @@ -23,13 +21,9 @@ export type Dependency = {| export type Module = {| dependencies: Map, inverseDependencies: Set, + output: TransformOutput, path: string, - output: { - +code: string, - +map: Array, - +source: string, - +type: DependencyType, - }, + getSource: () => string, |}; export type Graph = {| @@ -51,15 +45,19 @@ type Delta = { deleted: Set, }; -export type TransformFn = string => Promise<{ - dependencies: $ReadOnlyArray, - output: { +export type TransformOutput = $ReadOnlyArray<{| + +data: { +code: string, +map: Array, - +source: string, - +type: DependencyType, }, -}>; + +type: string, +|}>; + +export type TransformFn = string => Promise<{| + dependencies: $ReadOnlyArray, + output: TransformOutput, + +getSource: () => string, +|}>; export type Options = {| resolve: (from: string, to: string) => string, @@ -203,6 +201,7 @@ async function processModule( ); // Update the module information. + module.getSource = result.getSource; module.output = result.output; module.dependencies = new Map(); @@ -315,12 +314,8 @@ function createModule(filePath: string, graph: Graph): Module { dependencies: new Map(), inverseDependencies: new Set(), path: filePath, - output: { - code: '', - map: [], - source: '', - type: 'js/module', - }, + getSource: () => '', + output: [], }; graph.dependencies.set(filePath, module); diff --git a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js index 4062efb1..f2b79197 100644 --- a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js +++ b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js @@ -92,10 +92,14 @@ describe('HmrServer', () => { dependencies: new Map(), inverseDependencies: new Set(), path: '/hi', - output: { - code: '__d(function() { alert("hi"); });', - type: 'js/module', - }, + output: [ + { + type: 'js/module', + data: { + code: '__d(function() { alert("hi"); });', + }, + }, + ], }, ], ]), diff --git a/packages/metro/src/Server/__tests__/Server-test.js b/packages/metro/src/Server/__tests__/Server-test.js index a9a407d0..9f174a9b 100644 --- a/packages/metro/src/Server/__tests__/Server-test.js +++ b/packages/metro/src/Server/__tests__/Server-test.js @@ -108,12 +108,16 @@ describe('processRequest', () => { }, ], ]), - output: { - type: 'js/module', - code: '__d(function() {entry();});', - map: [], - source: 'code-mybundle', - }, + getSource: () => 'code-mybundle', + output: [ + { + type: 'js/module', + data: { + code: '__d(function() {entry();});', + map: [], + }, + }, + ], }, ], [ @@ -121,12 +125,16 @@ describe('processRequest', () => { { path: '/root/foo.js', dependencies: new Map(), - output: { - type: 'js/module', - code: '__d(function() {foo();});', - map: [], - source: 'code-foo', - }, + getSource: () => 'code-foo', + output: [ + { + type: 'js/module', + data: { + code: '__d(function() {foo();});', + map: [], + }, + }, + ], }, ], ]); @@ -160,12 +168,16 @@ describe('processRequest', () => { { path: 'require-js', dependencies: new Map(), - output: { - type: 'js/script', - code: 'function () {require();}', - map: [], - source: 'code-require', - }, + getSource: () => 'code-require', + output: [ + { + type: 'js/script', + data: { + code: 'function () {require();}', + map: [], + }, + }, + ], }, ]), ); @@ -461,11 +473,12 @@ describe('processRequest', () => { '/root/foo.js', { path: '/root/foo.js', - output: { - type: 'js/module', - code: '__d(function() {modified();});', - map: [], - }, + output: [ + { + type: 'js/module', + data: {code: '__d(function() {modified();});'}, + }, + ], dependencies: new Map(), }, ], @@ -506,11 +519,12 @@ describe('processRequest', () => { '/root/foo.js', { path: '/root/foo.js', - output: { - type: 'js/module', - code: '__d(function() {modified();});', - map: [], - }, + output: [ + { + type: 'js/module', + data: {code: '__d(function() {modified();});'}, + }, + ], dependencies: new Map(), }, ], diff --git a/packages/metro/src/lib/getAppendScripts.js b/packages/metro/src/lib/getAppendScripts.js index 33bec6f7..1638db65 100644 --- a/packages/metro/src/lib/getAppendScripts.js +++ b/packages/metro/src/lib/getAppendScripts.js @@ -36,13 +36,19 @@ function getAppendScripts( output.push({ path: `require-${path}`, dependencies: new Map(), + getSource: () => '', inverseDependencies: new Set(), - output: { - code: options.getRunModuleStatement(options.createModuleId(path)), - source: '', - map: [], - type: 'js/script/virtual', - }, + output: [ + { + type: 'js/script/virtual', + data: { + code: options.getRunModuleStatement( + options.createModuleId(path), + ), + map: [], + }, + }, + ], }); } } @@ -52,13 +58,17 @@ function getAppendScripts( output.push({ path: 'source-map', dependencies: new Map(), + getSource: () => '', inverseDependencies: new Set(), - output: { - code: `//# sourceMappingURL=${options.sourceMapUrl}`, - source: '', - map: [], - type: 'js/script/virtual', - }, + output: [ + { + type: 'js/script/virtual', + data: { + code: `//# sourceMappingURL=${options.sourceMapUrl}`, + map: [], + }, + }, + ], }); } diff --git a/packages/metro/src/lib/getPrependedScripts.js b/packages/metro/src/lib/getPrependedScripts.js index 7ed27ef8..678248bf 100644 --- a/packages/metro/src/lib/getPrependedScripts.js +++ b/packages/metro/src/lib/getPrependedScripts.js @@ -86,14 +86,18 @@ function _getPrelude({dev}: {dev: boolean}): Module { return { dependencies: new Map(), + getSource: () => code, inverseDependencies: new Set(), path: name, - output: { - code, - map: [], - source: code, - type: 'js/script/virtual', - }, + output: [ + { + type: 'js/script/virtual', + data: { + code, + map: [], + }, + }, + ], }; } diff --git a/packages/metro/src/lib/transformHelpers.js b/packages/metro/src/lib/transformHelpers.js index a633642e..a04cbd28 100644 --- a/packages/metro/src/lib/transformHelpers.js +++ b/packages/metro/src/lib/transformHelpers.js @@ -109,25 +109,19 @@ async function getTransformFn( ), }); - // eslint-disable-next-line lint/flow-no-fixme - // $FlowFixMe: "defineProperty" with a getter is buggy in flow. - const output = { - code: result.code, - map: result.map, - type: result.type, - }; - - // Lazily access source code; if not needed, don't read the file. - // eslint-disable-next-line lint/flow-no-fixme - // $FlowFixMe: "defineProperty" with a getter is buggy in flow. - Object.defineProperty(output, 'source', { - configurable: true, - enumerable: true, - get: () => result.source, - }); - return { - output, + getSource() { + return result.source; + }, + output: [ + { + data: { + code: result.code, + map: result.map, + }, + type: result.type, + }, + ], dependencies: result.dependencies, }; };