From a606e79af0d6494925a81946fd6fdc5123a9d8fd Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Thu, 28 Sep 2017 15:06:48 -0700 Subject: [PATCH] Integrate the incremental dependency traversor into the Delta Bundler Reviewed By: davidaurelio Differential Revision: D5880892 fbshipit-source-id: d6e04b59aee5fe0bf671553c98f728be054e01b9 --- .../src/DeltaBundler/DeltaCalculator.js | 325 ++++++------------ .../src/DeltaBundler/DeltaTransformer.js | 48 ++- .../__tests__/DeltaCalculator-test.js | 144 ++++---- .../src/DeltaBundler/traverseDependencies.js | 11 - 4 files changed, 201 insertions(+), 327 deletions(-) diff --git a/packages/metro-bundler/src/DeltaBundler/DeltaCalculator.js b/packages/metro-bundler/src/DeltaBundler/DeltaCalculator.js index 61ad5c26..5ac157a2 100644 --- a/packages/metro-bundler/src/DeltaBundler/DeltaCalculator.js +++ b/packages/metro-bundler/src/DeltaBundler/DeltaCalculator.js @@ -12,13 +12,16 @@ 'use strict'; +const { + initialTraverseDependencies, + traverseDependencies, +} = require('./traverseDependencies'); const {EventEmitter} = require('events'); -import type Bundler, {BundlingOptions} from '../Bundler'; +import type Bundler from '../Bundler'; import type {Options as JSTransformerOptions} from '../JSTransformer/worker'; -import type Resolver from '../Resolver'; import type {BundleOptions} from '../Server'; -import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse'; +import type DependencyGraph from '../node-haste/DependencyGraph'; import type Module from '../node-haste/Module'; export type DeltaResult = {| @@ -27,9 +30,7 @@ export type DeltaResult = {| +reset: boolean, |}; -export type ShallowDependencies = Map>; -export type ModulePaths = Set; -export type InverseDependencies = Map>; +import type {DependencyEdges} from './traverseDependencies'; /** * This class is in charge of calculating the delta of changed modules that @@ -39,27 +40,28 @@ export type InverseDependencies = Map>; */ class DeltaCalculator extends EventEmitter { _bundler: Bundler; - _resolver: Resolver; + _dependencyGraph: DependencyGraph; _options: BundleOptions; _transformerOptions: ?JSTransformerOptions; _currentBuildPromise: ?Promise; + _deletedFiles: Set = new Set(); _modifiedFiles: Set = new Set(); - _modules: ModulePaths = new Set(); - _shallowDependencies: ShallowDependencies = new Map(); - _modulesByName: Map = new Map(); - _inverseDependencies: InverseDependencies = new Map(); + _dependencyEdges: DependencyEdges = new Map(); - constructor(bundler: Bundler, resolver: Resolver, options: BundleOptions) { + constructor( + bundler: Bundler, + dependencyGraph: DependencyGraph, + options: BundleOptions, + ) { super(); this._bundler = bundler; this._options = options; - this._resolver = resolver; + this._dependencyGraph = dependencyGraph; - this._resolver - .getDependencyGraph() + this._dependencyGraph .getWatcher() .on('change', this._handleMultipleFileChanges); } @@ -68,16 +70,14 @@ class DeltaCalculator extends EventEmitter { * Stops listening for file changes and clears all the caches. */ end() { - this._resolver - .getDependencyGraph() + this._dependencyGraph .getWatcher() .removeListener('change', this._handleMultipleFileChanges); // Clean up all the cache data structures to deallocate memory. - this._modules = new Set(); - this._shallowDependencies = new Map(); this._modifiedFiles = new Set(); - this._modulesByName = new Map(); + this._deletedFiles = new Set(); + this._dependencyEdges = new Map(); } /** @@ -96,14 +96,21 @@ class DeltaCalculator extends EventEmitter { // and creating a new instance for the file watcher. const modifiedFiles = this._modifiedFiles; this._modifiedFiles = new Set(); + const deletedFiles = this._deletedFiles; + this._deletedFiles = new Set(); // Concurrent requests should reuse the same bundling process. To do so, // this method stores the promise as an instance variable, and then it's // removed after it gets resolved. - this._currentBuildPromise = this._getDelta(modifiedFiles); + this._currentBuildPromise = this._getChangedDependencies( + modifiedFiles, + deletedFiles, + ); let result; + const numDependencies = this._dependencyEdges.size; + try { result = await this._currentBuildPromise; } catch (error) { @@ -112,6 +119,14 @@ class DeltaCalculator extends EventEmitter { // do so, asking for a delta after an error will produce an empty Delta, // which is not correct. modifiedFiles.forEach(file => this._modifiedFiles.add(file)); + deletedFiles.forEach(file => this._deletedFiles.add(file)); + + // If after an error the number of edges has changed, we could be in + // a weird state. As a safe net we clean the dependency edges to force + // a clean traversal of the graph next time. + if (this._dependencyEdges.size !== numDependencies) { + this._dependencyEdges = new Map(); + } throw error; } finally { @@ -126,24 +141,30 @@ class DeltaCalculator extends EventEmitter { * read all the modules. This can be used by external objects to read again * any module very fast (since the options object instance will be the same). */ - getTransformerOptions(): JSTransformerOptions { + async getTransformerOptions(): Promise { if (!this._transformerOptions) { - throw new Error('Calculate a bundle first'); + this._transformerOptions = (await this._bundler.getTransformOptions( + this._options.entryFile, + { + dev: this._options.dev, + generateSourceMaps: false, + hot: this._options.hot, + minify: this._options.minify, + platform: this._options.platform, + prependPolyfills: false, + }, + )).transformer; } return this._transformerOptions; } /** - * Returns all the dependency pairs for each of the modules. Each dependency - * pair consists of a string which corresponds to the relative path used in - * the `require()` statement and the Module object for that dependency. + * Returns all the dependency edges from the graph. Each edge contains the + * needed information to do the traversing (dependencies, inverseDependencies) + * plus some metadata. */ - getShallowDependencies(): ShallowDependencies { - return this._shallowDependencies; - } - - getInverseDependencies(): Map> { - return this._inverseDependencies; + getDependencyEdges(): DependencyEdges { + return this._dependencyEdges; } _handleMultipleFileChanges = ({eventsQueue}) => { @@ -162,75 +183,87 @@ class DeltaCalculator extends EventEmitter { type: string, filePath: string, }): mixed => { - // We do not want to keep track of deleted files, since this can cause - // issues when moving files (or even deleting files). - // The only issue with this approach is that the user removes a file that - // is needed, the bundler will still create a correct bundle (since it - // won't detect any modified file). Once we have our own dependency - // traverser in Delta Bundler this will be easy to fix. if (type === 'delete') { - this._modules.delete(filePath); - return; + this._deletedFiles.add(filePath); + } else { + this._modifiedFiles.add(filePath); } - this._modifiedFiles.add(filePath); - // Notify users that there is a change in some of the bundle files. This // way the client can choose to refetch the bundle. - if (this._modules.has(filePath)) { - this.emit('change'); - } + this.emit('change'); }; - async _getDelta(modifiedFiles: Set): Promise { - // If we call getDelta() without being initialized, we need get all - // dependencies and return a reset delta. - if (this._modules.size === 0) { - const {added} = await this._calculateAllDependencies(); + async _getChangedDependencies( + modifiedFiles: Set, + deletedFiles: Set, + ): Promise { + const transformerOptions = await this.getTransformerOptions(); + + if (!this._dependencyEdges.size) { + const path = this._dependencyGraph.getAbsolutePath( + this._options.entryFile, + ); + + const modified = new Map([ + [path, this._dependencyGraph.getModuleForPath(path)], + ]); + + const {added} = await initialTraverseDependencies( + path, + this._dependencyGraph, + transformerOptions, + this._dependencyEdges, + this._options.onProgress || undefined, + ); + + for (const path of added) { + modified.set(path, this._dependencyGraph.getModuleForPath(path)); + } return { - modified: added, + modified, deleted: new Set(), reset: true, }; } - // We don't care about modified files that are not depended in the bundle. - // If any of these files is required by an existing file, it will - // automatically be picked up when calculating all dependencies. - const modifiedArray = Array.from(modifiedFiles).filter(file => - this._modules.has(file), + // If a file has been deleted, we want to invalidate any other file that + // depends on it, so we can process it and correctly return an error. + deletedFiles.forEach(filePath => { + const edge = this._dependencyEdges.get(filePath); + + if (edge) { + edge.inverseDependencies.forEach(path => modifiedFiles.add(path)); + } + }); + + // We only want to process files that are in the bundle. + const modifiedDependencies = Array.from(modifiedFiles).filter(filePath => + this._dependencyEdges.has(filePath), ); // No changes happened. Return empty delta. - if (modifiedArray.length === 0) { + if (modifiedDependencies.length === 0) { return {modified: new Map(), deleted: new Set(), reset: false}; } - // Build the modules from the files that have been modified. - const modified = new Map( - modifiedArray.map(file => { - const module = this._resolver.getModuleForPath(file); - return [file, module]; - }), + const {added, deleted} = await traverseDependencies( + modifiedDependencies, + this._dependencyGraph, + transformerOptions, + this._dependencyEdges, + this._options.onProgress || undefined, ); - const filesWithChangedDependencies = await Promise.all( - modifiedArray.map(this._hasChangedDependencies, this), - ); + const modified = new Map(); - // If there is no file with changes in its dependencies, we can just - // return the modified modules without recalculating the dependencies. - if (!filesWithChangedDependencies.some(value => value)) { - return {modified, deleted: new Set(), reset: false}; + for (const path of modifiedDependencies) { + modified.set(path, this._dependencyGraph.getModuleForPath(path)); } - // Recalculate all dependencies and append the newly added files to the - // modified files. - const {added, deleted} = await this._calculateAllDependencies(); - - for (const [key, value] of added) { - modified.set(key, value); + for (const path of added) { + modified.set(path, this._dependencyGraph.getModuleForPath(path)); } return { @@ -239,146 +272,6 @@ class DeltaCalculator extends EventEmitter { reset: false, }; } - - async _hasChangedDependencies(file: string) { - const module = this._resolver.getModuleForPath(file); - - if (!this._modules.has(module.path)) { - return false; - } - - const oldDependenciesMap = - this._shallowDependencies.get(module.path) || new Set(); - - const newDependencies = await this._getShallowDependencies(module); - const oldDependencies = new Set(oldDependenciesMap.keys()); - - if (!oldDependencies) { - return false; - } - - return areDifferent(oldDependencies, newDependencies); - } - - async _calculateAllDependencies(): Promise<{ - added: Map, - deleted: Set, - }> { - const added = new Map(); - - const response = await this._getAllDependencies(); - const currentDependencies = response.dependencies; - - this._transformerOptions = response.options.transformer; - - currentDependencies.forEach(module => { - const dependencyPairs = response.getResolvedDependencyPairs(module); - - const shallowDependencies = new Map(); - for (const [relativePath, module] of dependencyPairs) { - shallowDependencies.set(relativePath, module.path); - } - - this._shallowDependencies.set(module.path, shallowDependencies); - - // Only add it to the delta bundle if it did not exist before. - if (!this._modules.has(module.path)) { - added.set(module.path, module); - this._modules.add(module.path); - } - }); - - const deleted = new Set(); - - // We know that some files have been removed only if the size of the current - // dependencies is different that the size of the old dependencies after - // adding the new files. - if (currentDependencies.length !== this._modules.size) { - const currentSet = new Set(currentDependencies.map(dep => dep.path)); - - this._modules.forEach(file => { - if (currentSet.has(file)) { - return; - } - - this._modules.delete(file); - this._shallowDependencies.delete(file); - - deleted.add(file); - }); - } - - // Yet another iteration through all the dependencies. This one is to - // calculate the inverse dependencies. Right now we cannot do a faster - // iteration to only calculate this for changed files since - // `Bundler.getShallowDependencies()` return the relative name of the - // dependencies (this logic is very similar to the one in - // getInverseDependencies.js on the react-native repo). - // - // TODO: consider moving this calculation directly to - // `getInverseDependencies()`. - this._inverseDependencies = new Map(); - - currentDependencies.forEach(module => { - const dependencies = - this._shallowDependencies.get(module.path) || new Map(); - - dependencies.forEach((relativePath, path) => { - let inverse = this._inverseDependencies.get(path); - - if (!inverse) { - inverse = new Set(); - this._inverseDependencies.set(path, inverse); - } - inverse.add(module.path); - }); - }); - - return { - added, - deleted, - }; - } - - async _getShallowDependencies(module: Module): Promise> { - if (module.isAsset() || module.isJSON()) { - return new Set(); - } - - const dependencies = await this._bundler.getShallowDependencies({ - ...this._options, - entryFile: module.path, - rootEntryFile: this._options.entryFile, - generateSourceMaps: false, - transformerOptions: this._transformerOptions || undefined, - }); - - return new Set(dependencies); - } - - async _getAllDependencies(): Promise< - ResolutionResponse, - > { - return await this._bundler.getDependencies({ - ...this._options, - rootEntryFile: this._options.entryFile, - generateSourceMaps: this._options.generateSourceMaps, - prependPolyfills: false, - }); - } -} - -function areDifferent(first: Set, second: Set): boolean { - if (first.size !== second.size) { - return true; - } - - for (const element of first) { - if (!second.has(element)) { - return true; - } - } - return false; } module.exports = DeltaCalculator; diff --git a/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js b/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js index 3be73e94..d4678933 100644 --- a/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js +++ b/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js @@ -25,7 +25,7 @@ import type Resolver from '../Resolver'; import type {MappingsMap} from '../lib/SourceMap'; import type Module from '../node-haste/Module'; import type {Options as BundleOptions} from './'; -import type {ShallowDependencies} from './DeltaCalculator'; +import type {DependencyEdges} from './traverseDependencies'; type DeltaEntry = {| +code: string, @@ -118,7 +118,7 @@ class DeltaTransformer extends EventEmitter { const deltaCalculator = new DeltaCalculator( bundler, - resolver, + resolver.getDependencyGraph(), bundleOptions, ); @@ -171,14 +171,14 @@ class DeltaTransformer extends EventEmitter { // Calculate the delta of modules. const {modified, deleted, reset} = await this._deltaCalculator.getDelta(); - const transformerOptions = this._deltaCalculator.getTransformerOptions(); - const dependencyPairs = this._deltaCalculator.getShallowDependencies(); + const transformerOptions = await this._deltaCalculator.getTransformerOptions(); + const dependencyEdges = this._deltaCalculator.getDependencyEdges(); // Get the transformed source code of each modified/added module. const modifiedDelta = await this._transformModules( Array.from(modified.values()), transformerOptions, - dependencyPairs, + dependencyEdges, ); deleted.forEach(id => { @@ -188,19 +188,17 @@ class DeltaTransformer extends EventEmitter { // Return the source code that gets prepended to all the modules. This // contains polyfills and startup code (like the require() implementation). const prependSources = reset - ? await this._getPrepend(transformerOptions, dependencyPairs) + ? await this._getPrepend(transformerOptions, dependencyEdges) : new Map(); // Return the source code that gets appended to all the modules. This // contains the require() calls to startup the execution of the modules. const appendSources = reset - ? await this._getAppend(dependencyPairs) + ? await this._getAppend(dependencyEdges) : new Map(); // Inverse dependencies are needed for HMR. - const inverseDependencies = this._getInverseDependencies( - this._deltaCalculator.getInverseDependencies(), - ); + const inverseDependencies = this._getInverseDependencies(dependencyEdges); return { pre: prependSources, @@ -213,7 +211,7 @@ class DeltaTransformer extends EventEmitter { async _getPrepend( transformOptions: JSTransformerOptions, - dependencyPairs: ShallowDependencies, + dependencyEdges: DependencyEdges, ): Promise { // Get all the polyfills from the relevant option params (the // `getPolyfills()` method and the `polyfillModuleNames` variable). @@ -240,13 +238,11 @@ class DeltaTransformer extends EventEmitter { return await this._transformModules( modules, transformOptions, - dependencyPairs, + dependencyEdges, ); } - async _getAppend( - dependencyPairs: ShallowDependencies, - ): Promise { + async _getAppend(dependencyEdges: DependencyEdges): Promise { // Get the absolute path of the entry file, in order to be able to get the // actual correspondant module (and its moduleId) to be able to add the // correct require(); call at the very end of the bundle. @@ -300,13 +296,13 @@ class DeltaTransformer extends EventEmitter { * Converts the paths in the inverse dependendencies to module ids. */ _getInverseDependencies( - inverseDependencies: Map>, + dependencyEdges: DependencyEdges, ): {[key: string]: $ReadOnlyArray} { const output = Object.create(null); - for (const [key, dependencies] of inverseDependencies) { - output[this._getModuleId({path: key})] = Array.from( - dependencies, + for (const [path, {inverseDependencies}] of dependencyEdges.entries()) { + output[this._getModuleId({path})] = Array.from( + inverseDependencies, ).map(dep => this._getModuleId({path: dep})); } @@ -316,12 +312,12 @@ class DeltaTransformer extends EventEmitter { async _transformModules( modules: Array, transformOptions: JSTransformerOptions, - dependencyPairs: ShallowDependencies, + dependencyEdges: DependencyEdges, ): Promise { return new Map( await Promise.all( modules.map(module => - this._transformModule(module, transformOptions, dependencyPairs), + this._transformModule(module, transformOptions, dependencyEdges), ), ), ); @@ -330,18 +326,18 @@ class DeltaTransformer extends EventEmitter { async _transformModule( module: Module, transformOptions: JSTransformerOptions, - dependencyPairs: ShallowDependencies, + dependencyEdges: DependencyEdges, ): Promise<[number, ?DeltaEntry]> { const name = module.getName(); const metadata = await this._getMetadata(module, transformOptions); - const dependencyPairsForModule = - dependencyPairs.get(module.path) || new Map(); + const edge = dependencyEdges.get(module.path); + const dependencyPairs = edge ? edge.dependencies : new Map(); const wrapped = this._bundleOptions.wrapModules ? this._resolver.wrapModule({ module, getModuleId: this._getModuleId, - dependencyPairs: dependencyPairsForModule, + dependencyPairs, dependencyOffsets: metadata.dependencyOffsets || [], name, code: metadata.code, @@ -354,7 +350,7 @@ class DeltaTransformer extends EventEmitter { module, this._getModuleId, metadata.code, - dependencyPairsForModule, + dependencyPairs, metadata.dependencyOffsets || [], ), map: metadata.map, diff --git a/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaCalculator-test.js b/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaCalculator-test.js index d4d8abc9..95ab0f36 100644 --- a/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaCalculator-test.js +++ b/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaCalculator-test.js @@ -14,14 +14,19 @@ jest.mock('../../Bundler'); jest.mock('../../Resolver'); +jest.mock('../traverseDependencies'); const Bundler = require('../../Bundler'); const {EventEmitter} = require('events'); -const Resolver = require('../../Resolver'); const DeltaCalculator = require('../DeltaCalculator'); +const { + initialTraverseDependencies, + traverseDependencies, +} = require('../traverseDependencies'); describe('DeltaCalculator', () => { + 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'}); @@ -29,14 +34,13 @@ describe('DeltaCalculator', () => { let deltaCalculator; let fileWatcher; let mockedDependencies; - let mockedDependencyTree; const bundlerMock = new Bundler(); const options = { assetPlugins: [], dev: true, - entryFile: 'bundle.js', + entryFile: 'bundle', entryModuleOnly: false, excludeSource: false, generateSourceMaps: false, @@ -68,42 +72,49 @@ describe('DeltaCalculator', () => { } beforeEach(async () => { - mockedDependencies = [moduleFoo, moduleBar, moduleBaz]; - mockedDependencyTree = new Map([[moduleFoo, [moduleBar, moduleBaz]]]); + mockedDependencies = [entryModule, moduleFoo, moduleBar, moduleBaz]; fileWatcher = new EventEmitter(); - Resolver.prototype.getDependencyGraph.mockReturnValue({ + const dependencyGraph = { getWatcher() { return fileWatcher; }, - }); + getAbsolutePath(path) { + return '/' + path; + }, + getModuleForPath(path) { + return mockedDependencies.filter(dep => dep.path === path)[0]; + }, + }; - Resolver.prototype.getModuleForPath.mockImplementation( - path => mockedDependencies.filter(dep => dep.path === path)[0], - ); + initialTraverseDependencies.mockImplementationOnce( + async (path, dg, opt, edges) => { + edges.set('/foo', moduleFoo); - Bundler.prototype.getDependencies.mockImplementation(async () => { - return { - options: {}, - dependencies: mockedDependencies, - getResolvedDependencyPairs(module) { - const deps = mockedDependencyTree.get(module); - return deps ? deps.map(dep => [dep.name, dep]) : []; - }, - }; - }); - - Bundler.prototype.getShallowDependencies.mockImplementation( - async module => { - const deps = mockedDependencyTree.get(module); - return deps ? await Promise.all(deps.map(dep => dep.getName())) : []; + return { + added: new Set(['/foo', '/bar', '/baz']), + deleted: new Set(), + }; }, ); - const resolverMock = new Resolver(); + Bundler.prototype.getTransformOptions.mockImplementation(async () => { + return { + transformer: {}, + }; + }); - deltaCalculator = new DeltaCalculator(bundlerMock, resolverMock, options); + deltaCalculator = new DeltaCalculator( + bundlerMock, + dependencyGraph, + options, + ); + }); + + afterEach(() => { + initialTraverseDependencies.mockReset(); + traverseDependencies.mockReset(); }); it('should start listening for file changes after being initialized', async () => { @@ -116,11 +127,12 @@ describe('DeltaCalculator', () => { expect(fileWatcher.listeners('change')).toHaveLength(0); }); - it('should calculate the initial bundle correctly', async () => { + it('should include the entry file when calculating the initial bundle', async () => { const result = await deltaCalculator.getDelta(); expect(result).toEqual({ modified: new Map([ + ['/bundle', entryModule], ['/foo', moduleFoo], ['/bar', moduleBar], ['/baz', moduleBaz], @@ -138,20 +150,31 @@ describe('DeltaCalculator', () => { deleted: new Set(), reset: false, }); + + expect(traverseDependencies.mock.calls.length).toBe(0); }); it('should calculate a delta after a simple modification', async () => { - // Get initial delta await deltaCalculator.getDelta(); fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); + traverseDependencies.mockReturnValue( + Promise.resolve({ + added: new Set(), + deleted: new Set(), + }), + ); + const result = await deltaCalculator.getDelta(); + expect(result).toEqual({ modified: new Map([['/foo', moduleFoo]]), deleted: new Set(), reset: false, }); + + expect(traverseDependencies.mock.calls.length).toBe(1); }); it('should calculate a delta after removing a dependency', async () => { @@ -160,16 +183,22 @@ describe('DeltaCalculator', () => { fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); - // Remove moduleBar - mockedDependencyTree.set(moduleFoo, [moduleBaz]); - mockedDependencies = [moduleFoo, moduleBaz]; + traverseDependencies.mockReturnValue( + Promise.resolve({ + added: new Set(), + deleted: new Set(['/baz']), + }), + ); const result = await deltaCalculator.getDelta(); + expect(result).toEqual({ modified: new Map([['/foo', moduleFoo]]), - deleted: new Set(['/bar']), + deleted: new Set(['/baz']), reset: false, }); + + expect(traverseDependencies.mock.calls.length).toBe(1); }); it('should calculate a delta after adding/removing dependencies', async () => { @@ -178,10 +207,14 @@ describe('DeltaCalculator', () => { fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); - // Add moduleQux const moduleQux = createModule({path: '/qux', name: 'qux'}); - mockedDependencyTree.set(moduleFoo, [moduleQux]); - mockedDependencies = [moduleFoo, moduleQux]; + + traverseDependencies.mockReturnValue( + Promise.resolve({ + added: new Set([moduleQux.path]), + deleted: new Set(['/bar', '/baz']), + }), + ); const result = await deltaCalculator.getDelta(); expect(result).toEqual({ @@ -191,27 +224,6 @@ describe('DeltaCalculator', () => { }); }); - it('should calculate the shallowDependencies correctly after adding/removing dependencies', async () => { - // Get initial delta - await deltaCalculator.getDelta(); - - expect(deltaCalculator.getShallowDependencies().size).toBe(3); - - fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); - - // Add moduleQux - const moduleQux = createModule({path: '/qux', name: 'qux'}); - mockedDependencyTree.set(moduleFoo, [moduleBar, moduleBaz, moduleQux]); - mockedDependencies = [moduleFoo, moduleBar, moduleBaz, moduleQux]; - - await deltaCalculator.getDelta(); - - const shallowDependencies = deltaCalculator.getShallowDependencies(); - - expect(shallowDependencies.size).toBe(4); - expect(shallowDependencies.get('/foo').get('qux')).toEqual('/qux'); - }); - it('should emit an event when there is a relevant file change', async done => { await deltaCalculator.getDelta(); @@ -220,20 +232,6 @@ describe('DeltaCalculator', () => { fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); }); - it('should not emit an event when there is a file changed outside the bundle', async () => { - jest.useFakeTimers(); - - const onChangeFile = jest.fn(); - await deltaCalculator.getDelta(); - - deltaCalculator.on('change', onChangeFile); - fileWatcher.emit('change', {eventsQueue: [{filePath: '/another'}]}); - - jest.runAllTimers(); - - expect(onChangeFile.mock.calls.length).toBe(0); - }); - it('should not emit an event when there is a file deleted', async () => { jest.useFakeTimers(); @@ -254,9 +252,7 @@ describe('DeltaCalculator', () => { fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); - Bundler.prototype.getShallowDependencies.mockImplementation(async () => { - throw new Error('error'); - }); + traverseDependencies.mockReturnValue(Promise.reject(new Error())); await expect(deltaCalculator.getDelta()).rejects.toBeInstanceOf(Error); diff --git a/packages/metro-bundler/src/DeltaBundler/traverseDependencies.js b/packages/metro-bundler/src/DeltaBundler/traverseDependencies.js index 02dd7212..edd69844 100644 --- a/packages/metro-bundler/src/DeltaBundler/traverseDependencies.js +++ b/packages/metro-bundler/src/DeltaBundler/traverseDependencies.js @@ -14,17 +14,6 @@ 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,