Change format of dependencyPairs param received by wrapModule()

Reviewed By: jeanlauliac

Differential Revision: D5880766

fbshipit-source-id: ef9f0ec2d7dcc4e47f5dacb9d899b5f1fde18e8f
This commit is contained in:
Rafael Oleza 2017-09-26 06:54:51 -07:00 committed by Facebook Github Bot
parent 24be572cf5
commit 7ba76a32e1
9 changed files with 86 additions and 57 deletions

View File

@ -101,11 +101,20 @@ class Bundle extends BundleBase {
): Promise<void> { ): Promise<void> {
const index = super.addModule(moduleTransport); const index = super.addModule(moduleTransport);
const dependencyPairs = resolutionResponse.getResolvedDependencyPairs(
module,
);
const dependencyPairsMap = new Map();
for (const [relativePath, dependencyModule] of dependencyPairs) {
dependencyPairsMap.set(relativePath, dependencyModule.path);
}
return Promise.resolve( return Promise.resolve(
resolver.wrapModule({ resolver.wrapModule({
module, module,
getModuleId: resolutionResponse.getModuleId, getModuleId: resolutionResponse.getModuleId,
dependencyPairs: resolutionResponse.getResolvedDependencyPairs(module), dependencyPairs: dependencyPairsMap,
name: moduleTransport.name, name: moduleTransport.name,
code: moduleTransport.code, code: moduleTransport.code,
map: moduleTransport.map, map: moduleTransport.map,

View File

@ -49,12 +49,19 @@ class HMRBundle extends BundleBase {
/* $FlowFixMe: broken OOP design: function signature should be the same */ /* $FlowFixMe: broken OOP design: function signature should be the same */
moduleTransport: ModuleTransport, moduleTransport: ModuleTransport,
) { ) {
const dependencyPairs = response.getResolvedDependencyPairs(module);
const dependencyPairsMap = new Map();
for (const [relativePath, module] of dependencyPairs) {
dependencyPairsMap.set(relativePath, module.path);
}
const code = resolver.resolveRequires( const code = resolver.resolveRequires(
module, module,
/* $FlowFixMe: `getModuleId` is monkey-patched so may not exist */ /* $FlowFixMe: `getModuleId` is monkey-patched so may not exist */
response.getModuleId, response.getModuleId,
moduleTransport.code, moduleTransport.code,
response.getResolvedDependencyPairs(module), dependencyPairsMap,
/* $FlowFixMe: may not exist */ /* $FlowFixMe: may not exist */
moduleTransport.meta.dependencyOffsets, moduleTransport.meta.dependencyOffsets,
); );

View File

@ -18,7 +18,9 @@ const crypto = require('crypto');
const resolutionResponse = { const resolutionResponse = {
getModuleId() {}, getModuleId() {},
getResolvedDependencyPairs() {}, getResolvedDependencyPairs() {
return [];
},
}; };
describe('Bundle', () => { describe('Bundle', () => {

View File

@ -27,6 +27,10 @@ export type DeltaResult = {|
+reset: boolean, +reset: boolean,
|}; |};
export type ShallowDependencies = Map<string, Map<string, string>>;
export type ModulePaths = Set<string>;
export type InverseDependencies = Map<string, Set<string>>;
/** /**
* This class is in charge of calculating the delta of changed modules that * This class is in charge of calculating the delta of changed modules that
* happen between calls. To do so, it subscribes to file changes, so it can * happen between calls. To do so, it subscribes to file changes, so it can
@ -39,13 +43,13 @@ class DeltaCalculator extends EventEmitter {
_options: BundleOptions; _options: BundleOptions;
_transformerOptions: ?JSTransformerOptions; _transformerOptions: ?JSTransformerOptions;
_dependencies: Set<string> = new Set();
_shallowDependencies: Map<string, Set<string>> = new Map();
_modifiedFiles: Set<string> = new Set();
_currentBuildPromise: ?Promise<DeltaResult>; _currentBuildPromise: ?Promise<DeltaResult>;
_dependencyPairs: Map<string, $ReadOnlyArray<[string, Module]>> = new Map(); _modifiedFiles: Set<string> = new Set();
_modules: ModulePaths = new Set();
_shallowDependencies: ShallowDependencies = new Map();
_modulesByName: Map<string, Module> = new Map(); _modulesByName: Map<string, Module> = new Map();
_inverseDependencies: Map<string, Set<string>> = new Map(); _inverseDependencies: InverseDependencies = new Map();
constructor(bundler: Bundler, resolver: Resolver, options: BundleOptions) { constructor(bundler: Bundler, resolver: Resolver, options: BundleOptions) {
super(); super();
@ -70,10 +74,9 @@ class DeltaCalculator extends EventEmitter {
.removeListener('change', this._handleMultipleFileChanges); .removeListener('change', this._handleMultipleFileChanges);
// Clean up all the cache data structures to deallocate memory. // Clean up all the cache data structures to deallocate memory.
this._dependencies = new Set(); this._modules = new Set();
this._shallowDependencies = new Map(); this._shallowDependencies = new Map();
this._modifiedFiles = new Set(); this._modifiedFiles = new Set();
this._dependencyPairs = new Map();
this._modulesByName = new Map(); this._modulesByName = new Map();
} }
@ -135,8 +138,8 @@ class DeltaCalculator extends EventEmitter {
* pair consists of a string which corresponds to the relative path used in * pair consists of a string which corresponds to the relative path used in
* the `require()` statement and the Module object for that dependency. * the `require()` statement and the Module object for that dependency.
*/ */
getDependencyPairs(): Map<string, $ReadOnlyArray<[string, Module]>> { getShallowDependencies(): ShallowDependencies {
return this._dependencyPairs; return this._shallowDependencies;
} }
/** /**
@ -174,7 +177,7 @@ class DeltaCalculator extends EventEmitter {
// won't detect any modified file). Once we have our own dependency // won't detect any modified file). Once we have our own dependency
// traverser in Delta Bundler this will be easy to fix. // traverser in Delta Bundler this will be easy to fix.
if (type === 'delete') { if (type === 'delete') {
this._dependencies.delete(filePath); this._modules.delete(filePath);
return; return;
} }
@ -182,7 +185,7 @@ class DeltaCalculator extends EventEmitter {
// Notify users that there is a change in some of the bundle files. This // Notify users that there is a change in some of the bundle files. This
// way the client can choose to refetch the bundle. // way the client can choose to refetch the bundle.
if (this._dependencies.has(filePath)) { if (this._modules.has(filePath)) {
this.emit('change'); this.emit('change');
} }
}; };
@ -190,7 +193,7 @@ class DeltaCalculator extends EventEmitter {
async _getDelta(modifiedFiles: Set<string>): Promise<DeltaResult> { async _getDelta(modifiedFiles: Set<string>): Promise<DeltaResult> {
// If we call getDelta() without being initialized, we need get all // If we call getDelta() without being initialized, we need get all
// dependencies and return a reset delta. // dependencies and return a reset delta.
if (this._dependencies.size === 0) { if (this._modules.size === 0) {
const {added} = await this._calculateAllDependencies(); const {added} = await this._calculateAllDependencies();
return { return {
@ -204,7 +207,7 @@ class DeltaCalculator extends EventEmitter {
// If any of these files is required by an existing file, it will // If any of these files is required by an existing file, it will
// automatically be picked up when calculating all dependencies. // automatically be picked up when calculating all dependencies.
const modifiedArray = Array.from(modifiedFiles).filter(file => const modifiedArray = Array.from(modifiedFiles).filter(file =>
this._dependencies.has(file), this._modules.has(file),
); );
// No changes happened. Return empty delta. // No changes happened. Return empty delta.
@ -248,20 +251,20 @@ class DeltaCalculator extends EventEmitter {
async _hasChangedDependencies(file: string) { async _hasChangedDependencies(file: string) {
const module = this._resolver.getModuleForPath(file); const module = this._resolver.getModuleForPath(file);
if (!this._dependencies.has(module.path)) { if (!this._modules.has(module.path)) {
return false; return false;
} }
const oldDependenciesMap =
this._shallowDependencies.get(module.path) || new Set();
const newDependencies = await this._getShallowDependencies(module); const newDependencies = await this._getShallowDependencies(module);
const oldDependencies = this._shallowDependencies.get(module.path); const oldDependencies = new Set(oldDependenciesMap.keys());
if (!oldDependencies) { if (!oldDependencies) {
return false; return false;
} }
// Update the dependency and inverse dependency caches for this module.
this._shallowDependencies.set(module.path, newDependencies);
return areDifferent(oldDependencies, newDependencies); return areDifferent(oldDependencies, newDependencies);
} }
@ -279,16 +282,17 @@ class DeltaCalculator extends EventEmitter {
currentDependencies.forEach(module => { currentDependencies.forEach(module => {
const dependencyPairs = response.getResolvedDependencyPairs(module); const dependencyPairs = response.getResolvedDependencyPairs(module);
this._shallowDependencies.set( const shallowDependencies = new Map();
module.path, for (const [relativePath, module] of dependencyPairs) {
new Set(dependencyPairs.map(([name, module]) => name)), shallowDependencies.set(relativePath, module.path);
); }
this._dependencyPairs.set(module.path, dependencyPairs);
this._shallowDependencies.set(module.path, shallowDependencies);
// Only add it to the delta bundle if it did not exist before. // Only add it to the delta bundle if it did not exist before.
if (!this._dependencies.has(module.path)) { if (!this._modules.has(module.path)) {
added.set(module.path, module); added.set(module.path, module);
this._dependencies.add(module.path); this._modules.add(module.path);
} }
}); });
@ -297,17 +301,16 @@ class DeltaCalculator extends EventEmitter {
// We know that some files have been removed only if the size of the current // 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 // dependencies is different that the size of the old dependencies after
// adding the new files. // adding the new files.
if (currentDependencies.length !== this._dependencies.size) { if (currentDependencies.length !== this._modules.size) {
const currentSet = new Set(currentDependencies.map(dep => dep.path)); const currentSet = new Set(currentDependencies.map(dep => dep.path));
this._dependencies.forEach(file => { this._modules.forEach(file => {
if (currentSet.has(file)) { if (currentSet.has(file)) {
return; return;
} }
this._dependencies.delete(file); this._modules.delete(file);
this._shallowDependencies.delete(file); this._shallowDependencies.delete(file);
this._dependencyPairs.delete(file);
deleted.add(file); deleted.add(file);
}); });
@ -335,14 +338,15 @@ class DeltaCalculator extends EventEmitter {
this._inverseDependencies = new Map(); this._inverseDependencies = new Map();
currentDependencies.forEach(module => { currentDependencies.forEach(module => {
const dependencies = this._dependencyPairs.get(module.path) || []; const dependencies =
this._shallowDependencies.get(module.path) || new Map();
dependencies.forEach(([name, dependencyModule]) => { dependencies.forEach((relativePath, path) => {
let inverse = this._inverseDependencies.get(dependencyModule.path); let inverse = this._inverseDependencies.get(path);
if (!inverse) { if (!inverse) {
inverse = new Set(); inverse = new Set();
this._inverseDependencies.set(dependencyModule.path, inverse); this._inverseDependencies.set(path, inverse);
} }
inverse.add(module.path); inverse.add(module.path);
}); });

View File

@ -25,6 +25,7 @@ import type Resolver from '../Resolver';
import type {MappingsMap} from '../lib/SourceMap'; import type {MappingsMap} from '../lib/SourceMap';
import type Module from '../node-haste/Module'; import type Module from '../node-haste/Module';
import type {Options as BundleOptions} from './'; import type {Options as BundleOptions} from './';
import type {ShallowDependencies} from './DeltaCalculator';
type DeltaEntry = {| type DeltaEntry = {|
+code: string, +code: string,
@ -171,7 +172,7 @@ class DeltaTransformer extends EventEmitter {
const {modified, deleted, reset} = await this._deltaCalculator.getDelta(); const {modified, deleted, reset} = await this._deltaCalculator.getDelta();
const transformerOptions = this._deltaCalculator.getTransformerOptions(); const transformerOptions = this._deltaCalculator.getTransformerOptions();
const dependencyPairs = this._deltaCalculator.getDependencyPairs(); const dependencyPairs = this._deltaCalculator.getShallowDependencies();
// Get the transformed source code of each modified/added module. // Get the transformed source code of each modified/added module.
const modifiedDelta = await this._transformModules( const modifiedDelta = await this._transformModules(
@ -215,7 +216,7 @@ class DeltaTransformer extends EventEmitter {
async _getPrepend( async _getPrepend(
transformOptions: JSTransformerOptions, transformOptions: JSTransformerOptions,
dependencyPairs: Map<string, $ReadOnlyArray<[string, Module]>>, dependencyPairs: ShallowDependencies,
): Promise<DeltaEntries> { ): Promise<DeltaEntries> {
// Get all the polyfills from the relevant option params (the // Get all the polyfills from the relevant option params (the
// `getPolyfills()` method and the `polyfillModuleNames` variable). // `getPolyfills()` method and the `polyfillModuleNames` variable).
@ -247,7 +248,7 @@ class DeltaTransformer extends EventEmitter {
} }
async _getAppend( async _getAppend(
dependencyPairs: Map<string, $ReadOnlyArray<[string, Module]>>, dependencyPairs: ShallowDependencies,
modulesByName: Map<string, Module>, modulesByName: Map<string, Module>,
): Promise<DeltaEntries> { ): Promise<DeltaEntries> {
// Get the absolute path of the entry file, in order to be able to get the // Get the absolute path of the entry file, in order to be able to get the
@ -306,7 +307,7 @@ class DeltaTransformer extends EventEmitter {
async _transformModules( async _transformModules(
modules: Array<Module>, modules: Array<Module>,
transformOptions: JSTransformerOptions, transformOptions: JSTransformerOptions,
dependencyPairs: Map<string, $ReadOnlyArray<[string, Module]>>, dependencyPairs: ShallowDependencies,
): Promise<DeltaEntries> { ): Promise<DeltaEntries> {
return new Map( return new Map(
await Promise.all( await Promise.all(
@ -320,14 +321,15 @@ class DeltaTransformer extends EventEmitter {
async _transformModule( async _transformModule(
module: Module, module: Module,
transformOptions: JSTransformerOptions, transformOptions: JSTransformerOptions,
dependencyPairs: Map<string, $ReadOnlyArray<[string, Module]>>, dependencyPairs: ShallowDependencies,
): Promise<[number, ?DeltaEntry]> { ): Promise<[number, ?DeltaEntry]> {
const [name, metadata] = await Promise.all([ const [name, metadata] = await Promise.all([
module.getName(), module.getName(),
this._getMetadata(module, transformOptions), this._getMetadata(module, transformOptions),
]); ]);
const dependencyPairsForModule = dependencyPairs.get(module.path) || []; const dependencyPairsForModule =
dependencyPairs.get(module.path) || new Map();
const wrapped = this._bundleOptions.wrapModules const wrapped = this._bundleOptions.wrapModules
? this._resolver.wrapModule({ ? this._resolver.wrapModule({

View File

@ -191,11 +191,11 @@ describe('DeltaCalculator', () => {
}); });
}); });
it('should calculate the dependencyPairs correctly after adding/removing dependencies', async () => { it('should calculate the shallowDependencies correctly after adding/removing dependencies', async () => {
// Get initial delta // Get initial delta
await deltaCalculator.getDelta(); await deltaCalculator.getDelta();
expect(deltaCalculator.getDependencyPairs().size).toBe(3); expect(deltaCalculator.getShallowDependencies().size).toBe(3);
fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]}); fileWatcher.emit('change', {eventsQueue: [{filePath: '/foo'}]});
@ -206,11 +206,10 @@ describe('DeltaCalculator', () => {
await deltaCalculator.getDelta(); await deltaCalculator.getDelta();
const dependencyPairs = deltaCalculator.getDependencyPairs(); const shallowDependencies = deltaCalculator.getShallowDependencies();
expect(dependencyPairs.size).toBe(4); expect(shallowDependencies.size).toBe(4);
expect(dependencyPairs.get('/foo')[2][0]).toEqual('qux'); expect(shallowDependencies.get('/foo').get('qux')).toEqual('/qux');
expect(dependencyPairs.get('/foo')[2][1].path).toEqual('/qux');
}); });
it('should emit an event when there is a relevant file change', async done => { it('should emit an event when there is a relevant file change', async done => {

View File

@ -242,10 +242,18 @@ describe('Resolver', function() {
]), ]),
); );
const dependencyPairs = new Map();
for (const [
relativePath,
dependencyModule,
] of resolutionResponse.getResolvedDependencyPairs(module)) {
dependencyPairs.set(relativePath, dependencyModule.path);
}
const {code: processedCode} = depResolver.wrapModule({ const {code: processedCode} = depResolver.wrapModule({
module: module, module: module,
getModuleId: resolutionResponse.getModuleId, getModuleId: resolutionResponse.getModuleId,
dependencyPairs: resolutionResponse.getResolvedDependencyPairs(module), dependencyPairs,
name: 'test module', name: 'test module',
code, code,
dependencyOffsets, dependencyOffsets,

View File

@ -185,18 +185,16 @@ class Resolver {
module: Module, module: Module,
getModuleId: ({path: string}) => number, getModuleId: ({path: string}) => number,
code: string, code: string,
dependencyPairs: $ReadOnlyArray<[string, Module]>, dependencyPairs: Map<string, string>,
dependencyOffsets: Array<number> = [], dependencyOffsets: Array<number> = [],
): string { ): string {
const resolvedDeps = Object.create(null); const resolvedDeps = Object.create(null);
// here, we build a map of all require strings (relative and absolute) // here, we build a map of all require strings (relative and absolute)
// to the canonical ID of the module they reference // to the canonical ID of the module they reference
dependencyPairs.forEach(([name, module], key) => { for (const [name, path] of dependencyPairs) {
if (module) { resolvedDeps[name] = getModuleId({path});
resolvedDeps[name] = getModuleId(module); }
}
});
// if we have a canonical ID for the module imported here, // if we have a canonical ID for the module imported here,
// we use it, so that require() is always called with the same // we use it, so that require() is always called with the same
@ -229,7 +227,7 @@ class Resolver {
}: { }: {
module: Module, module: Module,
getModuleId: ({path: string}) => number, getModuleId: ({path: string}) => number,
dependencyPairs: $ReadOnlyArray<[string, Module]>, dependencyPairs: Map<string, string>,
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
name: string, name: string,
map: ?MappingsMap, map: ?MappingsMap,

View File

@ -119,7 +119,7 @@ class ResolutionResponse<TModule: {hash(): string}, TOptions> {
module: TModule, module: TModule,
): $ReadOnlyArray<[string, TModule]> { ): $ReadOnlyArray<[string, TModule]> {
this._assertFinalized(); this._assertFinalized();
return this._mappings[module.hash()]; return this._mappings[module.hash()] || [];
} }
} }