mirror of https://github.com/status-im/metro.git
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:
parent
f585a9ea63
commit
b3ad054424
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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', []),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue