RN buck: ModuleGraph: get rid of callbacks, async/await for the best

Reviewed By: davidaurelio

Differential Revision: D5842855

fbshipit-source-id: 71f9e799db4ad312213a20c1b1a93280e934d1e7
This commit is contained in:
Jean Lauliac 2017-09-18 04:12:11 -07:00 committed by Facebook Github Bot
parent 539885386a
commit a7ad7502aa
2 changed files with 85 additions and 120 deletions

View File

@ -7,29 +7,25 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @flow
* @format
*/ */
'use strict'; 'use strict';
const defaults = require('../defaults'); const defaults = require('../defaults');
const nullthrows = require('fbjs/lib/nullthrows'); const denodeify: Denodeify = require('denodeify');
const parallel = require('async/parallel');
const seq = require('async/seq');
const virtualModule = require('./module').virtual; const virtualModule = require('./module').virtual;
import type { import type {
BuildResult, BuildResult,
Callback, Callback,
GraphFn, GraphFn,
GraphResult,
Module,
PostProcessModules, PostProcessModules,
} from './types.flow'; } from './types.flow';
type BuildFn = ( export type BuildFn = (
entryPoints: Iterable<string>, entryPoints: Iterable<string>,
options: BuildOptions, options: BuildOptions,
callback: Callback<BuildResult>, ) => Promise<BuildResult>;
) => void;
type BuildOptions = {| type BuildOptions = {|
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>, getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
@ -37,78 +33,50 @@ type BuildOptions = {|
platform: string, platform: string,
|}; |};
type Denodeify = <A, B, C, T>(
(A, B, C, Callback<T>) => void,
) => (A, B, C) => Promise<T>;
exports.createBuildSetup = ( exports.createBuildSetup = (
graph: GraphFn, graphFn: GraphFn,
postProcessModules: PostProcessModules, postProcessModules: PostProcessModules,
translateDefaultsPath: string => string = x => x, translateDefaultsPath: string => string = x => x,
): BuildFn => ): BuildFn => async (entryPoints, options) => {
(entryPoints, options, callback) => {
const { const {
getPolyfills = (({platform}) => []), getPolyfills = ({platform}) => [],
optimize = false, optimize = false,
platform = defaults.platforms[0], platform = defaults.platforms[0],
} = options; } = options;
const graphOptions = {optimize}; const graphOptions = {optimize};
const graphWithOptions = const pgraph = denodeify(graphFn);
(entry, cb) => graph(entry, platform, graphOptions, cb); const graphWithOptions = entry => pgraph(entry, platform, graphOptions);
const graphOnlyModules = seq(graphWithOptions, getModules); const graphOnlyModules = async m => (await graphWithOptions(m)).modules;
parallel({ const [graph, moduleSystem, polyfills] = await Promise.all([
graph: cb => graphWithOptions(entryPoints, (error, result) => { (async () => {
if (error) { const result = await graphWithOptions(entryPoints);
cb(error);
return;
}
/* $FlowFixMe: not undefined if there is no error */
const {modules, entryModules} = result; const {modules, entryModules} = result;
const prModules = postProcessModules(modules, [...entryPoints]); const prModules = postProcessModules(modules, [...entryPoints]);
cb(null, {modules: prModules, entryModules}); return {modules: prModules, entryModules};
}), })(),
moduleSystem: cb => graphOnlyModules( graphOnlyModules([translateDefaultsPath(defaults.moduleSystem)]),
[translateDefaultsPath(defaults.moduleSystem)], graphOnlyModules(getPolyfills({platform}).map(translateDefaultsPath)),
cb, ]);
),
polyfills: cb => graphOnlyModules(
getPolyfills({platform}).map(translateDefaultsPath),
cb,
),
}, (
error: ?Error,
result?: {graph: GraphResult, moduleSystem: Array<Module>, polyfills: Array<Module>},
) => {
if (error) {
callback(error);
return;
}
const {
graph: {modules, entryModules},
moduleSystem,
polyfills,
} = nullthrows(result);
const {entryModules} = graph;
const preludeScript = prelude(optimize); const preludeScript = prelude(optimize);
const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills]; const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills];
callback(null, { return {
entryModules, entryModules,
modules: prependedScripts.concat(modules), modules: [...prependedScripts, ...graph.modules],
prependedScripts, prependedScripts,
});
});
}; };
};
const getModules = (x, cb) => cb(null, x.modules);
function* concat<T>(...iterables: Array<Iterable<T>>): Iterable<T> {
for (const it of iterables) {
yield* it;
}
}
function prelude(optimize) { function prelude(optimize) {
return virtualModule( return virtualModule(
`var __DEV__=${String(!optimize)},` + `var __DEV__=${String(!optimize)},` +
'__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now();' '__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now();',
); );
} }

View File

@ -5,10 +5,15 @@
* This source code is licensed under the BSD-style license found in the * This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant * LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+javascript_foundation
* @format
*/ */
'use strict'; 'use strict';
const ModuleGraph = require('../ModuleGraph'); const ModuleGraph = require('../ModuleGraph');
const defaults = require('../../defaults'); const defaults = require('../../defaults');
const FILE_TYPE = 'module'; const FILE_TYPE = 'module';
@ -21,37 +26,34 @@ describe('build setup', () => {
const noOptions = {}; const noOptions = {};
const noEntryPoints = []; const noEntryPoints = [];
it('adds a prelude containing start time and `__DEV__` to the build', done => { it('adds a prelude containing start time and `__DEV__` to the build', async () => {
buildSetup(noEntryPoints, noOptions, (error, result) => { const result = await buildSetup(noEntryPoints, noOptions);
expect(error).toEqual(null);
const [prelude] = result.modules; const [prelude] = result.modules;
expect(prelude).toEqual({ expect(prelude).toEqual({
dependencies: [], dependencies: [],
file: { file: {
code: 'var __DEV__=true,__BUNDLE_START_TIME__=' + code:
'var __DEV__=true,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();', 'this.nativePerformanceNow?nativePerformanceNow():Date.now();',
map: null, map: null,
path: '', path: '',
type: 'script', type: 'script',
}, },
}); });
done();
});
}); });
it('sets `__DEV__` to false in the prelude if optimization is enabled', done => { it('sets `__DEV__` to false in the prelude if optimization is enabled', async () => {
buildSetup(noEntryPoints, {optimize: true}, (error, result) => { const result = await buildSetup(noEntryPoints, {optimize: true});
const [prelude] = result.modules; const [prelude] = result.modules;
expect(prelude.file.code) expect(prelude.file.code).toEqual(
.toEqual('var __DEV__=false,__BUNDLE_START_TIME__=' + 'var __DEV__=false,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();'); 'this.nativePerformanceNow?nativePerformanceNow():Date.now();',
done(); );
});
}); });
it('places the module system implementation directly after the prelude', done => { it('places the module system implementation directly after the prelude', async () => {
buildSetup(noEntryPoints, noOptions, (error, result) => { const result = await buildSetup(noEntryPoints, noOptions);
const [, moduleSystem] = result.modules; const [, moduleSystem] = result.modules;
expect(moduleSystem).toEqual({ expect(moduleSystem).toEqual({
dependencies: [], dependencies: [],
@ -61,26 +63,21 @@ describe('build setup', () => {
type: FILE_TYPE, type: FILE_TYPE,
}, },
}); });
done();
});
}); });
it('places polyfills after the module system', done => { it('places polyfills after the module system', async () => {
buildSetup(noEntryPoints, polyfillOptions, (error, result) => { const result = await buildSetup(noEntryPoints, polyfillOptions);
const list = polyfillOptions.getPolyfills(); const list = polyfillOptions.getPolyfills();
const polyfills = result.modules.slice(2, list.length + 2); const polyfills = result.modules.slice(2, list.length + 2);
expect(polyfills).toEqual(list.map(moduleFromPath)); expect(polyfills).toEqual(list.map(moduleFromPath));
done();
});
}); });
it('places all entry points and dependencies at the end, post-processed', done => { it('places all entry points and dependencies at the end, post-processed', async () => {
const entryPoints = ['b', 'c', 'd']; const entryPoints = ['b', 'c', 'd'];
buildSetup(entryPoints, noOptions, (error, result) => { const result = await buildSetup(entryPoints, noOptions);
expect(result.modules.slice(-4)) expect(result.modules.slice(-4)).toEqual(
.toEqual(['a', 'b', 'c', 'd'].map(moduleFromPath)); ['a', 'b', 'c', 'd'].map(moduleFromPath),
done(); );
});
}); });
}); });