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.
*
* @flow
* @format
*/
'use strict';
const defaults = require('../defaults');
const nullthrows = require('fbjs/lib/nullthrows');
const parallel = require('async/parallel');
const seq = require('async/seq');
const denodeify: Denodeify = require('denodeify');
const virtualModule = require('./module').virtual;
import type {
BuildResult,
Callback,
GraphFn,
GraphResult,
Module,
PostProcessModules,
} from './types.flow';
type BuildFn = (
export type BuildFn = (
entryPoints: Iterable<string>,
options: BuildOptions,
callback: Callback<BuildResult>,
) => void;
) => Promise<BuildResult>;
type BuildOptions = {|
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
@ -37,78 +33,50 @@ type BuildOptions = {|
platform: string,
|};
type Denodeify = <A, B, C, T>(
(A, B, C, Callback<T>) => void,
) => (A, B, C) => Promise<T>;
exports.createBuildSetup = (
graph: GraphFn,
graphFn: GraphFn,
postProcessModules: PostProcessModules,
translateDefaultsPath: string => string = x => x,
): BuildFn =>
(entryPoints, options, callback) => {
const {
getPolyfills = (({platform}) => []),
optimize = false,
platform = defaults.platforms[0],
} = options;
const graphOptions = {optimize};
): BuildFn => async (entryPoints, options) => {
const {
getPolyfills = ({platform}) => [],
optimize = false,
platform = defaults.platforms[0],
} = options;
const graphOptions = {optimize};
const graphWithOptions =
(entry, cb) => graph(entry, platform, graphOptions, cb);
const graphOnlyModules = seq(graphWithOptions, getModules);
const pgraph = denodeify(graphFn);
const graphWithOptions = entry => pgraph(entry, platform, graphOptions);
const graphOnlyModules = async m => (await graphWithOptions(m)).modules;
parallel({
graph: cb => graphWithOptions(entryPoints, (error, result) => {
if (error) {
cb(error);
return;
}
/* $FlowFixMe: not undefined if there is no error */
const {modules, entryModules} = result;
const prModules = postProcessModules(modules, [...entryPoints]);
cb(null, {modules: prModules, entryModules});
}),
moduleSystem: cb => graphOnlyModules(
[translateDefaultsPath(defaults.moduleSystem)],
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, moduleSystem, polyfills] = await Promise.all([
(async () => {
const result = await graphWithOptions(entryPoints);
const {modules, entryModules} = result;
const prModules = postProcessModules(modules, [...entryPoints]);
return {modules: prModules, entryModules};
})(),
graphOnlyModules([translateDefaultsPath(defaults.moduleSystem)]),
graphOnlyModules(getPolyfills({platform}).map(translateDefaultsPath)),
]);
const {
graph: {modules, entryModules},
moduleSystem,
polyfills,
} = nullthrows(result);
const preludeScript = prelude(optimize);
const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills];
callback(null, {
entryModules,
modules: prependedScripts.concat(modules),
prependedScripts,
});
});
const {entryModules} = graph;
const preludeScript = prelude(optimize);
const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills];
return {
entryModules,
modules: [...prependedScripts, ...graph.modules],
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) {
return virtualModule(
`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
* 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.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';
const ModuleGraph = require('../ModuleGraph');
const defaults = require('../../defaults');
const FILE_TYPE = 'module';
@ -21,66 +26,58 @@ describe('build setup', () => {
const noOptions = {};
const noEntryPoints = [];
it('adds a prelude containing start time and `__DEV__` to the build', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
expect(error).toEqual(null);
it('adds a prelude containing start time and `__DEV__` to the build', async () => {
const result = await buildSetup(noEntryPoints, noOptions);
const [prelude] = result.modules;
expect(prelude).toEqual({
dependencies: [],
file: {
code: 'var __DEV__=true,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();',
map: null,
path: '',
type: 'script',
},
});
done();
const [prelude] = result.modules;
expect(prelude).toEqual({
dependencies: [],
file: {
code:
'var __DEV__=true,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();',
map: null,
path: '',
type: 'script',
},
});
});
it('sets `__DEV__` to false in the prelude if optimization is enabled', done => {
buildSetup(noEntryPoints, {optimize: true}, (error, result) => {
const [prelude] = result.modules;
expect(prelude.file.code)
.toEqual('var __DEV__=false,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();');
done();
it('sets `__DEV__` to false in the prelude if optimization is enabled', async () => {
const result = await buildSetup(noEntryPoints, {optimize: true});
const [prelude] = result.modules;
expect(prelude.file.code).toEqual(
'var __DEV__=false,__BUNDLE_START_TIME__=' +
'this.nativePerformanceNow?nativePerformanceNow():Date.now();',
);
});
it('places the module system implementation directly after the prelude', async () => {
const result = await buildSetup(noEntryPoints, noOptions);
const [, moduleSystem] = result.modules;
expect(moduleSystem).toEqual({
dependencies: [],
file: {
code: '',
path: defaults.moduleSystem,
type: FILE_TYPE,
},
});
});
it('places the module system implementation directly after the prelude', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
const [, moduleSystem] = result.modules;
expect(moduleSystem).toEqual({
dependencies: [],
file: {
code: '',
path: defaults.moduleSystem,
type: FILE_TYPE,
},
});
done();
});
it('places polyfills after the module system', async () => {
const result = await buildSetup(noEntryPoints, polyfillOptions);
const list = polyfillOptions.getPolyfills();
const polyfills = result.modules.slice(2, list.length + 2);
expect(polyfills).toEqual(list.map(moduleFromPath));
});
it('places polyfills after the module system', done => {
buildSetup(noEntryPoints, polyfillOptions, (error, result) => {
const list = polyfillOptions.getPolyfills();
const polyfills = result.modules.slice(2, list.length + 2);
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'];
buildSetup(entryPoints, noOptions, (error, result) => {
expect(result.modules.slice(-4))
.toEqual(['a', 'b', 'c', 'd'].map(moduleFromPath));
done();
});
const result = await buildSetup(entryPoints, noOptions);
expect(result.modules.slice(-4)).toEqual(
['a', 'b', 'c', 'd'].map(moduleFromPath),
);
});
});