mirror of https://github.com/status-im/metro.git
`GraphFn` returns promise rather than taking callback
Summary: Changes `GraphFn` to return a promise rather than taking a callback. This is more in line with call sites, which so far have been using `denodeify`. Reviewed By: jeanlauliac Differential Revision: D5857192 fbshipit-source-id: 61bbbef4fbf24b0b91c4a3008541eaa5a1af0f7a
This commit is contained in:
parent
5aaf148179
commit
c546349da4
|
@ -57,7 +57,7 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
|
|||
const resolveCallback = asyncify(resolve);
|
||||
const loadCallback = asyncify(load);
|
||||
|
||||
function Graph(entryPoints, platform, options, callback = emptyFunction) {
|
||||
function Graph(entryPoints, platform, options) {
|
||||
const {
|
||||
log = (console: any),
|
||||
optimize = false,
|
||||
|
@ -66,8 +66,7 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
|
|||
|
||||
if (typeof platform !== 'string') {
|
||||
log.error('`Graph`, called without a platform');
|
||||
callback(Error('The target platform has to be passed'));
|
||||
return;
|
||||
return Promise.reject(new Error('The target platform has to be passed'));
|
||||
}
|
||||
|
||||
const loadQueue: LoadQueue = queue(seq(
|
||||
|
@ -78,14 +77,16 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
|
|||
|
||||
const {collect, loadModule} = createGraphHelpers(loadQueue, skip);
|
||||
|
||||
const result = deferred();
|
||||
|
||||
loadQueue.drain = () => {
|
||||
loadQueue.kill();
|
||||
callback(null, collect());
|
||||
result.resolve(collect());
|
||||
};
|
||||
loadQueue.error = error => {
|
||||
loadQueue.error = emptyFunction;
|
||||
loadQueue.kill();
|
||||
callback(error);
|
||||
result.reject(error);
|
||||
};
|
||||
|
||||
let i = 0;
|
||||
|
@ -96,8 +97,10 @@ exports.create = function create(resolve: ResolveFn, load: LoadFn): GraphFn {
|
|||
if (i === 0) {
|
||||
log.error('`Graph` called without any entry points');
|
||||
loadQueue.kill();
|
||||
callback(Error('At least one entry point has to be passed.'));
|
||||
return Promise.reject(new Error('At least one entry point has to be passed.'));
|
||||
}
|
||||
|
||||
return result.promise;
|
||||
}
|
||||
|
||||
return Graph;
|
||||
|
@ -173,3 +176,17 @@ function createGraphHelpers(loadQueue, skip) {
|
|||
|
||||
return {collect, loadModule};
|
||||
}
|
||||
|
||||
function deferred<T>(): {
|
||||
promise: Promise<T>,
|
||||
reject: Error => void,
|
||||
resolve: T => void,
|
||||
} {
|
||||
let reject, resolve;
|
||||
const promise = new Promise((res, rej) => {
|
||||
reject = rej;
|
||||
resolve = res;
|
||||
});
|
||||
|
||||
return {promise, reject: nullthrows(reject), resolve: nullthrows(resolve)};
|
||||
}
|
||||
|
|
|
@ -12,15 +12,9 @@
|
|||
'use strict';
|
||||
|
||||
const defaults = require('../defaults');
|
||||
const denodeify: Denodeify = require('denodeify');
|
||||
const virtualModule = require('./module').virtual;
|
||||
|
||||
import type {
|
||||
BuildResult,
|
||||
Callback,
|
||||
GraphFn,
|
||||
PostProcessModules,
|
||||
} from './types.flow';
|
||||
import type {BuildResult, GraphFn, PostProcessModules} from './types.flow';
|
||||
|
||||
export type BuildFn = (
|
||||
entryPoints: Iterable<string>,
|
||||
|
@ -33,10 +27,6 @@ type BuildOptions = {|
|
|||
platform: string,
|
||||
|};
|
||||
|
||||
type Denodeify = <A, B, C, T>(
|
||||
(A, B, C, Callback<T>) => void,
|
||||
) => (A, B, C) => Promise<T>;
|
||||
|
||||
exports.createBuildSetup = (
|
||||
graphFn: GraphFn,
|
||||
postProcessModules: PostProcessModules,
|
||||
|
@ -49,8 +39,7 @@ exports.createBuildSetup = (
|
|||
} = options;
|
||||
const graphOptions = {optimize};
|
||||
|
||||
const pgraph = denodeify(graphFn);
|
||||
const graphWithOptions = entry => pgraph(entry, platform, graphOptions);
|
||||
const graphWithOptions = entry => graphFn(entry, platform, graphOptions);
|
||||
const graphOnlyModules = async m => (await graphWithOptions(m)).modules;
|
||||
|
||||
const [graph, moduleSystem, polyfills] = await Promise.all([
|
||||
|
|
|
@ -35,108 +35,80 @@ describe('Graph:', () => {
|
|||
graph = Graph.create(resolve, load);
|
||||
});
|
||||
|
||||
it('calls back an error when called without any entry point', done => {
|
||||
graph([], anyPlatform, {log: quiet}, error => {
|
||||
it('calls back an error when called without any entry point', async () => {
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await graph([], anyPlatform, {log: quiet});
|
||||
} catch (error) {
|
||||
expect(error).toEqual(any(Error));
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('resolves the entry point with the passed-in `resolve` function', done => {
|
||||
it('resolves the entry point with the passed-in `resolve` function', async () => {
|
||||
const entryPoint = '/arbitrary/path';
|
||||
graph([entryPoint], anyPlatform, noOpts, () => {
|
||||
expect(resolve).toBeCalledWith(
|
||||
entryPoint,
|
||||
null,
|
||||
any(String),
|
||||
any(Object),
|
||||
);
|
||||
done();
|
||||
});
|
||||
await graph([entryPoint], anyPlatform, noOpts);
|
||||
|
||||
expect(resolve).toBeCalledWith(entryPoint, null, any(String), any(Object));
|
||||
});
|
||||
|
||||
it('allows to specify multiple entry points', done => {
|
||||
it('allows to specify multiple entry points', async () => {
|
||||
const entryPoints = ['Arbitrary', '../entry.js'];
|
||||
graph(entryPoints, anyPlatform, noOpts, () => {
|
||||
expect(resolve).toBeCalledWith(
|
||||
entryPoints[0],
|
||||
null,
|
||||
any(String),
|
||||
any(Object),
|
||||
);
|
||||
expect(resolve).toBeCalledWith(
|
||||
entryPoints[1],
|
||||
null,
|
||||
any(String),
|
||||
any(Object),
|
||||
);
|
||||
done();
|
||||
});
|
||||
await graph(entryPoints, anyPlatform, noOpts);
|
||||
|
||||
expect(resolve).toBeCalledWith(
|
||||
entryPoints[0],
|
||||
null,
|
||||
any(String),
|
||||
any(Object),
|
||||
);
|
||||
expect(resolve).toBeCalledWith(
|
||||
entryPoints[1],
|
||||
null,
|
||||
any(String),
|
||||
any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('calls back with an error when called without `platform` option', done => {
|
||||
graph(anyEntry, undefined, {log: quiet}, error => {
|
||||
it('calls back with an error when called without `platform` option', async () => {
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await graph(anyEntry, undefined, {log: quiet});
|
||||
} catch (error) {
|
||||
expect(error).toEqual(any(Error));
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('forwards a passed-in `platform` to `resolve`', done => {
|
||||
it('forwards a passed-in `platform` to `resolve`', async () => {
|
||||
const platform = 'any';
|
||||
graph(anyEntry, platform, noOpts, () => {
|
||||
expect(resolve).toBeCalledWith(any(String), null, platform, any(Object));
|
||||
done();
|
||||
});
|
||||
await graph(anyEntry, platform, noOpts);
|
||||
|
||||
expect(resolve).toBeCalledWith(any(String), null, platform, any(Object));
|
||||
});
|
||||
|
||||
it('forwards a passed-in `log` option to `resolve`', done => {
|
||||
it('forwards a passed-in `log` option to `resolve`', async () => {
|
||||
const log = new Console();
|
||||
graph(anyEntry, anyPlatform, {log}, () => {
|
||||
expect(resolve).toBeCalledWith(
|
||||
any(String),
|
||||
null,
|
||||
any(String),
|
||||
objectContaining({log}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
await graph(anyEntry, anyPlatform, {log});
|
||||
expect(resolve).toBeCalledWith(
|
||||
any(String),
|
||||
null,
|
||||
any(String),
|
||||
objectContaining({log}),
|
||||
);
|
||||
});
|
||||
|
||||
it('calls back with every error produced by `resolve`', done => {
|
||||
it('calls back with every error produced by `resolve`', async () => {
|
||||
expect.assertions(1);
|
||||
const error = Error();
|
||||
resolve.stub.throws(error);
|
||||
graph(anyEntry, anyPlatform, noOpts, e => {
|
||||
|
||||
try {
|
||||
await graph(anyEntry, anyPlatform, noOpts);
|
||||
} catch (e) {
|
||||
expect(e).toBe(error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('only calls back once if two parallel invocations of `resolve` fail', done => {
|
||||
load.stub.returns({
|
||||
file: createFile('with two deps'),
|
||||
dependencies: ['depA', 'depB'],
|
||||
});
|
||||
resolve.stub
|
||||
.withArgs('depA')
|
||||
.throws(new Error())
|
||||
.withArgs('depB')
|
||||
.throws(new Error());
|
||||
|
||||
let calls = 0;
|
||||
function callback() {
|
||||
if (calls === 0) {
|
||||
process.nextTick(() => {
|
||||
expect(calls).toEqual(1);
|
||||
done();
|
||||
});
|
||||
}
|
||||
++calls;
|
||||
}
|
||||
|
||||
graph(['entryA', 'entryB'], anyPlatform, noOpts, callback);
|
||||
});
|
||||
|
||||
it('passes the files returned by `resolve` on to the `load` function', done => {
|
||||
it('passes the files returned by `resolve` on to the `load` function', async () => {
|
||||
const modules = new Map([
|
||||
['Arbitrary', '/absolute/path/to/Arbitrary.js'],
|
||||
['../entry.js', '/whereever/is/entry.js'],
|
||||
|
@ -146,51 +118,46 @@ describe('Graph:', () => {
|
|||
}
|
||||
const [file1, file2] = modules.values();
|
||||
|
||||
graph(modules.keys(), anyPlatform, noOpts, () => {
|
||||
expect(load).toBeCalledWith(file1, any(Object));
|
||||
expect(load).toBeCalledWith(file2, any(Object));
|
||||
done();
|
||||
});
|
||||
await graph(modules.keys(), anyPlatform, noOpts);
|
||||
expect(load).toBeCalledWith(file1, any(Object));
|
||||
expect(load).toBeCalledWith(file2, any(Object));
|
||||
});
|
||||
|
||||
it('passes the `optimize` flag on to `load`', done => {
|
||||
graph(anyEntry, anyPlatform, {optimize: true}, () => {
|
||||
expect(load).toBeCalledWith(
|
||||
any(String),
|
||||
objectContaining({optimize: true}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('passes the `optimize` flag on to `load`', async () => {
|
||||
await graph(anyEntry, anyPlatform, {optimize: true});
|
||||
expect(load).toBeCalledWith(
|
||||
any(String),
|
||||
objectContaining({optimize: true}),
|
||||
);
|
||||
});
|
||||
|
||||
it('uses `false` as the default for the `optimize` flag', done => {
|
||||
graph(anyEntry, anyPlatform, noOpts, () => {
|
||||
expect(load).toBeCalledWith(
|
||||
any(String),
|
||||
objectContaining({optimize: false}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('uses `false` as the default for the `optimize` flag', async () => {
|
||||
await graph(anyEntry, anyPlatform, noOpts);
|
||||
expect(load).toBeCalledWith(
|
||||
any(String),
|
||||
objectContaining({optimize: false}),
|
||||
);
|
||||
});
|
||||
|
||||
it('forwards a passed-in `log` to `load`', done => {
|
||||
it('forwards a passed-in `log` to `load`', async () => {
|
||||
const log = new Console();
|
||||
graph(anyEntry, anyPlatform, {log}, () => {
|
||||
expect(load).toBeCalledWith(any(String), objectContaining({log}));
|
||||
done();
|
||||
});
|
||||
await graph(anyEntry, anyPlatform, {log});
|
||||
expect(load).toBeCalledWith(any(String), objectContaining({log}));
|
||||
});
|
||||
|
||||
it('calls back with every error produced by `load`', done => {
|
||||
it('calls back with every error produced by `load`', async () => {
|
||||
expect.assertions(1);
|
||||
const error = Error();
|
||||
load.stub.throws(error);
|
||||
graph(anyEntry, anyPlatform, noOpts, e => {
|
||||
|
||||
try {
|
||||
await graph(anyEntry, anyPlatform, noOpts);
|
||||
} catch (e) {
|
||||
expect(e).toBe(error);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('resolves any dependencies provided by `load`', done => {
|
||||
it('resolves any dependencies provided by `load`', async () => {
|
||||
const entryPath = '/path/to/entry.js';
|
||||
const id1 = 'required/id';
|
||||
const id2 = './relative/import';
|
||||
|
@ -200,14 +167,12 @@ describe('Graph:', () => {
|
|||
dependencies: [id1, id2],
|
||||
});
|
||||
|
||||
graph(['entry'], anyPlatform, noOpts, () => {
|
||||
expect(resolve).toBeCalledWith(id1, entryPath, any(String), any(Object));
|
||||
expect(resolve).toBeCalledWith(id2, entryPath, any(String), any(Object));
|
||||
done();
|
||||
});
|
||||
await graph(['entry'], anyPlatform, noOpts);
|
||||
expect(resolve).toBeCalledWith(id1, entryPath, any(String), any(Object));
|
||||
expect(resolve).toBeCalledWith(id2, entryPath, any(String), any(Object));
|
||||
});
|
||||
|
||||
it('loads transitive dependencies', done => {
|
||||
it('loads transitive dependencies', async () => {
|
||||
const entryPath = '/path/to/entry.js';
|
||||
const id1 = 'required/id';
|
||||
const id2 = './relative/import';
|
||||
|
@ -227,15 +192,13 @@ describe('Graph:', () => {
|
|||
.withArgs(path1)
|
||||
.returns({file: {path: path1}, dependencies: [id2]});
|
||||
|
||||
graph(['entry'], anyPlatform, noOpts, () => {
|
||||
expect(resolve).toBeCalledWith(id2, path1, any(String), any(Object));
|
||||
expect(load).toBeCalledWith(path1, any(Object));
|
||||
expect(load).toBeCalledWith(path2, any(Object));
|
||||
done();
|
||||
});
|
||||
await graph(['entry'], anyPlatform, noOpts);
|
||||
expect(resolve).toBeCalledWith(id2, path1, any(String), any(Object));
|
||||
expect(load).toBeCalledWith(path1, any(Object));
|
||||
expect(load).toBeCalledWith(path2, any(Object));
|
||||
});
|
||||
|
||||
it('resolves modules in depth-first traversal order, regardless of the order of loading', done => {
|
||||
it('resolves modules in depth-first traversal order, regardless of the order of loading', async () => {
|
||||
load.stub.reset();
|
||||
resolve.stub.reset();
|
||||
|
||||
|
@ -269,23 +232,20 @@ describe('Graph:', () => {
|
|||
return {file: createFile('h'), dependencies: []};
|
||||
};
|
||||
|
||||
graph(['a'], anyPlatform, noOpts, (error, result) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'e', 'h']),
|
||||
createModule('b', ['c', 'd']),
|
||||
createModule('c'),
|
||||
createModule('d'),
|
||||
createModule('e', ['f', 'g']),
|
||||
createModule('f'),
|
||||
createModule('g'),
|
||||
createModule('h'),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a'], anyPlatform, noOpts);
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'e', 'h']),
|
||||
createModule('b', ['c', 'd']),
|
||||
createModule('c'),
|
||||
createModule('d'),
|
||||
createModule('e', ['f', 'g']),
|
||||
createModule('f'),
|
||||
createModule('g'),
|
||||
createModule('h'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('calls back with the resolved modules of the entry points', done => {
|
||||
it('calls back with the resolved modules of the entry points', async () => {
|
||||
load.stub.reset();
|
||||
resolve.stub.reset();
|
||||
|
||||
|
@ -306,16 +266,14 @@ describe('Graph:', () => {
|
|||
.split('')
|
||||
.forEach(id => resolve.stub.withArgs(id).returns(idToPath(id)));
|
||||
|
||||
graph(['a', 'c'], anyPlatform, noOpts, (error, result) => {
|
||||
expect(result.entryModules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('c', ['d']),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a', 'c'], anyPlatform, noOpts);
|
||||
expect(result.entryModules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('c', ['d']),
|
||||
]);
|
||||
});
|
||||
|
||||
it('resolves modules for all entry points correctly if one is a dependency of another', done => {
|
||||
it('resolves modules for all entry points correctly if one is a dependency of another', async () => {
|
||||
load.stub.reset();
|
||||
resolve.stub.reset();
|
||||
|
||||
|
@ -330,16 +288,14 @@ describe('Graph:', () => {
|
|||
.split('')
|
||||
.forEach(id => resolve.stub.withArgs(id).returns(idToPath(id)));
|
||||
|
||||
graph(['a', 'b'], anyPlatform, noOpts, (error, result) => {
|
||||
expect(result.entryModules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('b', []),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a', 'b'], anyPlatform, noOpts);
|
||||
expect(result.entryModules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('b', []),
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not include dependencies more than once', done => {
|
||||
it('does not include dependencies more than once', async () => {
|
||||
const ids = ['a', 'b', 'c', 'd'];
|
||||
ids.forEach(id => {
|
||||
const path = idToPath(id);
|
||||
|
@ -354,19 +310,16 @@ describe('Graph:', () => {
|
|||
.returns({file: createFile(id), dependencies: ['b', 'c']}),
|
||||
);
|
||||
|
||||
graph(['a', 'd', 'b'], anyPlatform, noOpts, (error, result) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'c']),
|
||||
createModule('b'),
|
||||
createModule('c'),
|
||||
createModule('d', ['b', 'c']),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a', 'd', 'b'], anyPlatform, noOpts);
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'c']),
|
||||
createModule('b'),
|
||||
createModule('c'),
|
||||
createModule('d', ['b', 'c']),
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles dependency cycles', done => {
|
||||
it('handles dependency cycles', async () => {
|
||||
resolve.stub
|
||||
.withArgs('a')
|
||||
.returns(idToPath('a'))
|
||||
|
@ -382,17 +335,15 @@ describe('Graph:', () => {
|
|||
.withArgs(idToPath('c'))
|
||||
.returns({file: createFile('c'), dependencies: ['a']});
|
||||
|
||||
graph(['a'], anyPlatform, noOpts, (error, result) => {
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('b', ['c']),
|
||||
createModule('c', ['a']),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a'], anyPlatform, noOpts);
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b']),
|
||||
createModule('b', ['c']),
|
||||
createModule('c', ['a']),
|
||||
]);
|
||||
});
|
||||
|
||||
it('can skip files', done => {
|
||||
it('can skip files', async () => {
|
||||
['a', 'b', 'c', 'd', 'e'].forEach(id =>
|
||||
resolve.stub.withArgs(id).returns(idToPath(id)),
|
||||
);
|
||||
|
@ -408,13 +359,11 @@ describe('Graph:', () => {
|
|||
);
|
||||
const skip = new Set([idToPath('b'), idToPath('c')]);
|
||||
|
||||
graph(['a'], anyPlatform, {skip}, (error, result) => {
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'c', 'd']),
|
||||
createModule('d', []),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
const result = await graph(['a'], anyPlatform, {skip});
|
||||
expect(result.modules).toEqual([
|
||||
createModule('a', ['b', 'c', 'd']),
|
||||
createModule('d', []),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -92,14 +92,14 @@ function moduleFromPath(path) {
|
|||
};
|
||||
}
|
||||
|
||||
function graph(entryPoints, platform, options, callback) {
|
||||
async function graph(entryPoints, platform, options, callback) {
|
||||
const modules = Array.from(entryPoints, moduleFromPath);
|
||||
const depModules = Array.prototype.concat.apply(
|
||||
[],
|
||||
modules.map(x => x.dependencies.map(moduleFromPath)),
|
||||
);
|
||||
callback(null, {
|
||||
return {
|
||||
entryModules: modules,
|
||||
modules: modules.concat(depModules),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,8 +42,7 @@ export type GraphFn = (
|
|||
entryPoints: Iterable<string>,
|
||||
platform: string,
|
||||
options?: ?GraphOptions,
|
||||
callback?: Callback<GraphResult>,
|
||||
) => void;
|
||||
) => Promise<GraphResult>;
|
||||
|
||||
type GraphOptions = {|
|
||||
log?: Console,
|
||||
|
|
Loading…
Reference in New Issue