mirror of https://github.com/status-im/metro.git
Make metro rely much less in the ModuleCache system
Reviewed By: jeanlauliac Differential Revision: D6949157 fbshipit-source-id: cb5ef25aa582935f7c624375392be7fceefd87b6
This commit is contained in:
parent
03c3b3d219
commit
a5307e986e
|
@ -19,16 +19,15 @@ const {EventEmitter} = require('events');
|
|||
import type Bundler from '../Bundler';
|
||||
import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
|
||||
import type DependencyGraph from '../node-haste/DependencyGraph';
|
||||
import type Module from '../node-haste/Module';
|
||||
import type {BundleOptions} from '../shared/types.flow';
|
||||
|
||||
export type DeltaResult = {|
|
||||
+modified: Map<string, Module>,
|
||||
+modified: Map<string, DependencyEdge>,
|
||||
+deleted: Set<string>,
|
||||
+reset: boolean,
|
||||
|};
|
||||
|
||||
import type {DependencyEdges} from './traverseDependencies';
|
||||
import type {DependencyEdge, DependencyEdges} from './traverseDependencies';
|
||||
|
||||
/**
|
||||
* This class is in charge of calculating the delta of changed modules that
|
||||
|
@ -181,7 +180,7 @@ class DeltaCalculator extends EventEmitter {
|
|||
new Map(),
|
||||
);
|
||||
|
||||
return [path, ...added];
|
||||
return Array.from(added.keys());
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -240,10 +239,6 @@ class DeltaCalculator extends EventEmitter {
|
|||
this._options.entryFile,
|
||||
);
|
||||
|
||||
const modified = new Map([
|
||||
[path, this._dependencyGraph.getModuleForPath(path)],
|
||||
]);
|
||||
|
||||
const {added} = await initialTraverseDependencies(
|
||||
path,
|
||||
this._dependencyGraph,
|
||||
|
@ -252,12 +247,8 @@ class DeltaCalculator extends EventEmitter {
|
|||
this._options.onProgress || undefined,
|
||||
);
|
||||
|
||||
for (const path of added) {
|
||||
modified.set(path, this._dependencyGraph.getModuleForPath(path));
|
||||
}
|
||||
|
||||
return {
|
||||
modified,
|
||||
modified: added,
|
||||
deleted: new Set(),
|
||||
reset: true,
|
||||
};
|
||||
|
@ -291,18 +282,8 @@ class DeltaCalculator extends EventEmitter {
|
|||
this._options.onProgress || undefined,
|
||||
);
|
||||
|
||||
const modified = new Map();
|
||||
|
||||
for (const path of modifiedDependencies) {
|
||||
modified.set(path, this._dependencyGraph.getModuleForPath(path));
|
||||
}
|
||||
|
||||
for (const path of added) {
|
||||
modified.set(path, this._dependencyGraph.getModuleForPath(path));
|
||||
}
|
||||
|
||||
return {
|
||||
modified,
|
||||
modified: added,
|
||||
deleted,
|
||||
reset: false,
|
||||
};
|
||||
|
|
|
@ -17,7 +17,6 @@ const createModuleIdFactory = require('../lib/createModuleIdFactory');
|
|||
const defaults = require('../defaults');
|
||||
const getPreludeCode = require('../lib/getPreludeCode');
|
||||
const nullthrows = require('fbjs/lib/nullthrows');
|
||||
const removeInlineRequiresBlacklistFromOptions = require('../lib/removeInlineRequiresBlacklistFromOptions');
|
||||
|
||||
const {EventEmitter} = require('events');
|
||||
|
||||
|
@ -26,7 +25,7 @@ import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
|
|||
import type DependencyGraph from '../node-haste/DependencyGraph';
|
||||
import type Module from '../node-haste/Module';
|
||||
import type {Options as BundleOptions, MainOptions} from './';
|
||||
import type {DependencyEdges} from './traverseDependencies';
|
||||
import type {DependencyEdge, DependencyEdges} from './traverseDependencies';
|
||||
import type {MetroSourceMapSegmentTuple} from 'metro-source-map';
|
||||
|
||||
export type DeltaEntryType =
|
||||
|
@ -342,8 +341,14 @@ class DeltaTransformer extends EventEmitter {
|
|||
}),
|
||||
);
|
||||
|
||||
const edges = await Promise.all(
|
||||
modules.map(module =>
|
||||
this._createEdgeFromScript(module, transformOptions),
|
||||
),
|
||||
);
|
||||
|
||||
const transformedModules = await this._transformModules(
|
||||
modules,
|
||||
edges,
|
||||
transformOptions,
|
||||
dependencyEdges,
|
||||
);
|
||||
|
@ -416,7 +421,7 @@ class DeltaTransformer extends EventEmitter {
|
|||
}
|
||||
|
||||
async _transformModules(
|
||||
modules: Array<Module>,
|
||||
modules: Array<DependencyEdge>,
|
||||
transformOptions: JSTransformerOptions,
|
||||
dependencyEdges: DependencyEdges,
|
||||
): Promise<DeltaEntries> {
|
||||
|
@ -429,15 +434,34 @@ class DeltaTransformer extends EventEmitter {
|
|||
);
|
||||
}
|
||||
|
||||
async _transformModule(
|
||||
async _createEdgeFromScript(
|
||||
module: Module,
|
||||
transformOptions: JSTransformerOptions,
|
||||
): Promise<DependencyEdge> {
|
||||
const result = await module.read(transformOptions);
|
||||
|
||||
const edge = {
|
||||
dependencies: new Map(),
|
||||
inverseDependencies: new Set(),
|
||||
path: module.path,
|
||||
output: {
|
||||
code: result.code,
|
||||
map: result.map,
|
||||
source: result.source,
|
||||
type: 'script',
|
||||
},
|
||||
};
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
async _transformModule(
|
||||
edge: DependencyEdge,
|
||||
transformOptions: JSTransformerOptions,
|
||||
dependencyEdges: DependencyEdges,
|
||||
): Promise<[number, ?DeltaEntry]> {
|
||||
const name = module.getName();
|
||||
const metadata = await this._getMetadata(module, transformOptions);
|
||||
const name = this._dependencyGraph.getHasteName(edge.path);
|
||||
|
||||
const edge = dependencyEdges.get(module.path);
|
||||
const dependencyPairs = edge ? edge.dependencies : new Map();
|
||||
|
||||
let wrappedCode;
|
||||
|
@ -445,26 +469,30 @@ class DeltaTransformer extends EventEmitter {
|
|||
// Get the absolute path of each of the module dependencies from the
|
||||
// dependency edges. The module dependencies ensure correct order, while
|
||||
// the dependency edges do not ensure the same order between rebuilds.
|
||||
const dependencies = metadata.dependencies.map(dependency =>
|
||||
const dependencies = Array.from(edge.dependencies.keys()).map(dependency =>
|
||||
nullthrows(dependencyPairs.get(dependency)),
|
||||
);
|
||||
|
||||
if (!module.isPolyfill()) {
|
||||
if (edge.output.type !== 'script') {
|
||||
wrappedCode = this._addDependencyMap({
|
||||
code: metadata.code,
|
||||
code: edge.output.code,
|
||||
dependencies,
|
||||
name,
|
||||
path: module.path,
|
||||
path: edge.path,
|
||||
});
|
||||
} else {
|
||||
wrappedCode = metadata.code;
|
||||
wrappedCode = edge.output.code;
|
||||
}
|
||||
|
||||
const {code, map} = transformOptions.minify
|
||||
? await this._bundler.minifyModule(module.path, wrappedCode, metadata.map)
|
||||
: {code: wrappedCode, map: metadata.map};
|
||||
? await this._bundler.minifyModule(
|
||||
edge.path,
|
||||
wrappedCode,
|
||||
edge.output.map,
|
||||
)
|
||||
: {code: wrappedCode, map: edge.output.map};
|
||||
|
||||
const id = this._getModuleId(module.path);
|
||||
const id = this._getModuleId(edge.path);
|
||||
|
||||
return [
|
||||
id,
|
||||
|
@ -473,9 +501,9 @@ class DeltaTransformer extends EventEmitter {
|
|||
id,
|
||||
map,
|
||||
name,
|
||||
source: metadata.source,
|
||||
path: module.path,
|
||||
type: this._getModuleType(module),
|
||||
source: edge.output.source,
|
||||
path: edge.path,
|
||||
type: edge.output.type,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -509,32 +537,6 @@ class DeltaTransformer extends EventEmitter {
|
|||
return addParamsToDefineCall(code, ...params);
|
||||
}
|
||||
|
||||
_getModuleType(module: Module): DeltaEntryType {
|
||||
if (module.isAsset()) {
|
||||
return 'asset';
|
||||
}
|
||||
|
||||
if (module.isPolyfill()) {
|
||||
return 'script';
|
||||
}
|
||||
|
||||
return 'module';
|
||||
}
|
||||
|
||||
async _getMetadata(
|
||||
module: Module,
|
||||
transformOptions: JSTransformerOptions,
|
||||
): Promise<{
|
||||
+code: string,
|
||||
+dependencies: Array<string>,
|
||||
+map: Array<MetroSourceMapSegmentTuple>,
|
||||
+source: string,
|
||||
}> {
|
||||
return await module.read(
|
||||
removeInlineRequiresBlacklistFromOptions(module.path, transformOptions),
|
||||
);
|
||||
}
|
||||
|
||||
_onFileChange = () => {
|
||||
this.emit('change');
|
||||
};
|
||||
|
|
|
@ -30,6 +30,11 @@ describe('DeltaCalculator', () => {
|
|||
const moduleBar = createModule({path: '/bar', name: 'bar'});
|
||||
const moduleBaz = createModule({path: '/baz', name: 'baz'});
|
||||
|
||||
let edgeModule;
|
||||
let edgeFoo;
|
||||
let edgeBar;
|
||||
let edgeBaz;
|
||||
|
||||
let deltaCalculator;
|
||||
let fileWatcher;
|
||||
let mockedDependencies;
|
||||
|
@ -85,13 +90,23 @@ describe('DeltaCalculator', () => {
|
|||
|
||||
initialTraverseDependencies.mockImplementationOnce(
|
||||
async (path, dg, opt, edges) => {
|
||||
edges.set('/bundle', entryModule);
|
||||
edges.set('/foo', {...moduleFoo, inverseDependencies: ['/bundle']});
|
||||
edges.set('/bar', {...moduleBar, inverseDependencies: ['/bundle']});
|
||||
edges.set('/baz', {...moduleBaz, inverseDependencies: ['/bundle']});
|
||||
edgeModule = {...entryModule};
|
||||
edgeFoo = {...moduleFoo, inverseDependencies: ['/bundle']};
|
||||
edgeBar = {...moduleBar, inverseDependencies: ['/bundle']};
|
||||
edgeBaz = {...moduleBaz, inverseDependencies: ['/bundle']};
|
||||
|
||||
edges.set('/bundle', edgeModule);
|
||||
edges.set('/foo', edgeFoo);
|
||||
edges.set('/bar', edgeBar);
|
||||
edges.set('/baz', edgeBaz);
|
||||
|
||||
return {
|
||||
added: new Set(['/bundle', '/foo', '/bar', '/baz']),
|
||||
added: new Map([
|
||||
['/bundle', edgeModule],
|
||||
['/foo', edgeFoo],
|
||||
['/bar', edgeBar],
|
||||
['/baz', edgeBaz],
|
||||
]),
|
||||
deleted: new Set(),
|
||||
};
|
||||
},
|
||||
|
@ -136,9 +151,9 @@ describe('DeltaCalculator', () => {
|
|||
expect(result).toEqual({
|
||||
modified: new Map([
|
||||
['/bundle', entryModule],
|
||||
['/foo', moduleFoo],
|
||||
['/bar', moduleBar],
|
||||
['/baz', moduleBaz],
|
||||
['/foo', edgeFoo],
|
||||
['/bar', edgeBar],
|
||||
['/baz', edgeBaz],
|
||||
]),
|
||||
deleted: new Set(),
|
||||
reset: true,
|
||||
|
@ -164,7 +179,7 @@ describe('DeltaCalculator', () => {
|
|||
|
||||
traverseDependencies.mockReturnValue(
|
||||
Promise.resolve({
|
||||
added: new Set(),
|
||||
added: new Map([['/foo', edgeFoo]]),
|
||||
deleted: new Set(),
|
||||
}),
|
||||
);
|
||||
|
@ -172,7 +187,7 @@ describe('DeltaCalculator', () => {
|
|||
const result = await deltaCalculator.getDelta();
|
||||
|
||||
expect(result).toEqual({
|
||||
modified: new Map([['/foo', moduleFoo]]),
|
||||
modified: new Map([['/foo', edgeFoo]]),
|
||||
deleted: new Set(),
|
||||
reset: false,
|
||||
});
|
||||
|
@ -188,7 +203,7 @@ describe('DeltaCalculator', () => {
|
|||
|
||||
traverseDependencies.mockReturnValue(
|
||||
Promise.resolve({
|
||||
added: new Set(),
|
||||
added: new Map([['/foo', edgeFoo]]),
|
||||
deleted: new Set(['/baz']),
|
||||
}),
|
||||
);
|
||||
|
@ -196,7 +211,7 @@ describe('DeltaCalculator', () => {
|
|||
const result = await deltaCalculator.getDelta();
|
||||
|
||||
expect(result).toEqual({
|
||||
modified: new Map([['/foo', moduleFoo]]),
|
||||
modified: new Map([['/foo', edgeFoo]]),
|
||||
deleted: new Set(['/baz']),
|
||||
reset: false,
|
||||
});
|
||||
|
@ -211,19 +226,22 @@ describe('DeltaCalculator', () => {
|
|||
fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]});
|
||||
|
||||
const moduleQux = createModule({path: '/qux', name: 'qux'});
|
||||
const edgeQux = {...moduleQux, inverseDependencies: []};
|
||||
|
||||
mockedDependencies.push(moduleQux);
|
||||
|
||||
traverseDependencies.mockReturnValue(
|
||||
Promise.resolve({
|
||||
added: new Set([moduleQux.path]),
|
||||
traverseDependencies.mockImplementation(async (path, dg, opt, edges) => {
|
||||
edges.set('/qux', edgeQux);
|
||||
|
||||
return {
|
||||
added: new Map([['/foo', edgeFoo], ['/qux', edgeQux]]),
|
||||
deleted: new Set(['/bar', '/baz']),
|
||||
}),
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const result = await deltaCalculator.getDelta();
|
||||
expect(result).toEqual({
|
||||
modified: new Map([['/foo', moduleFoo], ['/qux', moduleQux]]),
|
||||
modified: new Map([['/foo', edgeFoo], ['/qux', edgeQux]]),
|
||||
deleted: new Set(['/bar', '/baz']),
|
||||
reset: false,
|
||||
});
|
||||
|
@ -280,17 +298,18 @@ describe('DeltaCalculator', () => {
|
|||
|
||||
traverseDependencies.mockReturnValue(
|
||||
Promise.resolve({
|
||||
added: new Set(),
|
||||
added: new Map([['/bundle', edgeModule]]),
|
||||
deleted: new Set(['/foo']),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await deltaCalculator.getDelta()).toEqual({
|
||||
modified: new Map([['/bundle', entryModule]]),
|
||||
modified: new Map([['/bundle', edgeModule]]),
|
||||
deleted: new Set(['/foo']),
|
||||
reset: false,
|
||||
});
|
||||
|
||||
expect(traverseDependencies).toHaveBeenCalledTimes(1);
|
||||
expect(traverseDependencies.mock.calls[0][0]).toEqual(['/bundle']);
|
||||
});
|
||||
|
||||
|
|
|
@ -69,11 +69,11 @@ describe('traverseDependencies', function() {
|
|||
);
|
||||
|
||||
const dependencies = recursive
|
||||
? added
|
||||
? [...added.values()].map(edge => edge.path)
|
||||
: edges.get(entryPath).dependencies.values();
|
||||
|
||||
return await Promise.all(
|
||||
[entryPath, ...dependencies].map(async path => {
|
||||
[...dependencies].map(async path => {
|
||||
const dep = dgraph.getModuleForPath(path);
|
||||
const moduleDependencies = await dep.getDependencies();
|
||||
|
||||
|
@ -224,13 +224,6 @@ describe('traverseDependencies', function() {
|
|||
false,
|
||||
);
|
||||
expect(deps).toEqual([
|
||||
{
|
||||
id: 'index',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['a'],
|
||||
isAsset: false,
|
||||
isPolyfill: false,
|
||||
},
|
||||
{
|
||||
id: 'a',
|
||||
path: '/root/a.js',
|
||||
|
|
|
@ -31,16 +31,36 @@ function deferred(value) {
|
|||
return {promise, resolve: () => resolve(value)};
|
||||
}
|
||||
|
||||
function createModule({path, name, isAsset}) {
|
||||
function createModule({path, name, isAsset, isPolyfill}) {
|
||||
return {
|
||||
path,
|
||||
name,
|
||||
async getName() {
|
||||
return name;
|
||||
},
|
||||
isAsset() {
|
||||
return !!isAsset;
|
||||
},
|
||||
isPolyfill() {
|
||||
return !!isPolyfill;
|
||||
},
|
||||
async read() {
|
||||
const deps = mockedDependencyTree.get(path);
|
||||
const dependencies = deps ? deps.map(dep => dep.name) : [];
|
||||
|
||||
return {
|
||||
code: '// code',
|
||||
map: [],
|
||||
source: '// source',
|
||||
dependencies,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getPaths({added, deleted}) {
|
||||
const addedPaths = [...added.values()].map(edge => edge.path);
|
||||
|
||||
return {
|
||||
added: new Set(addedPaths),
|
||||
deleted,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -58,10 +78,6 @@ beforeEach(async () => {
|
|||
getModuleForPath(path) {
|
||||
return Array.from(mockedDependencies).find(dep => dep.path === path);
|
||||
},
|
||||
async getShallowDependencies(path) {
|
||||
const deps = mockedDependencyTree.get(path);
|
||||
return deps ? await Promise.all(deps.map(dep => dep.getName())) : [];
|
||||
},
|
||||
resolveDependency(module, relativePath) {
|
||||
const deps = mockedDependencyTree.get(module.path);
|
||||
const dependency = deps.filter(dep => dep.name === relativePath)[0];
|
||||
|
@ -83,8 +99,8 @@ it('should do the initial traversal correctly', async () => {
|
|||
edges,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
added: new Set(['/foo', '/bar', '/baz']),
|
||||
expect(getPaths(result)).toEqual({
|
||||
added: new Set(['/bundle', '/foo', '/bar', '/baz']),
|
||||
deleted: new Set(),
|
||||
});
|
||||
});
|
||||
|
@ -94,9 +110,11 @@ it('should return an empty result when there are no changes', async () => {
|
|||
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||
|
||||
expect(
|
||||
await traverseDependencies(['/bundle'], dependencyGraph, {}, edges),
|
||||
getPaths(
|
||||
await traverseDependencies(['/bundle'], dependencyGraph, {}, edges),
|
||||
),
|
||||
).toEqual({
|
||||
added: new Set(),
|
||||
added: new Set(['/bundle']),
|
||||
deleted: new Set(),
|
||||
});
|
||||
});
|
||||
|
@ -109,9 +127,9 @@ it('should return a removed dependency', async () => {
|
|||
mockedDependencyTree.set(moduleFoo.path, [moduleBaz]);
|
||||
|
||||
expect(
|
||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||
getPaths(await traverseDependencies(['/foo'], dependencyGraph, {}, edges)),
|
||||
).toEqual({
|
||||
added: new Set(),
|
||||
added: new Set(['/foo']),
|
||||
deleted: new Set(['/bar']),
|
||||
});
|
||||
});
|
||||
|
@ -126,9 +144,9 @@ it('should return added/removed dependencies', async () => {
|
|||
mockedDependencies.add(moduleQux);
|
||||
|
||||
expect(
|
||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||
getPaths(await traverseDependencies(['/foo'], dependencyGraph, {}, edges)),
|
||||
).toEqual({
|
||||
added: new Set(['/qux']),
|
||||
added: new Set(['/foo', '/qux']),
|
||||
deleted: new Set(['/bar', '/baz']),
|
||||
});
|
||||
});
|
||||
|
@ -159,19 +177,20 @@ describe('edge cases', () => {
|
|||
const moduleQux = createModule({path: '/qux', name: 'qux'});
|
||||
mockedDependencyTree.set(moduleFoo.path, [moduleQux, moduleBar]);
|
||||
mockedDependencies.add(moduleQux);
|
||||
mockedDependencies.delete(moduleBaz);
|
||||
|
||||
// Call traverseDependencies with /foo, /qux and /baz, simulating that the
|
||||
// user has modified the 3 files.
|
||||
expect(
|
||||
await traverseDependencies(
|
||||
['/foo', '/qux', '/baz'],
|
||||
dependencyGraph,
|
||||
{},
|
||||
edges,
|
||||
getPaths(
|
||||
await traverseDependencies(
|
||||
['/foo', '/qux', '/baz'],
|
||||
dependencyGraph,
|
||||
{},
|
||||
edges,
|
||||
),
|
||||
),
|
||||
).toEqual({
|
||||
added: new Set(['/qux']),
|
||||
added: new Set(['/foo', '/qux']),
|
||||
deleted: new Set(['/baz']),
|
||||
});
|
||||
});
|
||||
|
@ -193,9 +212,11 @@ describe('edge cases', () => {
|
|||
// Call traverseDependencies with /foo, /qux and /baz, simulating that the
|
||||
// user has modified the 3 files.
|
||||
expect(
|
||||
await traverseDependencies(['/bundle'], dependencyGraph, {}, edges),
|
||||
getPaths(
|
||||
await traverseDependencies(['/bundle'], dependencyGraph, {}, edges),
|
||||
),
|
||||
).toEqual({
|
||||
added: new Set(['/foo-renamed']),
|
||||
added: new Set(['/bundle', '/foo-renamed']),
|
||||
deleted: new Set(['/foo']),
|
||||
});
|
||||
});
|
||||
|
@ -205,13 +226,19 @@ describe('edge cases', () => {
|
|||
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||
|
||||
mockedDependencyTree.set(moduleFoo.path, [moduleBar]);
|
||||
mockedDependencies.delete(moduleBaz);
|
||||
|
||||
// Modify /baz, rename it to /qux and modify it again.
|
||||
expect(
|
||||
await traverseDependencies(['/baz', '/foo'], dependencyGraph, {}, edges),
|
||||
getPaths(
|
||||
await traverseDependencies(
|
||||
['/baz', '/foo'],
|
||||
dependencyGraph,
|
||||
{},
|
||||
edges,
|
||||
),
|
||||
),
|
||||
).toEqual({
|
||||
added: new Set(),
|
||||
added: new Set(['/foo']),
|
||||
deleted: new Set(['/baz']),
|
||||
});
|
||||
});
|
||||
|
@ -227,9 +254,11 @@ describe('edge cases', () => {
|
|||
|
||||
// Modify /baz, rename it to /qux and modify it again.
|
||||
expect(
|
||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||
getPaths(
|
||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||
),
|
||||
).toEqual({
|
||||
added: new Set(['/baz-moved']),
|
||||
added: new Set(['/foo', '/baz-moved']),
|
||||
deleted: new Set(['/baz']),
|
||||
});
|
||||
});
|
||||
|
@ -273,14 +302,16 @@ describe('edge cases', () => {
|
|||
async function assertOrder() {
|
||||
expect(
|
||||
Array.from(
|
||||
(await initialTraverseDependencies(
|
||||
'/bundle',
|
||||
dependencyGraph,
|
||||
{},
|
||||
new Map(),
|
||||
)).added,
|
||||
getPaths(
|
||||
await initialTraverseDependencies(
|
||||
'/bundle',
|
||||
dependencyGraph,
|
||||
{},
|
||||
new Map(),
|
||||
),
|
||||
).added,
|
||||
),
|
||||
).toEqual(['/foo', '/baz', '/bar']);
|
||||
).toEqual(['/bundle', '/foo', '/baz', '/bar']);
|
||||
}
|
||||
|
||||
// Create a dependency tree where moduleBaz has two inverse dependencies.
|
||||
|
@ -300,7 +331,10 @@ describe('edge cases', () => {
|
|||
});
|
||||
|
||||
it('should simplify inlineRequires transform option', async () => {
|
||||
jest.spyOn(dependencyGraph, 'getShallowDependencies');
|
||||
jest.spyOn(entryModule, 'read');
|
||||
jest.spyOn(moduleFoo, 'read');
|
||||
jest.spyOn(moduleBar, 'read');
|
||||
jest.spyOn(moduleBaz, 'read');
|
||||
|
||||
const edges = new Map();
|
||||
const transformOptions = {
|
||||
|
@ -318,14 +352,12 @@ describe('edge cases', () => {
|
|||
edges,
|
||||
);
|
||||
|
||||
expect(dependencyGraph.getShallowDependencies.mock.calls).toEqual([
|
||||
['/bundle', {inlineRequires: true}],
|
||||
['/foo', {inlineRequires: true}],
|
||||
['/bar', {inlineRequires: true}],
|
||||
['/baz', {inlineRequires: false}],
|
||||
]);
|
||||
expect(entryModule.read).toHaveBeenCalledWith({inlineRequires: true});
|
||||
expect(moduleFoo.read).toHaveBeenCalledWith({inlineRequires: true});
|
||||
expect(moduleBar.read).toHaveBeenCalledWith({inlineRequires: true});
|
||||
expect(moduleBaz.read).toHaveBeenCalledWith({inlineRequires: false});
|
||||
|
||||
dependencyGraph.getShallowDependencies.mockClear();
|
||||
moduleFoo.read.mockClear();
|
||||
|
||||
await traverseDependencies(
|
||||
['/foo'],
|
||||
|
@ -334,8 +366,6 @@ describe('edge cases', () => {
|
|||
edges,
|
||||
);
|
||||
|
||||
expect(dependencyGraph.getShallowDependencies.mock.calls).toEqual([
|
||||
['/foo', {inlineRequires: true}],
|
||||
]);
|
||||
expect(moduleFoo.read).toHaveBeenCalledWith({inlineRequires: true});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,16 +14,26 @@ const removeInlineRequiresBlacklistFromOptions = require('../lib/removeInlineReq
|
|||
|
||||
import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
|
||||
import type DependencyGraph from '../node-haste/DependencyGraph';
|
||||
import type Module from '../node-haste/Module';
|
||||
import type {MetroSourceMapSegmentTuple} from 'metro-source-map';
|
||||
|
||||
export type DependencyType = 'module' | 'script' | 'asset';
|
||||
|
||||
export type DependencyEdge = {|
|
||||
dependencies: Map<string, string>,
|
||||
inverseDependencies: Set<string>,
|
||||
path: string,
|
||||
output: {
|
||||
code: string,
|
||||
map: Array<MetroSourceMapSegmentTuple>,
|
||||
source: string,
|
||||
type: DependencyType,
|
||||
},
|
||||
|};
|
||||
|
||||
export type DependencyEdges = Map<string, DependencyEdge>;
|
||||
|
||||
type Result = {added: Set<string>, deleted: Set<string>};
|
||||
type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>};
|
||||
|
||||
/**
|
||||
* Dependency Traversal logic for the Delta Bundler. This method calculates
|
||||
|
@ -58,12 +68,12 @@ async function traverseDependencies(
|
|||
),
|
||||
);
|
||||
|
||||
const added = new Set();
|
||||
const added = new Map();
|
||||
const deleted = new Set();
|
||||
|
||||
for (const change of changes) {
|
||||
for (const path of change.added) {
|
||||
added.add(path);
|
||||
for (const [path, edge] of change.added) {
|
||||
added.set(path, edge);
|
||||
}
|
||||
for (const path of change.deleted) {
|
||||
// If a path has been marked both as added and deleted, it means that this
|
||||
|
@ -90,8 +100,8 @@ async function initialTraverseDependencies(
|
|||
transformOptions: JSTransformerOptions,
|
||||
edges: DependencyEdges,
|
||||
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||
) {
|
||||
createEdge(path, edges);
|
||||
): Promise<Result> {
|
||||
createEdge(dependencyGraph.getModuleForPath(path), edges);
|
||||
|
||||
return await traverseDependenciesForSingleFile(
|
||||
path,
|
||||
|
@ -113,13 +123,18 @@ async function traverseDependenciesForSingleFile(
|
|||
|
||||
// If the passed edge does not exist does not exist in the graph, ignore it.
|
||||
if (!edge) {
|
||||
return {added: new Set(), deleted: new Set()};
|
||||
return {added: new Map(), deleted: new Set()};
|
||||
}
|
||||
|
||||
const shallow = await dependencyGraph.getShallowDependencies(
|
||||
path,
|
||||
removeInlineRequiresBlacklistFromOptions(path, transformOptions),
|
||||
);
|
||||
const result = await dependencyGraph
|
||||
.getModuleForPath(path)
|
||||
.read(removeInlineRequiresBlacklistFromOptions(path, transformOptions));
|
||||
|
||||
edge.output.code = result.code;
|
||||
edge.output.map = result.map;
|
||||
edge.output.source = result.source;
|
||||
|
||||
const shallow = result.dependencies;
|
||||
|
||||
// Get the absolute path of all sub-dependencies (some of them could have been
|
||||
// moved but maintain the same relative path).
|
||||
|
@ -132,8 +147,6 @@ async function traverseDependenciesForSingleFile(
|
|||
|
||||
const previousDependencies = new Set(edge.dependencies.values());
|
||||
|
||||
const nonNullEdge = edge;
|
||||
|
||||
let numProcessed = 0;
|
||||
let total = 1;
|
||||
onProgress(numProcessed, total);
|
||||
|
@ -141,7 +154,7 @@ async function traverseDependenciesForSingleFile(
|
|||
const deleted = Array.from(edge.dependencies.entries())
|
||||
.map(([relativePath, absolutePath]) => {
|
||||
if (!currentDependencies.has(absolutePath)) {
|
||||
return removeDependency(nonNullEdge, relativePath, edges);
|
||||
return removeDependency(edge, relativePath, edges);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -151,15 +164,15 @@ async function traverseDependenciesForSingleFile(
|
|||
// Check all the module dependencies and start traversing the tree from each
|
||||
// added and removed dependency, to get all the modules that have to be added
|
||||
// and removed from the dependency graph.
|
||||
const added = await Promise.all(
|
||||
const addedDependencies = await Promise.all(
|
||||
Array.from(currentDependencies).map(
|
||||
async ([absolutePath, relativePath]) => {
|
||||
if (previousDependencies.has(absolutePath)) {
|
||||
return new Set();
|
||||
return new Map();
|
||||
}
|
||||
|
||||
return await addDependency(
|
||||
nonNullEdge,
|
||||
edge,
|
||||
relativePath,
|
||||
dependencyGraph,
|
||||
transformOptions,
|
||||
|
@ -177,11 +190,13 @@ async function traverseDependenciesForSingleFile(
|
|||
),
|
||||
);
|
||||
|
||||
const added = [new Map([[edge.path, edge]])].concat(addedDependencies);
|
||||
|
||||
numProcessed++;
|
||||
onProgress(numProcessed, total);
|
||||
|
||||
return {
|
||||
added: flatten(reorderDependencies(added, edges)),
|
||||
added: flattenMap(reorderDependencies(added, edges)),
|
||||
deleted: flatten(deleted),
|
||||
};
|
||||
}
|
||||
|
@ -194,7 +209,7 @@ async function addDependency(
|
|||
edges: DependencyEdges,
|
||||
onDependencyAdd: () => mixed,
|
||||
onDependencyAdded: () => mixed,
|
||||
): Promise<Set<string>> {
|
||||
): Promise<Map<string, DependencyEdge>> {
|
||||
const parentModule = dependencyGraph.getModuleForPath(parentEdge.path);
|
||||
const module = dependencyGraph.resolveDependency(
|
||||
parentModule,
|
||||
|
@ -205,35 +220,36 @@ async function addDependency(
|
|||
// Update the parent edge to keep track of the new dependency.
|
||||
parentEdge.dependencies.set(relativePath, module.path);
|
||||
|
||||
let dependencyEdge = edges.get(module.path);
|
||||
const existingEdge = edges.get(module.path);
|
||||
|
||||
// The new dependency was already in the graph, we don't need to do anything.
|
||||
if (dependencyEdge) {
|
||||
dependencyEdge.inverseDependencies.add(parentEdge.path);
|
||||
if (existingEdge) {
|
||||
existingEdge.inverseDependencies.add(parentEdge.path);
|
||||
|
||||
return new Set();
|
||||
return new Map();
|
||||
}
|
||||
|
||||
onDependencyAdd();
|
||||
|
||||
// Create the new edge and traverse all its subdependencies, looking for new
|
||||
// subdependencies recursively.
|
||||
dependencyEdge = createEdge(module.path, edges);
|
||||
dependencyEdge.inverseDependencies.add(parentEdge.path);
|
||||
const edge = createEdge(module, edges);
|
||||
edge.inverseDependencies.add(parentEdge.path);
|
||||
|
||||
const addedDependencies = new Set([dependencyEdge.path]);
|
||||
const addedDependencies = new Map([[edge.path, edge]]);
|
||||
|
||||
const shallowDeps = await dependencyGraph.getShallowDependencies(
|
||||
dependencyEdge.path,
|
||||
removeInlineRequiresBlacklistFromOptions(module.path, transformOptions),
|
||||
const result = await module.read(
|
||||
removeInlineRequiresBlacklistFromOptions(edge.path, transformOptions),
|
||||
);
|
||||
|
||||
const nonNullDependencyEdge = dependencyEdge;
|
||||
edge.output.code = result.code;
|
||||
edge.output.map = result.map;
|
||||
edge.output.source = result.source;
|
||||
|
||||
const added = await Promise.all(
|
||||
shallowDeps.map(dep =>
|
||||
result.dependencies.map(dep =>
|
||||
addDependency(
|
||||
nonNullDependencyEdge,
|
||||
edge,
|
||||
dep,
|
||||
dependencyGraph,
|
||||
transformOptions,
|
||||
|
@ -244,8 +260,8 @@ async function addDependency(
|
|||
),
|
||||
);
|
||||
|
||||
for (const newDependency of flatten(added)) {
|
||||
addedDependencies.add(newDependency);
|
||||
for (const [newDepPath, newDepEdge] of flattenMap(added)) {
|
||||
addedDependencies.set(newDepPath, newDepEdge);
|
||||
}
|
||||
|
||||
onDependencyAdded();
|
||||
|
@ -294,17 +310,35 @@ function removeDependency(
|
|||
return removedDependencies;
|
||||
}
|
||||
|
||||
function createEdge(path: string, edges: DependencyEdges): DependencyEdge {
|
||||
function createEdge(module: Module, edges: DependencyEdges): DependencyEdge {
|
||||
const edge = {
|
||||
dependencies: new Map(),
|
||||
inverseDependencies: new Set(),
|
||||
path,
|
||||
path: module.path,
|
||||
output: {
|
||||
code: '',
|
||||
map: [],
|
||||
source: '',
|
||||
type: getType(module),
|
||||
},
|
||||
};
|
||||
edges.set(path, edge);
|
||||
edges.set(module.path, edge);
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
function getType(module: Module): DependencyType {
|
||||
if (module.isAsset()) {
|
||||
return 'asset';
|
||||
}
|
||||
|
||||
if (module.isPolyfill()) {
|
||||
return 'script';
|
||||
}
|
||||
|
||||
return 'module';
|
||||
}
|
||||
|
||||
function destroyEdge(edge: DependencyEdge, edges: DependencyEdges) {
|
||||
edges.delete(edge.path);
|
||||
}
|
||||
|
@ -347,29 +381,36 @@ function resolveDependencies(
|
|||
* guarantee the same order between runs.
|
||||
*/
|
||||
function reorderDependencies(
|
||||
dependencies: Array<Set<string>>,
|
||||
dependencies: Array<Map<string, DependencyEdge>>,
|
||||
edges: DependencyEdges,
|
||||
): Array<Set<string>> {
|
||||
const flatDependencies = flatten(dependencies);
|
||||
): Array<Map<string, DependencyEdge>> {
|
||||
const flatDependencies = flattenMap(dependencies);
|
||||
|
||||
return dependencies.map(dependencies =>
|
||||
reorderDependency(Array.from(dependencies)[0], flatDependencies, edges),
|
||||
);
|
||||
return dependencies.map(dependencies => {
|
||||
if (dependencies.size === 0) {
|
||||
return new Map();
|
||||
}
|
||||
return reorderDependency(
|
||||
Array.from(dependencies)[0][0],
|
||||
flatDependencies,
|
||||
edges,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function reorderDependency(
|
||||
path: string,
|
||||
dependencies: Set<string>,
|
||||
dependencies: Map<string, DependencyEdge>,
|
||||
edges: DependencyEdges,
|
||||
orderedDependencies?: Set<string> = new Set(),
|
||||
): Set<string> {
|
||||
orderedDependencies?: Map<string, DependencyEdge> = new Map(),
|
||||
): Map<string, DependencyEdge> {
|
||||
const edge = edges.get(path);
|
||||
|
||||
if (!edge || !dependencies.has(path) || orderedDependencies.has(path)) {
|
||||
return orderedDependencies;
|
||||
}
|
||||
|
||||
orderedDependencies.add(path);
|
||||
orderedDependencies.set(path, edge);
|
||||
|
||||
edge.dependencies.forEach(path =>
|
||||
reorderDependency(path, dependencies, edges, orderedDependencies),
|
||||
|
@ -390,6 +431,18 @@ function flatten<T>(input: Iterable<Iterable<T>>): Set<T> {
|
|||
return output;
|
||||
}
|
||||
|
||||
function flattenMap<K, V>(input: Iterable<Map<K, V>>): Map<K, V> {
|
||||
const output = new Map();
|
||||
|
||||
for (const items of input) {
|
||||
for (const [key, value] of items.entries()) {
|
||||
output.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initialTraverseDependencies,
|
||||
traverseDependencies,
|
||||
|
|
|
@ -22,6 +22,7 @@ const fs = require('fs');
|
|||
const isAbsolutePath = require('absolute-path');
|
||||
const parsePlatformFilePath = require('./lib/parsePlatformFilePath');
|
||||
const path = require('path');
|
||||
const toLocalPath = require('../node-haste/lib/toLocalPath');
|
||||
const util = require('util');
|
||||
|
||||
const {ModuleResolver} = require('./DependencyGraph/ModuleResolution');
|
||||
|
@ -274,6 +275,16 @@ class DependencyGraph extends EventEmitter {
|
|||
return platform;
|
||||
}
|
||||
|
||||
getHasteName(filePath: string): string {
|
||||
const hasteName = this._hasteFS.getModuleName(filePath);
|
||||
|
||||
if (hasteName) {
|
||||
return hasteName;
|
||||
}
|
||||
|
||||
return toLocalPath(this._opts.projectRoots, filePath);
|
||||
}
|
||||
|
||||
getAbsolutePath(filePath: string) {
|
||||
if (isAbsolutePath(filePath)) {
|
||||
return path.resolve(filePath);
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
export type HasteFS = {
|
||||
exists(filePath: string): boolean,
|
||||
getAllFiles(): Array<string>,
|
||||
getModuleName(filePath: string): ?string,
|
||||
matchFiles(pattern: RegExp | string): Array<string>,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue