mirror of https://github.com/status-im/metro.git
Add module for incrementally traversing the dependencies
Reviewed By: davidaurelio Differential Revision: D5880827 fbshipit-source-id: 9500c54c2f93726449a413321b9e4ef46825b977
This commit is contained in:
parent
34b108b37e
commit
7e65f2f1ea
|
@ -0,0 +1,192 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @emails oncall+javascript_foundation
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {
|
||||||
|
initialTraverseDependencies,
|
||||||
|
traverseDependencies,
|
||||||
|
} = require('../traverseDependencies');
|
||||||
|
|
||||||
|
const entryModule = createModule({path: '/bundle', name: 'bundle'});
|
||||||
|
const moduleFoo = createModule({path: '/foo', name: 'foo'});
|
||||||
|
const moduleBar = createModule({path: '/bar', name: 'bar'});
|
||||||
|
const moduleBaz = createModule({path: '/baz', name: 'baz'});
|
||||||
|
|
||||||
|
let dependencyGraph;
|
||||||
|
let mockedDependencies;
|
||||||
|
let mockedDependencyTree;
|
||||||
|
|
||||||
|
function createModule({path, name, isAsset, isJSON}) {
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
async getName() {
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
isAsset() {
|
||||||
|
return !!isAsset;
|
||||||
|
},
|
||||||
|
isJSON() {
|
||||||
|
return !!isAsset;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockedDependencies = new Set([entryModule, moduleFoo, moduleBar, moduleBaz]);
|
||||||
|
mockedDependencyTree = new Map([
|
||||||
|
[entryModule.path, [moduleFoo]],
|
||||||
|
[moduleFoo.path, [moduleBar, moduleBaz]],
|
||||||
|
]);
|
||||||
|
|
||||||
|
dependencyGraph = {
|
||||||
|
getAbsolutePath(path) {
|
||||||
|
return '/' + path;
|
||||||
|
},
|
||||||
|
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];
|
||||||
|
|
||||||
|
if (!mockedDependencies.has(dependency)) {
|
||||||
|
throw new Error('Dependency not found');
|
||||||
|
}
|
||||||
|
return dependency;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do the initial traversal correctly', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
const result = await initialTraverseDependencies(
|
||||||
|
'/bundle',
|
||||||
|
dependencyGraph,
|
||||||
|
{},
|
||||||
|
edges,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
added: new Set(['/foo', '/bar', '/baz']),
|
||||||
|
deleted: new Set(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty result when there are no changes', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await traverseDependencies(['/bundle'], dependencyGraph, {}, edges),
|
||||||
|
).toEqual({
|
||||||
|
added: new Set(),
|
||||||
|
deleted: new Set(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a removed dependency', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||||
|
|
||||||
|
// Remove moduleBar
|
||||||
|
mockedDependencyTree.set(moduleFoo.path, [moduleBaz]);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||||
|
).toEqual({
|
||||||
|
added: new Set(),
|
||||||
|
deleted: new Set(['/bar']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return added/removed dependencies', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||||
|
|
||||||
|
// Add moduleQux
|
||||||
|
const moduleQux = createModule({path: '/qux', name: 'qux'});
|
||||||
|
mockedDependencyTree.set(moduleFoo.path, [moduleQux]);
|
||||||
|
mockedDependencies.add(moduleQux);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||||
|
).toEqual({
|
||||||
|
added: new Set(['/qux']),
|
||||||
|
deleted: new Set(['/bar', '/baz']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retry to traverse the dependencies as it was after getting an error', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||||
|
|
||||||
|
mockedDependencies.delete(moduleBar);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||||
|
).rejects.toBeInstanceOf(Error);
|
||||||
|
|
||||||
|
// Second time that the traversal of dependencies we still have to throw an
|
||||||
|
// error (no matter if no file has been changed).
|
||||||
|
await expect(
|
||||||
|
traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
||||||
|
).rejects.toBeInstanceOf(Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('edge cases', () => {
|
||||||
|
it('should handle renames correctly', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
|
||||||
|
|
||||||
|
// Change the dependencies of /path, removing /baz and adding /qux.
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
added: new Set(['/qux']),
|
||||||
|
deleted: new Set(['/baz']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modify a file and delete it afterwards', async () => {
|
||||||
|
const edges = new Map();
|
||||||
|
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),
|
||||||
|
).toEqual({
|
||||||
|
added: new Set(),
|
||||||
|
deleted: new Set(['/baz']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,346 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
|
||||||
|
import type DependencyGraph from '../node-haste/DependencyGraph';
|
||||||
|
import type {
|
||||||
|
InverseDependencies,
|
||||||
|
ModulePaths,
|
||||||
|
ShallowDependencies,
|
||||||
|
} from './DeltaCalculator';
|
||||||
|
|
||||||
|
export type Caches = {|
|
||||||
|
inverseDependencies: InverseDependencies,
|
||||||
|
modules: ModulePaths,
|
||||||
|
shallowDependencies: ShallowDependencies,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export type DependencyEdge = {|
|
||||||
|
dependencies: Map<string, string>,
|
||||||
|
inverseDependencies: Set<string>,
|
||||||
|
path: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export type DependencyEdges = Map<string, DependencyEdge>;
|
||||||
|
|
||||||
|
type Result = {added: Set<string>, deleted: Set<string>};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency Traversal logic for the Delta Bundler. This method calculates
|
||||||
|
* the modules that should be included in the bundle by traversing the
|
||||||
|
* dependency graph.
|
||||||
|
* Instead of traversing the whole graph each time, it just calculates the
|
||||||
|
* difference between runs by only traversing the added/removed dependencies.
|
||||||
|
* To do so, it uses the passed `edges` paramater, which is a data structure
|
||||||
|
* that contains the whole status of the dependency graph. During the
|
||||||
|
* recalculation of the dependencies, it mutates the edges graph.
|
||||||
|
*
|
||||||
|
* The paths parameter contains the absolute paths of the root files that the
|
||||||
|
* method should traverse. Normally, these paths should be the modified files
|
||||||
|
* since the last traversal.
|
||||||
|
*/
|
||||||
|
async function traverseDependencies(
|
||||||
|
paths: Array<string>,
|
||||||
|
dependencyGraph: DependencyGraph,
|
||||||
|
transformOptions: JSTransformerOptions,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
|
): Promise<Result> {
|
||||||
|
const changes = await Promise.all(
|
||||||
|
paths.map(path =>
|
||||||
|
traverseDependenciesForSingleFile(
|
||||||
|
path,
|
||||||
|
dependencyGraph,
|
||||||
|
transformOptions,
|
||||||
|
edges,
|
||||||
|
onProgress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const added = new Set();
|
||||||
|
const deleted = new Set();
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
for (const path of change.added) {
|
||||||
|
added.add(path);
|
||||||
|
}
|
||||||
|
for (const path of change.deleted) {
|
||||||
|
deleted.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
added,
|
||||||
|
deleted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initialTraverseDependencies(
|
||||||
|
path: string,
|
||||||
|
dependencyGraph: DependencyGraph,
|
||||||
|
transformOptions: JSTransformerOptions,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
|
) {
|
||||||
|
createEdge(path, edges);
|
||||||
|
|
||||||
|
return await traverseDependenciesForSingleFile(
|
||||||
|
path,
|
||||||
|
dependencyGraph,
|
||||||
|
transformOptions,
|
||||||
|
edges,
|
||||||
|
onProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function traverseDependenciesForSingleFile(
|
||||||
|
path: string,
|
||||||
|
dependencyGraph: DependencyGraph,
|
||||||
|
transformOptions: JSTransformerOptions,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
|
): Promise<Result> {
|
||||||
|
const edge = edges.get(path);
|
||||||
|
|
||||||
|
// If the passed edge does not exist does not exist in the graph, ignore it.
|
||||||
|
if (!edge) {
|
||||||
|
return {added: new Set(), deleted: new Set()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDependencies = new Set(
|
||||||
|
await dependencyGraph.getShallowDependencies(path, transformOptions),
|
||||||
|
);
|
||||||
|
const previousDependencies = new Set(edge.dependencies.keys());
|
||||||
|
|
||||||
|
const nonNullEdge = edge;
|
||||||
|
|
||||||
|
let numProcessed = 0;
|
||||||
|
let total = currentDependencies.size;
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
Array.from(currentDependencies).map(async dependency => {
|
||||||
|
let newDependencies;
|
||||||
|
|
||||||
|
if (!previousDependencies.has(dependency)) {
|
||||||
|
newDependencies = await addDependency(
|
||||||
|
nonNullEdge,
|
||||||
|
dependency,
|
||||||
|
dependencyGraph,
|
||||||
|
transformOptions,
|
||||||
|
edges,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
newDependencies = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
numProcessed += newDependencies.size + 1;
|
||||||
|
total += newDependencies.size;
|
||||||
|
onProgress(numProcessed, total);
|
||||||
|
|
||||||
|
return newDependencies;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if all currentDependencies are still in the bundle (some files can
|
||||||
|
// have been removed).
|
||||||
|
checkModuleDependencies(
|
||||||
|
path,
|
||||||
|
currentDependencies,
|
||||||
|
dependencyGraph,
|
||||||
|
transformOptions,
|
||||||
|
edges,
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleted = Array.from(previousDependencies)
|
||||||
|
.map(dependency => {
|
||||||
|
if (!currentDependencies.has(dependency)) {
|
||||||
|
return removeDependency(nonNullEdge, dependency, edges);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
added: flatten(added),
|
||||||
|
deleted: flatten(deleted),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addDependency(
|
||||||
|
parentEdge: DependencyEdge,
|
||||||
|
relativePath: string,
|
||||||
|
dependencyGraph: DependencyGraph,
|
||||||
|
transformOptions: JSTransformerOptions,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
): Promise<Set<string>> {
|
||||||
|
const parentModule = dependencyGraph.getModuleForPath(parentEdge.path);
|
||||||
|
const module = dependencyGraph.resolveDependency(
|
||||||
|
parentModule,
|
||||||
|
relativePath,
|
||||||
|
transformOptions.platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the parent edge to keep track of the new dependency.
|
||||||
|
parentEdge.dependencies.set(relativePath, module.path);
|
||||||
|
|
||||||
|
let dependencyEdge = 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);
|
||||||
|
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 addedDependencies = new Set([dependencyEdge.path]);
|
||||||
|
|
||||||
|
const shallowDeps = await dependencyGraph.getShallowDependencies(
|
||||||
|
dependencyEdge.path,
|
||||||
|
transformOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
const nonNullDependencyEdge = dependencyEdge;
|
||||||
|
|
||||||
|
const added = await Promise.all(
|
||||||
|
shallowDeps.map(dep =>
|
||||||
|
addDependency(
|
||||||
|
nonNullDependencyEdge,
|
||||||
|
dep,
|
||||||
|
dependencyGraph,
|
||||||
|
transformOptions,
|
||||||
|
edges,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const newDependency of flatten(added)) {
|
||||||
|
addedDependencies.add(newDependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addedDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDependency(
|
||||||
|
parentEdge: DependencyEdge,
|
||||||
|
relativePath: string,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
): Set<string> {
|
||||||
|
// Find the actual edge that represents the removed dependency. We do this
|
||||||
|
// from the egdes data structure, since the file may have been deleted
|
||||||
|
// already.
|
||||||
|
const dependencyEdge = resolveEdge(parentEdge, relativePath, edges);
|
||||||
|
if (!dependencyEdge) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
parentEdge.dependencies.delete(relativePath);
|
||||||
|
dependencyEdge.inverseDependencies.delete(parentEdge.path);
|
||||||
|
|
||||||
|
// This module is still used by another modules, so we cannot remove it from
|
||||||
|
// the bundle.
|
||||||
|
if (dependencyEdge.inverseDependencies.size) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
const removedDependencies = new Set([dependencyEdge.path]);
|
||||||
|
|
||||||
|
// Now we need to iterate through the module dependencies in order to
|
||||||
|
// clean up everything (we cannot read the module because it may have
|
||||||
|
// been deleted).
|
||||||
|
for (const subDependency of dependencyEdge.dependencies.keys()) {
|
||||||
|
const removed = removeDependency(dependencyEdge, subDependency, edges);
|
||||||
|
|
||||||
|
for (const removedDependency of removed.values()) {
|
||||||
|
removedDependencies.add(removedDependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This module is not used anywhere else!! we can clear it from the bundle
|
||||||
|
destroyEdge(dependencyEdge, edges);
|
||||||
|
|
||||||
|
return removedDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEdge(path: string, edges: DependencyEdges): DependencyEdge {
|
||||||
|
const edge = {
|
||||||
|
dependencies: new Map(),
|
||||||
|
inverseDependencies: new Set(),
|
||||||
|
path,
|
||||||
|
};
|
||||||
|
edges.set(path, edge);
|
||||||
|
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyEdge(edge: DependencyEdge, edges: DependencyEdges) {
|
||||||
|
edges.delete(edge.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveEdge(
|
||||||
|
parentEdge: DependencyEdge,
|
||||||
|
relativePath: string,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
): ?DependencyEdge {
|
||||||
|
const absolutePath = parentEdge.dependencies.get(relativePath);
|
||||||
|
if (!absolutePath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges.get(absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkModuleDependencies(
|
||||||
|
parentPath,
|
||||||
|
dependencies: Set<string>,
|
||||||
|
dependencyGraph: DependencyGraph,
|
||||||
|
transformOptions: JSTransformerOptions,
|
||||||
|
edges: DependencyEdges,
|
||||||
|
) {
|
||||||
|
const parentModule = dependencyGraph.getModuleForPath(parentPath);
|
||||||
|
|
||||||
|
for (const dependency of dependencies.values()) {
|
||||||
|
dependencyGraph.resolveDependency(
|
||||||
|
parentModule,
|
||||||
|
dependency,
|
||||||
|
transformOptions.platform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatten<T>(input: Iterable<Iterable<T>>): Set<T> {
|
||||||
|
const output = new Set();
|
||||||
|
|
||||||
|
for (const items of input) {
|
||||||
|
for (const item of items) {
|
||||||
|
output.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initialTraverseDependencies,
|
||||||
|
traverseDependencies,
|
||||||
|
};
|
|
@ -237,6 +237,22 @@ class DependencyGraph extends EventEmitter {
|
||||||
return Promise.resolve(this._moduleCache.getAllModules());
|
return Promise.resolve(this._moduleCache.getAllModules());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveDependency(
|
||||||
|
fromModule: Module,
|
||||||
|
toModuleName: string,
|
||||||
|
platform: ?string,
|
||||||
|
): Module {
|
||||||
|
const req = new ResolutionRequest({
|
||||||
|
moduleResolver: this._moduleResolver,
|
||||||
|
entryPath: fromModule.path,
|
||||||
|
helpers: this._helpers,
|
||||||
|
platform: platform || null,
|
||||||
|
moduleCache: this._moduleCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
return req.resolveDependency(fromModule, toModuleName);
|
||||||
|
}
|
||||||
|
|
||||||
getDependencies<T: {+transformer: JSTransformerOptions}>({
|
getDependencies<T: {+transformer: JSTransformerOptions}>({
|
||||||
entryPath,
|
entryPath,
|
||||||
options,
|
options,
|
||||||
|
|
Loading…
Reference in New Issue