Use Graph traversal logic for building polyfills

Reviewed By: mjesun

Differential Revision: D7240946

fbshipit-source-id: 5af729878cdf31831b15867c296cf89025ab6f58
This commit is contained in:
Rafael Oleza 2018-03-19 10:04:57 -07:00 committed by Facebook Github Bot
parent 6be6b0805d
commit d8eed45c37
10 changed files with 94 additions and 94 deletions

View File

@ -42,6 +42,7 @@ export type Options = {
+minify: boolean, +minify: boolean,
+onProgress: ?(doneCont: number, totalCount: number) => mixed, +onProgress: ?(doneCont: number, totalCount: number) => mixed,
+platform: ?string, +platform: ?string,
+type: 'module' | 'script',
}; };
/** /**
@ -199,6 +200,17 @@ class DeltaCalculator extends EventEmitter {
projectRoot, projectRoot,
}; };
// When we're processing scripts, we don't need to calculate any
// inlineRequires information, since scripts by definition don't have
// requires().
if (this._options.type === 'script') {
// $FlowIssue #23854098 - Object.assign() loses the strictness of an object in flow
return {
...transformOptionsForBlacklist,
inlineRequires: false,
};
}
const { const {
inlineRequires, inlineRequires,
} = await this._bundler.getTransformOptionsForEntryFiles( } = await this._bundler.getTransformOptionsForEntryFiles(
@ -211,14 +223,14 @@ class DeltaCalculator extends EventEmitter {
entryPoints: [path], entryPoints: [path],
}, },
this._dependencyGraph, this._dependencyGraph,
transformOptionsForBlacklist, {...transformOptionsForBlacklist, type: this._options.type},
); );
return Array.from(added.keys()); return Array.from(added.keys());
}, },
); );
// $FlowFixMe flow does not recognize well Object.assign() return types. // $FlowIssue #23854098 - Object.assign() loses the strictness of an object in flow
return { return {
...transformOptionsForBlacklist, ...transformOptionsForBlacklist,
inlineRequires: inlineRequires || false, inlineRequires: inlineRequires || false,
@ -267,7 +279,10 @@ class DeltaCalculator extends EventEmitter {
modifiedFiles: Set<string>, modifiedFiles: Set<string>,
deletedFiles: Set<string>, deletedFiles: Set<string>,
): Promise<DeltaResult> { ): Promise<DeltaResult> {
const transformerOptions = await this.getTransformerOptions(); const transformerOptions = {
...(await this.getTransformerOptions()),
type: this._options.type,
};
if (!this._graph.dependencies.size) { if (!this._graph.dependencies.size) {
const {added} = await initialTraverseDependencies( const {added} = await initialTraverseDependencies(

View File

@ -126,6 +126,7 @@ class DeltaTransformer extends EventEmitter {
const deltaCalculator = new DeltaCalculator(bundler, dependencyGraph, { const deltaCalculator = new DeltaCalculator(bundler, dependencyGraph, {
...bundleOptions, ...bundleOptions,
entryPoints: [bundleOptions.entryFile], entryPoints: [bundleOptions.entryFile],
type: 'module',
}); });
return new DeltaTransformer( return new DeltaTransformer(

View File

@ -52,6 +52,11 @@ type Delta = {
deleted: Set<string>, deleted: Set<string>,
}; };
export type TransformOptions = {|
...JSTransformerOptions,
type: 'module' | 'script',
|};
/** /**
* Dependency Traversal logic for the Delta Bundler. This method calculates * Dependency Traversal logic for the Delta Bundler. This method calculates
* the modules that should be included in the bundle by traversing the * the modules that should be included in the bundle by traversing the
@ -69,7 +74,7 @@ type Delta = {
async function traverseDependencies( async function traverseDependencies(
paths: $ReadOnlyArray<string>, paths: $ReadOnlyArray<string>,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
graph: Graph, graph: Graph,
onProgress?: (numProcessed: number, total: number) => mixed = () => {}, onProgress?: (numProcessed: number, total: number) => mixed = () => {},
): Promise<Result> { ): Promise<Result> {
@ -136,11 +141,17 @@ async function traverseDependencies(
async function initialTraverseDependencies( async function initialTraverseDependencies(
graph: Graph, graph: Graph,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
onProgress?: (numProcessed: number, total: number) => mixed = () => {}, onProgress?: (numProcessed: number, total: number) => mixed = () => {},
): Promise<Result> { ): Promise<Result> {
graph.entryPoints.forEach(entryPoint => graph.entryPoints.forEach(entryPoint =>
createEdge(dependencyGraph.getModuleForPath(entryPoint), graph), createEdge(
dependencyGraph.getModuleForPath(
entryPoint,
transformOptions.type === 'script',
),
graph,
),
); );
await traverseDependencies( await traverseDependencies(
@ -162,7 +173,7 @@ async function initialTraverseDependencies(
async function traverseDependenciesForSingleFile( async function traverseDependenciesForSingleFile(
edge: DependencyEdge, edge: DependencyEdge,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
graph: Graph, graph: Graph,
delta: Delta, delta: Delta,
onProgress?: (numProcessed: number, total: number) => mixed = () => {}, onProgress?: (numProcessed: number, total: number) => mixed = () => {},
@ -194,7 +205,7 @@ async function traverseDependenciesForSingleFile(
async function processEdge( async function processEdge(
edge: DependencyEdge, edge: DependencyEdge,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
graph: Graph, graph: Graph,
delta: Delta, delta: Delta,
onDependencyAdd: () => mixed, onDependencyAdd: () => mixed,
@ -202,11 +213,12 @@ async function processEdge(
): Promise<void> { ): Promise<void> {
const previousDependencies = edge.dependencies; const previousDependencies = edge.dependencies;
const result = await dependencyGraph const {type, ...workerTransformOptions} = transformOptions;
.getModuleForPath(edge.path)
.read( const module = dependencyGraph.getModuleForPath(edge.path, type === 'script');
removeInlineRequiresBlacklistFromOptions(edge.path, transformOptions), const result = await module.read(
); removeInlineRequiresBlacklistFromOptions(edge.path, workerTransformOptions),
);
// Get the absolute path of all sub-dependencies (some of them could have been // Get the absolute path of all sub-dependencies (some of them could have been
// moved but maintain the same relative path). // moved but maintain the same relative path).
@ -262,7 +274,7 @@ async function addDependency(
parentEdge: DependencyEdge, parentEdge: DependencyEdge,
path: string, path: string,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
graph: Graph, graph: Graph,
delta: Delta, delta: Delta,
onDependencyAdd: () => mixed, onDependencyAdd: () => mixed,
@ -277,7 +289,10 @@ async function addDependency(
return; return;
} }
const edge = createEdge(dependencyGraph.getModuleForPath(path), graph); const edge = createEdge(
dependencyGraph.getModuleForPath(path, transformOptions.type === 'script'),
graph,
);
edge.inverseDependencies.add(parentEdge.path); edge.inverseDependencies.add(parentEdge.path);
delta.added.set(edge.path, edge); delta.added.set(edge.path, edge);
@ -365,11 +380,14 @@ function destroyEdge(edge: DependencyEdge, graph: Graph) {
function resolveDependencies( function resolveDependencies(
parentPath, parentPath,
dependencies: Array<string>, dependencies: $ReadOnlyArray<string>,
dependencyGraph: DependencyGraph, dependencyGraph: DependencyGraph,
transformOptions: JSTransformerOptions, transformOptions: TransformOptions,
): Map<string, string> { ): Map<string, string> {
const parentModule = dependencyGraph.getModuleForPath(parentPath); const parentModule = dependencyGraph.getModuleForPath(
parentPath,
transformOptions.type === 'string',
);
return new Map( return new Map(
dependencies.map(relativePath => [ dependencies.map(relativePath => [

View File

@ -466,6 +466,7 @@ describe('processRequest', () => {
minify: false, minify: false,
onProgress: null, onProgress: null,
platform: undefined, platform: undefined,
type: 'module',
}), }),
); );
}); });

View File

@ -245,7 +245,7 @@ class Server {
this._opts.projectRoots, this._opts.projectRoots,
); );
let graph = await this._deltaBundler.buildGraph({ const crawlingOptions = {
assetPlugins: options.assetPlugins, assetPlugins: options.assetPlugins,
customTransformOptions: options.customTransformOptions, customTransformOptions: options.customTransformOptions,
dev: options.dev, dev: options.dev,
@ -254,11 +254,14 @@ class Server {
minify: options.minify, minify: options.minify,
onProgress: options.onProgress, onProgress: options.onProgress,
platform: options.platform, platform: options.platform,
}); type: 'module',
};
let graph = await this._deltaBundler.buildGraph(crawlingOptions);
let prependScripts = await getPrependedScripts( let prependScripts = await getPrependedScripts(
this._opts, this._opts,
options, crawlingOptions,
this._bundler, this._deltaBundler,
); );
if (options.minify) { if (options.minify) {
@ -973,6 +976,7 @@ class Server {
runBeforeMainModule: [], runBeforeMainModule: [],
runModule: true, runModule: true,
sourceMapUrl: null, sourceMapUrl: null,
type: 'script',
unbundle: false, unbundle: false,
}; };
} }

View File

@ -43,6 +43,7 @@ async function getTransformOptions(): Promise<JSTransformerOptions> {
hot: true, hot: true,
minify: false, minify: false,
platform: 'ios', platform: 'ios',
type: 'module',
}; };
const deltaCalculator = new DeltaCalculator( const deltaCalculator = new DeltaCalculator(

View File

@ -13,27 +13,27 @@
const defaults = require('../defaults'); const defaults = require('../defaults');
const getPreludeCode = require('./getPreludeCode'); const getPreludeCode = require('./getPreludeCode');
import type Bundler from '../Bundler';
import type {DependencyEdge} from '../DeltaBundler/traverseDependencies'; import type {DependencyEdge} from '../DeltaBundler/traverseDependencies';
import type Module from '../node-haste/Module'; import type DeltaBundler from '../DeltaBundler';
import type {CustomTransformOptions} from '../JSTransformer/worker';
type Options = { type Options = {
enableBabelRCLookup: boolean,
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>, getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
polyfillModuleNames: Array<string>, polyfillModuleNames: Array<string>,
projectRoots: $ReadOnlyArray<string>,
}; };
type BundleOptions = { type BundleOptions = {
customTransformOptions: CustomTransformOptions,
+dev: boolean, +dev: boolean,
+hot: boolean, +hot: boolean,
+minify: boolean,
+platform: ?string, +platform: ?string,
}; };
async function getPrependedScripts( async function getPrependedScripts(
options: Options, options: Options,
bundleOptions: BundleOptions, bundleOptions: BundleOptions,
bundler: Bundler, deltaBundler: DeltaBundler,
): Promise<Array<DependencyEdge>> { ): Promise<Array<DependencyEdge>> {
// Get all the polyfills from the relevant option params (the // Get all the polyfills from the relevant option params (the
// `getPolyfills()` method and the `polyfillModuleNames` variable). // `getPolyfills()` method and the `polyfillModuleNames` variable).
@ -43,32 +43,22 @@ async function getPrependedScripts(
}) })
.concat(options.polyfillModuleNames); .concat(options.polyfillModuleNames);
const dependencyGraph = await bundler.getDependencyGraph(); const graph = await deltaBundler.buildGraph({
assetPlugins: [],
// Build the module system dependencies (scripts that need to customTransformOptions: bundleOptions.customTransformOptions,
// 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, dev: bundleOptions.dev,
enableBabelRCLookup: options.enableBabelRCLookup, entryPoints: [defaults.moduleSystem, ...polyfillModuleNames],
hot: bundleOptions.hot, hot: bundleOptions.hot,
projectRoot: options.projectRoots[0], minify: bundleOptions.minify,
}; onProgress: null,
platform: bundleOptions.platform,
type: 'script',
});
const out = await Promise.all( return [
modules.map(module => _createEdgeFromScript(module, transformOptions)), _getPrelude({dev: bundleOptions.dev}),
); ...graph.dependencies.values(),
];
out.unshift(_getPrelude({dev: bundleOptions.dev}));
return out;
} }
function _getPrelude({dev}: {dev: boolean}): DependencyEdge { function _getPrelude({dev}: {dev: boolean}): DependencyEdge {
@ -88,38 +78,4 @@ function _getPrelude({dev}: {dev: boolean}): DependencyEdge {
}; };
} }
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; module.exports = getPrependedScripts;

View File

@ -238,7 +238,11 @@ class DependencyGraph extends EventEmitter {
this._haste.end(); this._haste.end();
} }
getModuleForPath(entryFile: string) { getModuleForPath(entryFile: string, isPolyfill: boolean): Module {
if (isPolyfill) {
return this._moduleCache.getPolyfillModule(entryFile);
}
if (this._helpers.isAssetFile(entryFile)) { if (this._helpers.isAssetFile(entryFile)) {
return this._moduleCache.getAssetModule(entryFile); return this._moduleCache.getAssetModule(entryFile);
} }

View File

@ -87,7 +87,7 @@ class ModuleCache {
this._roots = roots; this._roots = roots;
} }
getModule(filePath: string): Module { getModule(filePath: string) {
if (!this._moduleCache[filePath]) { if (!this._moduleCache[filePath]) {
this._moduleCache[filePath] = new Module({ this._moduleCache[filePath] = new Module({
depGraphHelpers: this._depGraphHelpers, depGraphHelpers: this._depGraphHelpers,
@ -129,6 +129,14 @@ class ModuleCache {
return this._moduleCache[filePath]; return this._moduleCache[filePath];
} }
getPolyfillModule(filePath: string) {
if (!this._moduleCache[filePath]) {
this._moduleCache[filePath] = this.createPolyfill({file: filePath});
}
return this._moduleCache[filePath];
}
getPackage(filePath: string): Package { getPackage(filePath: string): Package {
if (!this._packageCache[filePath]) { if (!this._packageCache[filePath]) {
this._packageCache[filePath] = new Package({ this._packageCache[filePath] = new Package({

View File

@ -119,14 +119,6 @@ type Params = {
}; };
function transform({filename, options, src, plugins}: Params) { function transform({filename, options, src, plugins}: Params) {
options = options || {
assetDataPlugins: [],
platform: '',
projectRoot: '',
inlineRequires: false,
minify: false,
};
const OLD_BABEL_ENV = process.env.BABEL_ENV; const OLD_BABEL_ENV = process.env.BABEL_ENV;
process.env.BABEL_ENV = options.dev ? 'development' : 'production'; process.env.BABEL_ENV = options.dev ? 'development' : 'production';