ModuleGraph: Also call back with module objects of entry points

Summary:
This changes the callback value of `Graph` function so that it also contains a separate property with `Module` instances of the entry points.

Having references to the entry point modules allows to e.g. create require calls to them dynamically to kick of bundle execution.

The commit also contains a refactoring of the `Graph` function and its helpers that reduces the need for extensive parameter passing and long nested functions.

Reviewed By: cpojer

Differential Revision: D4250772

fbshipit-source-id: 2edca77bbef2308d3176a62123b8cecd70e2c8c7
This commit is contained in:
David Aurelio 2016-12-05 16:40:19 -08:00 committed by Facebook Github Bot
parent f585a9ea63
commit b3ad054424
3 changed files with 104 additions and 52 deletions

View File

@ -21,7 +21,6 @@ import type {
File, File,
GraphFn, GraphFn,
LoadFn, LoadFn,
Module,
ResolveFn, ResolveFn,
} from './types.flow'; } from './types.flow';
@ -70,17 +69,16 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
return; return;
} }
const modules: Map<string | null, Module> = new Map();
modules.set(null, createParentModule());
const loadQueue: LoadQueue = queue(seq( const loadQueue: LoadQueue = queue(seq(
({id, parent}, cb) => resolve(id, parent, platform, options || NO_OPTIONS, cb), ({id, parent}, cb) => resolve(id, parent, platform, options || NO_OPTIONS, cb),
memoize((file, cb) => load(file, {log, optimize}, cb)), memoize((file, cb) => load(file, {log, optimize}, cb)),
), Number.MAX_SAFE_INTEGER); ), Number.MAX_SAFE_INTEGER);
const {collect, loadModule} = createGraphHelpers(loadQueue, cwd, skip);
loadQueue.drain = () => { loadQueue.drain = () => {
loadQueue.kill(); loadQueue.kill();
callback(null, collect(null, modules)); callback(null, collect());
}; };
loadQueue.error = error => { loadQueue.error = error => {
loadQueue.error = noop; loadQueue.error = noop;
@ -90,7 +88,7 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
let i = 0; let i = 0;
for (const entryPoint of entryPoints) { for (const entryPoint of entryPoints) {
loadModule(entryPoint, null, i++, loadQueue, modules, skip, cwd, callback); loadModule(entryPoint, null, i++);
} }
if (i === 0) { if (i === 0) {
@ -103,19 +101,50 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
return Graph; return Graph;
}; };
function loadModule( function createGraphHelpers(loadQueue, cwd, skip) {
id: string, const modules = new Map([[null, createParentModule()]]);
parent: string | null,
parentDependencyIndex: number, function collect(
loadQueue: LoadQueue, path = null,
modules: Map<string | null, Module>, serialized = {entryModules: [], modules: []},
skip?: Set<string>, seen = new Set(),
cwd: string,
) { ) {
const module = modules.get(path);
if (module == null || seen.has(path)) {
return serialized;
}
const {dependencies} = module;
if (path === null) {
serialized.entryModules =
dependencies.map(dep => nullthrows(modules.get(dep.path)));
} else {
serialized.modules.push(module);
seen.add(path);
}
for (const dependency of dependencies) {
collect(dependency.path, serialized, seen);
}
return serialized;
}
function loadModule(id, parent, parentDepIndex) {
loadQueue.push(
{id, parent: parent != null ? parent : cwd},
(error, file, dependencyIDs) =>
onFileLoaded(error, file, dependencyIDs, id, parent, parentDepIndex),
);
}
function onFileLoaded( function onFileLoaded(
error?: ?Error, error,
file?: File, file,
dependencyIDs?: Array<string>, dependencyIDs,
id,
parent,
parentDependencyIndex,
) { ) {
if (error) { if (error) {
return; return;
@ -129,38 +158,16 @@ function loadModule(
parentModule.dependencies[parentDependencyIndex] = {id, path}; parentModule.dependencies[parentDependencyIndex] = {id, path};
if ((!skip || !skip.has(path)) && !modules.has(path)) { if ((!skip || !skip.has(path)) && !modules.has(path)) {
const dependencies = Array(dependencyIDs.length); const module = {
modules.set(path, {dependencies, file: nullthrows(file)}); dependencies: Array(dependencyIDs.length),
file: nullthrows(file),
};
modules.set(path, module);
for (let i = 0; i < dependencyIDs.length; ++i) { for (let i = 0; i < dependencyIDs.length; ++i) {
loadModule(dependencyIDs[i], path, i, loadQueue, modules, skip, cwd); loadModule(dependencyIDs[i], path, i);
} }
} }
} }
loadQueue.push( return {collect, loadModule};
{id, parent: parent != null ? parent : cwd},
onFileLoaded,
);
}
function collect(
path,
modules,
serialized = [],
seen: Set<string | null> = new Set(),
): Array<Module> {
const module = modules.get(path);
if (!module || seen.has(path)) { return serialized; }
if (path !== null) {
serialized.push(module);
seen.add(path);
}
const {dependencies} = module;
for (var i = 0; i < dependencies.length; i++) {
collect(dependencies[i].path, modules, serialized, seen);
}
return serialized;
} }

View File

@ -239,7 +239,7 @@ describe('Graph:', () => {
graph(['a'], anyPlatform, noOpts, (error, result) => { graph(['a'], anyPlatform, noOpts, (error, result) => {
expect(error).toEqual(null); expect(error).toEqual(null);
expect(result).toEqual([ expect(result.modules).toEqual([
createModule('a', ['b', 'e', 'h']), createModule('a', ['b', 'e', 'h']),
createModule('b', ['c', 'd']), createModule('b', ['c', 'd']),
createModule('c'), createModule('c'),
@ -253,6 +253,46 @@ describe('Graph:', () => {
}); });
}); });
it('calls back with the resolved modules of the entry points', done => {
load.stub.reset();
resolve.stub.reset();
load.stub.withArgs(idToPath('a')).yields(null, createFile('a'), ['b']);
load.stub.withArgs(idToPath('b')).yields(null, createFile('b'), []);
load.stub.withArgs(idToPath('c')).yields(null, createFile('c'), ['d']);
load.stub.withArgs(idToPath('d')).yields(null, createFile('d'), []);
'abcd'.split('')
.forEach(id => resolve.stub.withArgs(id).yields(null, idToPath(id)));
graph(['a', 'c'], anyPlatform, noOpts, (error, result) => {
expect(result.entryModules).toEqual([
createModule('a', ['b']),
createModule('c', ['d']),
]);
done();
});
});
it('calls back with the resolved modules of the entry points if one entry point is a dependency of another', done => {
load.stub.reset();
resolve.stub.reset();
load.stub.withArgs(idToPath('a')).yields(null, createFile('a'), ['b']);
load.stub.withArgs(idToPath('b')).yields(null, createFile('b'), []);
'ab'.split('')
.forEach(id => resolve.stub.withArgs(id).yields(null, idToPath(id)));
graph(['a', 'b'], anyPlatform, noOpts, (error, result) => {
expect(result.entryModules).toEqual([
createModule('a', ['b']),
createModule('b', []),
]);
done();
});
});
it('does not include dependencies more than once', done => { it('does not include dependencies more than once', done => {
const ids = ['a', 'b', 'c', 'd']; const ids = ['a', 'b', 'c', 'd'];
ids.forEach(id => { ids.forEach(id => {
@ -266,7 +306,7 @@ describe('Graph:', () => {
graph(['a', 'd', 'b'], anyPlatform, noOpts, (error, result) => { graph(['a', 'd', 'b'], anyPlatform, noOpts, (error, result) => {
expect(error).toEqual(null); expect(error).toEqual(null);
expect(result).toEqual([ expect(result.modules).toEqual([
createModule('a', ['b', 'c']), createModule('a', ['b', 'c']),
createModule('b'), createModule('b'),
createModule('c'), createModule('c'),
@ -287,7 +327,7 @@ describe('Graph:', () => {
.withArgs(idToPath('c')).yields(null, createFile('c'), ['a']); .withArgs(idToPath('c')).yields(null, createFile('c'), ['a']);
graph(['a'], anyPlatform, noOpts, (error, result) => { graph(['a'], anyPlatform, noOpts, (error, result) => {
expect(result).toEqual([ expect(result.modules).toEqual([
createModule('a', ['b']), createModule('a', ['b']),
createModule('b', ['c']), createModule('b', ['c']),
createModule('c', ['a']), createModule('c', ['a']),
@ -307,7 +347,7 @@ describe('Graph:', () => {
const skip = new Set([idToPath('b'), idToPath('c')]); const skip = new Set([idToPath('b'), idToPath('c')]);
graph(['a'], anyPlatform, {skip}, (error, result) => { graph(['a'], anyPlatform, {skip}, (error, result) => {
expect(result).toEqual([ expect(result.modules).toEqual([
createModule('a', ['b', 'c', 'd']), createModule('a', ['b', 'c', 'd']),
createModule('d', []), createModule('d', []),
]); ]);

View File

@ -56,9 +56,14 @@ export type GraphFn = (
entryPoints: Iterable<string>, entryPoints: Iterable<string>,
platform: string, platform: string,
options?: ?GraphOptions, options?: ?GraphOptions,
callback?: Callback<Array<Module>>, callback?: Callback<GraphResult>,
) => void; ) => void;
type GraphResult = {
entryModules: Array<Module>,
modules: Array<Module>,
};
export type ResolveFn = ( export type ResolveFn = (
id: string, id: string,
source: string, source: string,