Refactor production serializers to use directly the graph information

Reviewed By: mjesun

Differential Revision: D7158632

fbshipit-source-id: 91fad2e3ca617ea5f3b283e335c0d70edbb6ff3b
This commit is contained in:
Rafael Oleza 2018-03-19 10:04:55 -07:00 committed by Facebook Github Bot
parent 3ac0bb47d9
commit 6be6b0805d
14 changed files with 479 additions and 94 deletions

View File

@ -34,7 +34,7 @@ function withSourceMap(
function minify(inputCode: string, inputMap: ?BabelSourceMap) { function minify(inputCode: string, inputMap: ?BabelSourceMap) {
const result = uglify.minify(inputCode, { const result = uglify.minify(inputCode, {
mangle: {toplevel: true}, mangle: {toplevel: false},
output: { output: {
ascii_only: true, ascii_only: true,
quote_style: 3, quote_style: 3,
@ -44,7 +44,7 @@ function minify(inputCode: string, inputMap: ?BabelSourceMap) {
content: inputMap, content: inputMap,
includeSources: false, includeSources: false,
}, },
toplevel: true, toplevel: false,
compress: { compress: {
// reduce_funcs inlines single-use function, which cause perf regressions. // reduce_funcs inlines single-use function, which cause perf regressions.
reduce_funcs: false, reduce_funcs: false,

View File

@ -444,7 +444,7 @@ class DeltaTransformer extends EventEmitter {
const name = this._dependencyGraph.getHasteName(edge.path); const name = this._dependencyGraph.getHasteName(edge.path);
const wrappedCode = wrapModule(edge, { const wrappedCode = wrapModule(edge, {
createModuleIdFn: this._getModuleId, createModuleId: this._getModuleId,
dev: transformOptions.dev, dev: transformOptions.dev,
}); });

View File

@ -42,7 +42,7 @@ it('should serialize a very simple bundle', () => {
entryPoints: ['foo'], entryPoints: ['foo'],
}, },
{ {
createModuleIdFn: path => path, createModuleId: path => path,
dev: true, dev: true,
runBeforeMainModule: [], runBeforeMainModule: [],
runModule: true, runModule: true,
@ -70,7 +70,7 @@ it('should add runBeforeMainModule statements if found in the graph', () => {
entryPoints: ['foo'], entryPoints: ['foo'],
}, },
{ {
createModuleIdFn: path => path, createModuleId: path => path,
dev: true, dev: true,
runBeforeMainModule: ['bar', 'non-existant'], runBeforeMainModule: ['bar', 'non-existant'],
runModule: true, runModule: true,
@ -99,7 +99,7 @@ it('should handle numeric module ids', () => {
entryPoints: ['foo'], entryPoints: ['foo'],
}, },
{ {
createModuleIdFn: createModuleIdFactory(), createModuleId: createModuleIdFactory(),
dev: true, dev: true,
runBeforeMainModule: ['bar', 'non-existant'], runBeforeMainModule: ['bar', 'non-existant'],
runModule: true, runModule: true,

View File

@ -35,7 +35,7 @@ describe('wrapModule()', () => {
it('Should wrap a module in nondev mode', () => { it('Should wrap a module in nondev mode', () => {
expect( expect(
wrapModule(myModule, { wrapModule(myModule, {
createModuleIdFn: createModuleIdFactory(), createModuleId: createModuleIdFactory(),
dev: false, dev: false,
}), }),
).toEqual('__d(function() { console.log("foo") },0,[1,2]);'); ).toEqual('__d(function() { console.log("foo") },0,[1,2]);');
@ -44,7 +44,7 @@ describe('wrapModule()', () => {
it('Should wrap a module in dev mode', () => { it('Should wrap a module in dev mode', () => {
expect( expect(
wrapModule(myModule, { wrapModule(myModule, {
createModuleIdFn: createModuleIdFactory(), createModuleId: createModuleIdFactory(),
dev: true, dev: true,
}), }),
).toEqual('__d(function() { console.log("foo") },0,[1,2],"foo.js");'); ).toEqual('__d(function() { console.log("foo") },0,[1,2],"foo.js");');
@ -55,17 +55,17 @@ describe('wrapModule()', () => {
expect( expect(
wrapModule(myModule, { wrapModule(myModule, {
createModuleIdFn: createModuleIdFactory(), createModuleId: createModuleIdFactory(),
dev: true, dev: true,
}), }),
).toEqual(myModule.output.code); ).toEqual(myModule.output.code);
}); });
it('should use custom createModuleIdFn param', () => { it('should use custom createModuleId param', () => {
// Just use a createModuleIdFn that returns the same path. // Just use a createModuleId that returns the same path.
expect( expect(
wrapModule(myModule, { wrapModule(myModule, {
createModuleIdFn: path => path, createModuleId: path => path,
dev: false, dev: false,
}), }),
).toEqual( ).toEqual(

View File

@ -16,7 +16,7 @@ const path = require('path');
import type {DependencyEdge} from '../../traverseDependencies'; import type {DependencyEdge} from '../../traverseDependencies';
export type Options = { export type Options = {
+createModuleIdFn: string => number | string, +createModuleId: string => number | string,
+dev: boolean, +dev: boolean,
}; };
@ -25,10 +25,10 @@ function wrapModule(module: DependencyEdge, options: Options) {
return module.output.code; return module.output.code;
} }
const moduleId = options.createModuleIdFn(module.path); const moduleId = options.createModuleId(module.path);
const params = [ const params = [
moduleId, moduleId,
Array.from(module.dependencies.values()).map(options.createModuleIdFn), Array.from(module.dependencies.values()).map(options.createModuleId),
]; ];
// Add the module name as the last parameter (to make it easier to do // Add the module name as the last parameter (to make it easier to do

View File

@ -10,13 +10,15 @@
'use strict'; 'use strict';
const getAppendScripts = require('../../lib/getAppendScripts');
const {wrapModule} = require('./helpers/js'); const {wrapModule} = require('./helpers/js');
import type {Graph} from '../DeltaCalculator'; import type {Graph} from '../DeltaCalculator';
import type {DependencyEdge} from '../traverseDependencies'; import type {DependencyEdge} from '../traverseDependencies';
type Options = {| type Options = {|
createModuleIdFn: string => number | string, createModuleId: string => number | string,
+dev: boolean, +dev: boolean,
+runBeforeMainModule: $ReadOnlyArray<string>, +runBeforeMainModule: $ReadOnlyArray<string>,
+runModule: boolean, +runModule: boolean,
@ -29,35 +31,17 @@ function plainJSBundle(
graph: Graph, graph: Graph,
options: Options, options: Options,
): string { ): string {
const output = [];
for (const module of pre) {
output.push(wrapModule(module, options));
}
for (const module of graph.dependencies.values()) { for (const module of graph.dependencies.values()) {
output.push(wrapModule(module, options)); options.createModuleId(module.path);
} }
for (const path of options.runBeforeMainModule) { return [
if (graph.dependencies.has(path)) { ...pre,
output.push( ...graph.dependencies.values(),
`require(${JSON.stringify(options.createModuleIdFn(path))});`, ...getAppendScripts(entryPoint, graph, options),
); ]
} .map(module => wrapModule(module, options))
} .join('\n');
if (options.runModule && graph.dependencies.has(entryPoint)) {
output.push(
`require(${JSON.stringify(options.createModuleIdFn(entryPoint))});`,
);
}
if (options.sourceMapUrl) {
output.push(`//# sourceMappingURL=${options.sourceMapUrl}`);
}
return output.join('\n');
} }
module.exports = plainJSBundle; module.exports = plainJSBundle;

View File

@ -10,10 +10,16 @@
'use strict'; 'use strict';
const DeltaCalculator = require('./DeltaCalculator');
const DeltaTransformer = require('./DeltaTransformer'); const DeltaTransformer = require('./DeltaTransformer');
import type Bundler from '../Bundler'; import type Bundler from '../Bundler';
import type {BundleOptions} from '../shared/types.flow'; import type {BundleOptions} from '../shared/types.flow';
import type {
DeltaResult,
Graph as CalculatorGraph,
Options,
} from './DeltaCalculator';
import type {DeltaEntry} from './DeltaTransformer'; import type {DeltaEntry} from './DeltaTransformer';
export type PostProcessModules = ( export type PostProcessModules = (
@ -27,6 +33,9 @@ export type MainOptions = {|
postProcessModules?: PostProcessModules, postProcessModules?: PostProcessModules,
|}; |};
export type Delta = DeltaResult;
export type Graph = CalculatorGraph;
/** /**
* `DeltaBundler` uses the `DeltaTransformer` to build bundle deltas. This * `DeltaBundler` uses the `DeltaTransformer` to build bundle deltas. This
* module handles all the transformer instances so it can support multiple * module handles all the transformer instances so it can support multiple
@ -38,6 +47,7 @@ class DeltaBundler {
_options: MainOptions; _options: MainOptions;
_deltaTransformers: Map<string, DeltaTransformer> = new Map(); _deltaTransformers: Map<string, DeltaTransformer> = new Map();
_currentId: number = 0; _currentId: number = 0;
_deltaCalculators: Map<Graph, DeltaCalculator> = new Map();
constructor(bundler: Bundler, options: MainOptions) { constructor(bundler: Bundler, options: MainOptions) {
this._bundler = bundler; this._bundler = bundler;
@ -47,6 +57,9 @@ class DeltaBundler {
end() { end() {
this._deltaTransformers.forEach(DeltaTransformer => DeltaTransformer.end()); this._deltaTransformers.forEach(DeltaTransformer => DeltaTransformer.end());
this._deltaTransformers = new Map(); this._deltaTransformers = new Map();
this._deltaCalculators.forEach(deltaCalculator => deltaCalculator.end());
this._deltaCalculators = new Map();
} }
endTransformer(clientId: string) { endTransformer(clientId: string) {
@ -78,6 +91,55 @@ class DeltaBundler {
return deltaTransformer; return deltaTransformer;
} }
async buildGraph(options: Options): Promise<Graph> {
const depGraph = await this._bundler.getDependencyGraph();
const deltaCalculator = new DeltaCalculator(
this._bundler,
depGraph,
options,
);
await deltaCalculator.getDelta({reset: true});
const graph = deltaCalculator.getGraph();
this._deltaCalculators.set(graph, deltaCalculator);
return graph;
}
async getDelta(graph: Graph, {reset}: {reset: boolean}): Promise<Delta> {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
return await deltaCalculator.getDelta({reset});
}
listen(graph: Graph, callback: () => mixed) {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
deltaCalculator.on('change', callback);
}
endGraph(graph: Graph) {
const deltaCalculator = this._deltaCalculators.get(graph);
if (!deltaCalculator) {
throw new Error('Graph not found');
}
deltaCalculator.end();
this._deltaCalculators.delete(graph);
}
getPostProcessModulesFn( getPostProcessModulesFn(
entryPoint: string, entryPoint: string,
): (modules: $ReadOnlyArray<DeltaEntry>) => $ReadOnlyArray<DeltaEntry> { ): (modules: $ReadOnlyArray<DeltaEntry>) => $ReadOnlyArray<DeltaEntry> {

View File

@ -19,6 +19,7 @@ jest
})) }))
.mock('../../Bundler') .mock('../../Bundler')
.mock('../../Assets') .mock('../../Assets')
.mock('../../lib/getPrependedScripts')
.mock('../../node-haste/DependencyGraph') .mock('../../node-haste/DependencyGraph')
.mock('metro-core/src/Logger') .mock('metro-core/src/Logger')
.mock('../../lib/getAbsolutePath') .mock('../../lib/getAbsolutePath')
@ -29,6 +30,7 @@ describe('processRequest', () => {
let Bundler; let Bundler;
let Server; let Server;
let getAsset; let getAsset;
let getPrependedScripts;
let symbolicate; let symbolicate;
let Serializers; let Serializers;
let DeltaBundler; let DeltaBundler;
@ -40,6 +42,7 @@ describe('processRequest', () => {
Bundler = require('../../Bundler'); Bundler = require('../../Bundler');
Server = require('../'); Server = require('../');
getAsset = require('../../Assets').getAsset; getAsset = require('../../Assets').getAsset;
getPrependedScripts = require('../../lib/getPrependedScripts');
symbolicate = require('../symbolicate/symbolicate'); symbolicate = require('../symbolicate/symbolicate');
Serializers = require('../../DeltaBundler/Serializers/Serializers'); Serializers = require('../../DeltaBundler/Serializers/Serializers');
DeltaBundler = require('../../DeltaBundler'); DeltaBundler = require('../../DeltaBundler');
@ -85,6 +88,15 @@ describe('processRequest', () => {
let requestHandler; let requestHandler;
beforeEach(() => { beforeEach(() => {
DeltaBundler.prototype.buildGraph = jest.fn().mockReturnValue(
Promise.resolve({
entryPoints: [''],
dependencies: new Map(),
}),
);
getPrependedScripts.mockReturnValue(Promise.resolve([]));
Serializers.fullBundle.mockReturnValue( Serializers.fullBundle.mockReturnValue(
Promise.resolve({ Promise.resolve({
bundle: 'this is the source', bundle: 'this is the source',
@ -445,28 +457,16 @@ describe('processRequest', () => {
entryFile: 'foo file', entryFile: 'foo file',
}) })
.then(() => .then(() =>
expect(Serializers.fullBundle).toBeCalledWith( expect(DeltaBundler.prototype.buildGraph).toBeCalledWith({
expect.any(DeltaBundler), assetPlugins: [],
{ customTransformOptions: {},
assetPlugins: [], dev: true,
customTransformOptions: {}, entryPoints: ['/root/foo file'],
dev: true, hot: false,
entryFile: '/root/foo file', minify: false,
entryModuleOnly: false, onProgress: null,
excludeSource: false, platform: undefined,
hot: false, }),
inlineSourceMap: false,
isolateModuleIDs: false,
minify: false,
onProgress: null,
platform: undefined,
resolutionResponse: null,
runBeforeMainModule: ['InitializeCore'],
runModule: true,
sourceMapUrl: null,
unbundle: false,
},
),
); );
}); });
}); });

View File

@ -14,13 +14,19 @@ const Bundler = require('../Bundler');
const DeltaBundler = require('../DeltaBundler'); const DeltaBundler = require('../DeltaBundler');
const MultipartResponse = require('./MultipartResponse'); const MultipartResponse = require('./MultipartResponse');
const Serializers = require('../DeltaBundler/Serializers/Serializers'); const Serializers = require('../DeltaBundler/Serializers/Serializers');
const defaultCreateModuleIdFactory = require('../lib/createModuleIdFactory');
const plainJSBundle = require('../DeltaBundler/Serializers/plainJSBundle');
const sourceMapString = require('../DeltaBundler/Serializers/sourceMapString');
const debug = require('debug')('Metro:Server'); const debug = require('debug')('Metro:Server');
const defaults = require('../defaults'); const defaults = require('../defaults');
const formatBundlingError = require('../lib/formatBundlingError'); const formatBundlingError = require('../lib/formatBundlingError');
const getAbsolutePath = require('../lib/getAbsolutePath'); const getAbsolutePath = require('../lib/getAbsolutePath');
const getMaxWorkers = require('../lib/getMaxWorkers'); const getMaxWorkers = require('../lib/getMaxWorkers');
const getOrderedDependencyPaths = require('../lib/getOrderedDependencyPaths'); const getOrderedDependencyPaths = require('../lib/getOrderedDependencyPaths');
const getPrependedScripts = require('../lib/getPrependedScripts');
const mime = require('mime-types'); const mime = require('mime-types');
const mapGraph = require('../lib/mapGraph');
const nullthrows = require('fbjs/lib/nullthrows'); const nullthrows = require('fbjs/lib/nullthrows');
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions'); const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath'); const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath');
@ -32,6 +38,7 @@ const {getAsset} = require('../Assets');
const resolveSync: ResolveSync = require('resolve').sync; const resolveSync: ResolveSync = require('resolve').sync;
import type {CustomError} from '../lib/formatBundlingError'; import type {CustomError} from '../lib/formatBundlingError';
import type {DependencyEdge} from '../DeltaBundler/traverseDependencies';
import type {IncomingMessage, ServerResponse} from 'http'; import type {IncomingMessage, ServerResponse} from 'http';
import type {Reporter} from '../lib/reporting'; import type {Reporter} from '../lib/reporting';
import type { import type {
@ -73,7 +80,7 @@ class Server {
blacklistRE: void | RegExp, blacklistRE: void | RegExp,
cacheStores: $ReadOnlyArray<CacheStore<TransformedCode>>, cacheStores: $ReadOnlyArray<CacheStore<TransformedCode>>,
cacheVersion: string, cacheVersion: string,
createModuleIdFactory?: () => (path: string) => number, createModuleId: (path: string) => number,
enableBabelRCLookup: boolean, enableBabelRCLookup: boolean,
extraNodeModules: {}, extraNodeModules: {},
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>, getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
@ -119,6 +126,9 @@ class Server {
const assetExts = options.assetExts || defaults.assetExts; const assetExts = options.assetExts || defaults.assetExts;
const sourceExts = options.sourceExts || defaults.sourceExts; const sourceExts = options.sourceExts || defaults.sourceExts;
const _createModuleId =
options.createModuleId || defaultCreateModuleIdFactory();
this._opts = { this._opts = {
assetExts: options.assetTransforms ? [] : assetExts, assetExts: options.assetTransforms ? [] : assetExts,
assetRegistryPath: options.assetRegistryPath, assetRegistryPath: options.assetRegistryPath,
@ -126,7 +136,7 @@ class Server {
cacheStores: options.cacheStores, cacheStores: options.cacheStores,
cacheVersion: options.cacheVersion, cacheVersion: options.cacheVersion,
dynamicDepsInPackages: options.dynamicDepsInPackages || 'throwAtRuntime', dynamicDepsInPackages: options.dynamicDepsInPackages || 'throwAtRuntime',
createModuleIdFactory: options.createModuleIdFactory, createModuleId: _createModuleId,
enableBabelRCLookup: enableBabelRCLookup:
options.enableBabelRCLookup != null options.enableBabelRCLookup != null
? options.enableBabelRCLookup ? options.enableBabelRCLookup
@ -175,7 +185,7 @@ class Server {
// This slices out options that are not part of the strict BundlerOptions // This slices out options that are not part of the strict BundlerOptions
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
const { const {
createModuleIdFactory, createModuleId,
getModulesRunBeforeMainModule, getModulesRunBeforeMainModule,
moduleFormat, moduleFormat,
silent, silent,
@ -230,27 +240,48 @@ class Server {
} }
async build(options: BundleOptions): Promise<{code: string, map: string}> { async build(options: BundleOptions): Promise<{code: string, map: string}> {
options = { const entryPoint = getAbsolutePath(
...options, options.entryFile,
runBeforeMainModule: this._opts.getModulesRunBeforeMainModule( this._opts.projectRoots,
options.entryFile,
),
entryFile: getAbsolutePath(options.entryFile, this._opts.projectRoots),
};
const fullBundle = await Serializers.fullBundle(
this._deltaBundler,
options,
); );
const fullMap = await Serializers.fullSourceMap( let graph = await this._deltaBundler.buildGraph({
this._deltaBundler, assetPlugins: options.assetPlugins,
customTransformOptions: options.customTransformOptions,
dev: options.dev,
entryPoints: [entryPoint],
hot: options.hot,
minify: options.minify,
onProgress: options.onProgress,
platform: options.platform,
});
let prependScripts = await getPrependedScripts(
this._opts,
options, options,
this._bundler,
); );
if (options.minify) {
prependScripts = await Promise.all(
prependScripts.map(script => this._minifyModule(script)),
);
graph = await mapGraph(graph, module => this._minifyModule(module));
}
return { return {
code: fullBundle.bundle, code: plainJSBundle(entryPoint, prependScripts, graph, {
map: fullMap, createModuleId: this._opts.createModuleId,
dev: options.dev,
runBeforeMainModule: this._opts.getModulesRunBeforeMainModule(
options.entryFile,
),
runModule: options.runModule,
sourceMapUrl: options.sourceMapUrl,
}),
map: sourceMapString(prependScripts, graph, {
excludeSource: options.excludeSource,
}),
}; };
} }
@ -299,6 +330,24 @@ class Server {
return await getOrderedDependencyPaths(this._deltaBundler, bundleOptions); return await getOrderedDependencyPaths(this._deltaBundler, bundleOptions);
} }
async _minifyModule(module: DependencyEdge): Promise<DependencyEdge> {
const {code, map} = await this._bundler.minifyModule(
module.path,
module.output.code,
module.output.map,
);
// $FlowIssue #16581373 spread of an exact object should be exact
return {
...module,
output: {
...module.output,
code,
map,
},
};
}
onFileChange(type: string, filePath: string) { onFileChange(type: string, filePath: string) {
Promise.all( Promise.all(
this._fileChangeListeners.map(listener => listener(filePath)), this._fileChangeListeners.map(listener => listener(filePath)),

View File

@ -149,7 +149,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
Foo: Foo, Foo: Foo,
Bar: Bar Bar: Bar
}; };
},4,[5,6]); },0,[1,2]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
@ -159,7 +159,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
type: 'bar', type: 'bar',
foo: Foo.type foo: Foo.type
}; };
},5,[6]); },1,[2]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
@ -169,7 +169,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
type: 'foo', type: 'foo',
asset: asset asset: asset
}; };
},6,[7]); },2,[3]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
module.exports = _require(_dependencyMap[0]).registerAsset({ module.exports = _require(_dependencyMap[0]).registerAsset({
\\"__packager_asset\\": true, \\"__packager_asset\\": true,
@ -181,11 +181,11 @@ __d(function (global, _require, module, exports, _dependencyMap) {
\\"name\\": \\"test\\", \\"name\\": \\"test\\",
\\"type\\": \\"png\\" \\"type\\": \\"png\\"
}); });
},7,[8]); },3,[4]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
},8,[]); },4,[]);
require(4);" require(0);"
`; `;
exports[`basic_bundle bundles package without polyfills 1`] = ` exports[`basic_bundle bundles package without polyfills 1`] = `
@ -323,7 +323,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
Foo: Foo, Foo: Foo,
Bar: Bar Bar: Bar
}; };
},2,[3,4]); },0,[1,2]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
@ -333,7 +333,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
type: 'bar', type: 'bar',
foo: Foo.type foo: Foo.type
}; };
},3,[4]); },1,[2]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
@ -343,7 +343,7 @@ __d(function (global, _require, module, exports, _dependencyMap) {
type: 'foo', type: 'foo',
asset: asset asset: asset
}; };
},4,[5]); },2,[3]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
module.exports = _require(_dependencyMap[0]).registerAsset({ module.exports = _require(_dependencyMap[0]).registerAsset({
\\"__packager_asset\\": true, \\"__packager_asset\\": true,
@ -355,9 +355,9 @@ __d(function (global, _require, module, exports, _dependencyMap) {
\\"name\\": \\"test\\", \\"name\\": \\"test\\",
\\"type\\": \\"png\\" \\"type\\": \\"png\\"
}); });
},5,[6]); },3,[4]);
__d(function (global, _require, module, exports, _dependencyMap) { __d(function (global, _require, module, exports, _dependencyMap) {
'use strict'; 'use strict';
},6,[]); },4,[]);
require(2);" require(0);"
`; `;

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';
const mapGraph = require('../mapGraph');
let graph;
beforeEach(() => {
graph = {
dependencies: new Map([
['/entryPoint', {name: 'entryPoint', id: '1'}],
['/foo', {name: 'foo', id: '2'}],
['/baz', {name: 'baz', id: '3'}],
]),
entryPoints: ['/entryPoint'],
};
});
it('should map the passed graph when a sync function is passed', async () => {
const mapped = await mapGraph(graph, element => ({
name: '-' + element.name + '-',
id: parseInt(element.id, 10),
}));
expect(mapped.dependencies).toEqual(
new Map([
['/entryPoint', {name: '-entryPoint-', id: 1}],
['/foo', {name: '-foo-', id: 2}],
['/baz', {name: '-baz-', id: 3}],
]),
);
expect(mapped.entryPoints).toEqual(['/entryPoint']);
});
it('should map the passed graph when an async function is passed', async () => {
const mapped = await mapGraph(graph, async element => ({
name: '-' + element.name + '-',
id: parseInt(element.id, 10),
}));
expect(mapped.dependencies).toEqual(
new Map([
['/entryPoint', {name: '-entryPoint-', id: 1}],
['/foo', {name: '-foo-', id: 2}],
['/baz', {name: '-baz-', id: 3}],
]),
);
expect(mapped.entryPoints).toEqual(['/entryPoint']);
});

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {Graph} from '../DeltaBundler/DeltaCalculator';
import type {DependencyEdge} from '../DeltaBundler/traverseDependencies';
type Options = {
+createModuleId: string => number | string,
+runBeforeMainModule: $ReadOnlyArray<string>,
+runModule: boolean,
+sourceMapUrl: ?string,
};
function getAppendScripts(
entryPoint: string,
graph: Graph,
options: Options,
): $ReadOnlyArray<DependencyEdge> {
const output = [];
if (options.runModule) {
const paths = [...options.runBeforeMainModule, entryPoint];
for (const path of paths) {
if (graph.dependencies.has(path)) {
output.push({
path: `require-${path}`,
dependencies: new Map(),
inverseDependencies: new Set(),
output: {
code: `require(${JSON.stringify(options.createModuleId(path))});`,
source: '',
map: [],
type: 'script',
},
});
}
}
}
if (options.sourceMapUrl) {
output.push({
path: 'source-map',
dependencies: new Map(),
inverseDependencies: new Set(),
output: {
code: `//# sourceMappingURL=${options.sourceMapUrl}`,
source: '',
map: [],
type: 'script',
},
});
}
return output;
}
module.exports = getAppendScripts;

View File

@ -0,0 +1,125 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const defaults = require('../defaults');
const getPreludeCode = require('./getPreludeCode');
import type Bundler from '../Bundler';
import type {DependencyEdge} from '../DeltaBundler/traverseDependencies';
import type Module from '../node-haste/Module';
type Options = {
enableBabelRCLookup: boolean,
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
polyfillModuleNames: Array<string>,
projectRoots: $ReadOnlyArray<string>,
};
type BundleOptions = {
+dev: boolean,
+hot: boolean,
+platform: ?string,
};
async function getPrependedScripts(
options: Options,
bundleOptions: BundleOptions,
bundler: Bundler,
): Promise<Array<DependencyEdge>> {
// Get all the polyfills from the relevant option params (the
// `getPolyfills()` method and the `polyfillModuleNames` variable).
const polyfillModuleNames = options
.getPolyfills({
platform: bundleOptions.platform,
})
.concat(options.polyfillModuleNames);
const dependencyGraph = await bundler.getDependencyGraph();
// Build the module system dependencies (scripts that need to
// be included at the very beginning of the bundle) + any polifyll.
const modules = [defaults.moduleSystem]
.concat(polyfillModuleNames)
.map(polyfillModuleName =>
dependencyGraph.createPolyfill({
file: polyfillModuleName,
}),
);
const transformOptions = {
dev: bundleOptions.dev,
enableBabelRCLookup: options.enableBabelRCLookup,
hot: bundleOptions.hot,
projectRoot: options.projectRoots[0],
};
const out = await Promise.all(
modules.map(module => _createEdgeFromScript(module, transformOptions)),
);
out.unshift(_getPrelude({dev: bundleOptions.dev}));
return out;
}
function _getPrelude({dev}: {dev: boolean}): DependencyEdge {
const code = getPreludeCode({isDev: dev});
const name = '__prelude__';
return {
dependencies: new Map(),
inverseDependencies: new Set(),
path: name,
output: {
code,
map: [],
source: code,
type: 'script',
},
};
}
async function _createEdgeFromScript(
module: Module,
options: {
dev: boolean,
enableBabelRCLookup: boolean,
hot: boolean,
projectRoot: string,
},
): Promise<DependencyEdge> {
const result = await module.read({
assetDataPlugins: [],
customTransformOptions: {},
dev: options.dev,
enableBabelRCLookup: options.enableBabelRCLookup,
hot: options.hot,
inlineRequires: false,
minify: false,
platform: undefined,
projectRoot: options.projectRoot,
});
return {
dependencies: new Map(),
inverseDependencies: new Set(),
path: module.path,
output: {
code: result.code,
map: result.map,
source: result.source,
type: 'script',
},
};
}
module.exports = getPrependedScripts;

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {DependencyEdge} from '../DeltaBundler/traverseDependencies';
import type {Graph} from '../DeltaBundler';
/**
* Generates a new Graph object, which has all the dependencies returned by the
* mapping function (similar to Array.prototype.map).
**/
async function mapGraph(
graph: Graph,
mappingFn: DependencyEdge => Promise<DependencyEdge>,
): Promise<Graph> {
const dependencies = new Map(
await Promise.all(
Array.from(graph.dependencies.entries()).map(async ([path, module]) => {
const mutated = await mappingFn(module);
return [path, mutated];
}),
),
);
return {
dependencies,
entryPoints: graph.entryPoints,
};
}
module.exports = mapGraph;