mirror of https://github.com/status-im/metro.git
Add multiple entrypoint support to the dependency traversal logic
Reviewed By: davidaurelio Differential Revision: D7180692 fbshipit-source-id: 65b1992abbf10e753b6895623824ff90d7cadc24
This commit is contained in:
parent
5e9cf9ec4b
commit
45123d822b
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
const {
|
const {
|
||||||
initialTraverseDependencies,
|
initialTraverseDependencies,
|
||||||
reorderDependencies,
|
reorderGraph,
|
||||||
traverseDependencies,
|
traverseDependencies,
|
||||||
} = require('./traverseDependencies');
|
} = require('./traverseDependencies');
|
||||||
const {EventEmitter} = require('events');
|
const {EventEmitter} = require('events');
|
||||||
|
@ -60,13 +60,15 @@ class DeltaCalculator extends EventEmitter {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
this._dependencyGraph = dependencyGraph;
|
this._dependencyGraph = dependencyGraph;
|
||||||
|
|
||||||
const entryFilePath = this._dependencyGraph.getAbsolutePath(
|
// The traverse dependencies logic supports multiple entry points, but
|
||||||
this._options.entryFile,
|
// currently metro only supports to pass a single entry point when bundling.
|
||||||
);
|
const entryPoints = [
|
||||||
|
this._dependencyGraph.getAbsolutePath(this._options.entryFile),
|
||||||
|
];
|
||||||
|
|
||||||
this._graph = {
|
this._graph = {
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: entryFilePath,
|
entryPoints,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._dependencyGraph
|
this._dependencyGraph
|
||||||
|
@ -87,7 +89,7 @@ class DeltaCalculator extends EventEmitter {
|
||||||
// Clean up all the cache data structures to deallocate memory.
|
// Clean up all the cache data structures to deallocate memory.
|
||||||
this._graph = {
|
this._graph = {
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: this._options.entryFile,
|
entryPoints: [this._options.entryFile],
|
||||||
};
|
};
|
||||||
this._modifiedFiles = new Set();
|
this._modifiedFiles = new Set();
|
||||||
this._deletedFiles = new Set();
|
this._deletedFiles = new Set();
|
||||||
|
@ -148,10 +150,7 @@ class DeltaCalculator extends EventEmitter {
|
||||||
|
|
||||||
// Return all the modules if the client requested a reset delta.
|
// Return all the modules if the client requested a reset delta.
|
||||||
if (reset) {
|
if (reset) {
|
||||||
this._graph.dependencies = reorderDependencies(
|
reorderGraph(this._graph);
|
||||||
this._graph.dependencies.get(this._graph.entryFile),
|
|
||||||
this._graph.dependencies,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modified: this._graph.dependencies,
|
modified: this._graph.dependencies,
|
||||||
|
@ -202,7 +201,7 @@ class DeltaCalculator extends EventEmitter {
|
||||||
const {added} = await initialTraverseDependencies(
|
const {added} = await initialTraverseDependencies(
|
||||||
{
|
{
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: path,
|
entryPoints: [path],
|
||||||
},
|
},
|
||||||
this._dependencyGraph,
|
this._dependencyGraph,
|
||||||
transformOptionsForBlacklist,
|
transformOptionsForBlacklist,
|
||||||
|
|
|
@ -15,7 +15,6 @@ jest.mock('../traverseDependencies');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
initialTraverseDependencies,
|
initialTraverseDependencies,
|
||||||
reorderDependencies,
|
|
||||||
traverseDependencies,
|
traverseDependencies,
|
||||||
} = require('../traverseDependencies');
|
} = require('../traverseDependencies');
|
||||||
|
|
||||||
|
@ -198,8 +197,6 @@ describe('DeltaCalculator', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a full delta when passing reset=true', async () => {
|
it('should return a full delta when passing reset=true', async () => {
|
||||||
reorderDependencies.mockImplementation((_, edges) => edges);
|
|
||||||
|
|
||||||
await deltaCalculator.getDelta({reset: false});
|
await deltaCalculator.getDelta({reset: false});
|
||||||
|
|
||||||
const result = await deltaCalculator.getDelta({reset: true});
|
const result = await deltaCalculator.getDelta({reset: true});
|
||||||
|
|
|
@ -59,6 +59,8 @@ Object {
|
||||||
"path": "/baz",
|
"path": "/baz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"entryFile": "/bundle",
|
"entryPoints": Array [
|
||||||
|
"/bundle",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe('traverseDependencies', function() {
|
||||||
|
|
||||||
const graph = {
|
const graph = {
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: entryPath,
|
entryPoints: [entryPath],
|
||||||
};
|
};
|
||||||
|
|
||||||
const {added} = await traverseDependencies.initialTraverseDependencies(
|
const {added} = await traverseDependencies.initialTraverseDependencies(
|
||||||
|
@ -5073,7 +5073,7 @@ describe('traverseDependencies', function() {
|
||||||
return traverseDependencies.initialTraverseDependencies(
|
return traverseDependencies.initialTraverseDependencies(
|
||||||
{
|
{
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: '/root/index.js',
|
entryPoints: ['/root/index.js'],
|
||||||
},
|
},
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
emptyTransformOptions,
|
emptyTransformOptions,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
const {
|
const {
|
||||||
initialTraverseDependencies,
|
initialTraverseDependencies,
|
||||||
|
reorderGraph,
|
||||||
traverseDependencies,
|
traverseDependencies,
|
||||||
} = require('../traverseDependencies');
|
} = require('../traverseDependencies');
|
||||||
|
|
||||||
|
@ -172,7 +173,7 @@ beforeEach(async () => {
|
||||||
|
|
||||||
graph = {
|
graph = {
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: '/bundle',
|
entryPoints: ['/bundle'],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -377,6 +378,33 @@ describe('edge cases', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should traverse a graph from multiple entry points', async () => {
|
||||||
|
entryModule = Actions.createFile('/bundle-2');
|
||||||
|
|
||||||
|
Actions.addDependency('/bundle-2', '/bundle-2-foo');
|
||||||
|
Actions.addDependency('/bundle-2', '/bundle-2-bar');
|
||||||
|
Actions.addDependency('/bundle-2', '/bar');
|
||||||
|
|
||||||
|
files = new Set();
|
||||||
|
|
||||||
|
graph = {
|
||||||
|
dependencies: new Map(),
|
||||||
|
entryPoints: ['/bundle', '/bundle-2'],
|
||||||
|
};
|
||||||
|
|
||||||
|
await initialTraverseDependencies(graph, dependencyGraph, {});
|
||||||
|
|
||||||
|
expect([...graph.dependencies.keys()]).toEqual([
|
||||||
|
'/bundle',
|
||||||
|
'/foo',
|
||||||
|
'/bar',
|
||||||
|
'/baz',
|
||||||
|
'/bundle-2',
|
||||||
|
'/bundle-2-foo',
|
||||||
|
'/bundle-2-bar',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should traverse the dependency tree in a deterministic order', async () => {
|
it('should traverse the dependency tree in a deterministic order', async () => {
|
||||||
// Mocks the shallow dependency call, always resolving the module in
|
// Mocks the shallow dependency call, always resolving the module in
|
||||||
// `slowPath` after the module in `fastPath`.
|
// `slowPath` after the module in `fastPath`.
|
||||||
|
@ -416,7 +444,7 @@ describe('edge cases', () => {
|
||||||
async function assertOrder() {
|
async function assertOrder() {
|
||||||
graph = {
|
graph = {
|
||||||
dependencies: new Map(),
|
dependencies: new Map(),
|
||||||
entryFile: '/bundle',
|
entryPoints: ['/bundle'],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -483,3 +511,33 @@ describe('edge cases', () => {
|
||||||
expect(moduleFoo.read).toHaveBeenCalledWith({inlineRequires: true});
|
expect(moduleFoo.read).toHaveBeenCalledWith({inlineRequires: true});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('reorderGraph', () => {
|
||||||
|
it('should reorder any unordered graph in DFS order', async () => {
|
||||||
|
const graph = {
|
||||||
|
dependencies: new Map([
|
||||||
|
['/2', {dependencies: new Map(), path: '/2'}],
|
||||||
|
[
|
||||||
|
'/0',
|
||||||
|
{path: '/0', dependencies: new Map([['/1', '/1'], ['/2', '/2']])},
|
||||||
|
],
|
||||||
|
['/1', {dependencies: new Map([['/2', '/2']]), path: '/1'}],
|
||||||
|
['/3', {dependencies: new Map(), path: '/3'}],
|
||||||
|
['/b', {dependencies: new Map([['/3', '/3']]), path: '/b'}],
|
||||||
|
['/a', {dependencies: new Map([['/0', '/0']]), path: '/a'}],
|
||||||
|
]),
|
||||||
|
entryPoints: ['/a', '/b'],
|
||||||
|
};
|
||||||
|
|
||||||
|
reorderGraph(graph);
|
||||||
|
|
||||||
|
expect([...graph.dependencies.keys()]).toEqual([
|
||||||
|
'/a',
|
||||||
|
'/0',
|
||||||
|
'/1',
|
||||||
|
'/2',
|
||||||
|
'/b',
|
||||||
|
'/3',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@ export type DependencyEdges = Map<string, DependencyEdge>;
|
||||||
|
|
||||||
export type Graph = {|
|
export type Graph = {|
|
||||||
dependencies: DependencyEdges,
|
dependencies: DependencyEdges,
|
||||||
entryFile: string,
|
entryPoints: $ReadOnlyArray<string>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>};
|
type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>};
|
||||||
|
@ -67,7 +67,7 @@ type Delta = {
|
||||||
* since the last traversal.
|
* since the last traversal.
|
||||||
*/
|
*/
|
||||||
async function traverseDependencies(
|
async function traverseDependencies(
|
||||||
paths: Array<string>,
|
paths: $ReadOnlyArray<string>,
|
||||||
dependencyGraph: DependencyGraph,
|
dependencyGraph: DependencyGraph,
|
||||||
transformOptions: JSTransformerOptions,
|
transformOptions: JSTransformerOptions,
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
|
@ -139,29 +139,23 @@ async function initialTraverseDependencies(
|
||||||
transformOptions: JSTransformerOptions,
|
transformOptions: JSTransformerOptions,
|
||||||
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
const edge = createEdge(
|
graph.entryPoints.forEach(entryPoint =>
|
||||||
dependencyGraph.getModuleForPath(graph.entryFile),
|
createEdge(dependencyGraph.getModuleForPath(entryPoint), graph),
|
||||||
graph,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const delta = {
|
await traverseDependencies(
|
||||||
added: new Map([[edge.path, edge]]),
|
graph.entryPoints,
|
||||||
modified: new Map(),
|
|
||||||
deleted: new Set(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await traverseDependenciesForSingleFile(
|
|
||||||
edge,
|
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
transformOptions,
|
transformOptions,
|
||||||
graph,
|
graph,
|
||||||
delta,
|
|
||||||
onProgress,
|
onProgress,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
reorderGraph(graph);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
added: reorderDependencies(edge, delta.added),
|
added: graph.dependencies,
|
||||||
deleted: delta.deleted,
|
deleted: new Set(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,30 +385,37 @@ function resolveDependencies(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retraverse the dependency graph in DFS order to reorder the modules and
|
* Retraverse the dependency graph in DFS order to reorder the modules and
|
||||||
* guarantee the same order between runs.
|
* guarantee the same order between runs. This method mutates the passed graph.
|
||||||
*/
|
*/
|
||||||
|
function reorderGraph(graph: Graph) {
|
||||||
|
const parent = {
|
||||||
|
dependencies: new Map(graph.entryPoints.map(e => [e, e])),
|
||||||
|
};
|
||||||
|
|
||||||
|
const dependencies = reorderDependencies(parent, graph.dependencies);
|
||||||
|
|
||||||
|
graph.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
function reorderDependencies(
|
function reorderDependencies(
|
||||||
edge: ?DependencyEdge,
|
edge: {|dependencies: Map<string, string>|} | DependencyEdge,
|
||||||
dependencies: Map<string, DependencyEdge>,
|
dependencies: Map<string, DependencyEdge>,
|
||||||
orderedDependencies?: Map<string, DependencyEdge> = new Map(),
|
orderedDependencies?: Map<string, DependencyEdge> = new Map(),
|
||||||
): Map<string, DependencyEdge> {
|
): Map<string, DependencyEdge> {
|
||||||
if (
|
if (edge.path) {
|
||||||
!edge ||
|
if (orderedDependencies.has(edge.path)) {
|
||||||
!dependencies.has(edge.path) ||
|
|
||||||
orderedDependencies.has(edge.path)
|
|
||||||
) {
|
|
||||||
return orderedDependencies;
|
return orderedDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
orderedDependencies.set(edge.path, edge);
|
orderedDependencies.set(edge.path, edge);
|
||||||
|
}
|
||||||
|
|
||||||
edge.dependencies.forEach(path =>
|
edge.dependencies.forEach(path => {
|
||||||
reorderDependencies(
|
const dep = dependencies.get(path);
|
||||||
dependencies.get(path),
|
if (dep) {
|
||||||
dependencies,
|
reorderDependencies(dep, dependencies, orderedDependencies);
|
||||||
orderedDependencies,
|
}
|
||||||
),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return orderedDependencies;
|
return orderedDependencies;
|
||||||
}
|
}
|
||||||
|
@ -422,5 +423,5 @@ function reorderDependencies(
|
||||||
module.exports = {
|
module.exports = {
|
||||||
initialTraverseDependencies,
|
initialTraverseDependencies,
|
||||||
traverseDependencies,
|
traverseDependencies,
|
||||||
reorderDependencies,
|
reorderGraph,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue