Make HMR faster

Summary:
public

At the moment, when the user changes a file we end up pulling the dependencies of the entry point to build the bundle. This could take a long time if the bundle is big. To avoid it, lets introduce a new parameter to `getDependencies` to be able to avoid processing the modules recursively and reuse the resolution responseto build the bundle.

Reviewed By: davidaurelio

Differential Revision: D2862850

fb-gh-sync-id: b8ae2b811a8ae9aec5612f9655d1c762671ce730
This commit is contained in:
Martín Bigio 2016-01-29 10:14:37 -08:00 committed by facebook-github-bot-9
parent d503cf243c
commit 5d2a457880
7 changed files with 86 additions and 32 deletions

View File

@ -206,11 +206,12 @@ describe('Bundler', function() {
}); });
pit('gets the list of dependencies from the resolver', function() { pit('gets the list of dependencies from the resolver', function() {
return bundler.getDependencies('/root/foo.js', true) return bundler.getDependencies('/root/foo.js', true).then(() =>
.then( expect(getDependencies).toBeCalledWith(
() => expect(getDependencies) '/root/foo.js',
.toBeCalledWith('/root/foo.js', { dev: true }) { dev: true, recursive: true },
); )
);
}); });
describe('getOrderedDependencyPaths', () => { describe('getOrderedDependencyPaths', () => {

View File

@ -235,14 +235,48 @@ class Bundler {
unbundle: isUnbundle, unbundle: isUnbundle,
hot: hot, hot: hot,
entryModuleOnly, entryModuleOnly,
resolutionResponse,
}) { }) {
const findEventId = Activity.startEvent('find dependencies');
let transformEventId; let transformEventId;
const moduleSystemDeps = includeSystemDependencies
? this._resolver.getModuleSystemDependencies(
{ dev: isDev, platform, isUnbundle }
)
: [];
const findModules = () => {
const findEventId = Activity.startEvent('find dependencies');
return this.getDependencies(entryFile, isDev, platform).then(response => {
Activity.endEvent(findEventId);
bundle.setMainModuleId(response.mainModuleId);
bundle.setMainModuleName(response.mainModuleId);
if (!entryModuleOnly && bundle.setNumPrependedModules) {
bundle.setNumPrependedModules(
response.numPrependedDependencies + moduleSystemDeps.length
);
}
return {
response,
modulesToProcess: response.dependencies,
};
});
};
const useProvidedModules = () => {
const moduleId = this._resolver.getModuleForPath(entryFile);
bundle.setMainModuleId(moduleId);
bundle.setMainModuleName(moduleId);
return Promise.resolve({
response: resolutionResponse,
modulesToProcess: modules
});
};
return (
modules ? useProvidedModules() : findModules()
).then(({response, modulesToProcess}) => {
return this.getDependencies(entryFile, isDev, platform).then((response) => {
Activity.endEvent(findEventId);
bundle.setMainModuleId(response.mainModuleId);
bundle.setMainModuleName(response.mainModuleId);
transformEventId = Activity.startEvent('transform'); transformEventId = Activity.startEvent('transform');
let dependencies; let dependencies;
@ -259,10 +293,6 @@ class Bundler {
const modulesToProcess = modules || response.dependencies; const modulesToProcess = modules || response.dependencies;
dependencies = moduleSystemDeps.concat(modulesToProcess); dependencies = moduleSystemDeps.concat(modulesToProcess);
bundle.setNumPrependedModules && bundle.setNumPrependedModules(
response.numPrependedDependencies + moduleSystemDeps.length
);
} }
let bar; let bar;
@ -280,7 +310,6 @@ class Bundler {
module => { module => {
return this._transformModule( return this._transformModule(
bundle, bundle,
response,
module, module,
platform, platform,
isDev, isDev,
@ -347,7 +376,6 @@ class Bundler {
response.dependencies.map( response.dependencies.map(
module => this._transformModule( module => this._transformModule(
bundle, bundle,
response,
module, module,
platform, platform,
isDev, isDev,
@ -418,8 +446,15 @@ class Bundler {
return this._resolver.getModuleForPath(entryFile); return this._resolver.getModuleForPath(entryFile);
} }
getDependencies(main, isDev, platform) { getDependencies(main, isDev, platform, recursive = true) {
return this._resolver.getDependencies(main, { dev: isDev, platform }); return this._resolver.getDependencies(
main,
{
dev: isDev,
platform,
recursive,
},
);
} }
getOrderedDependencyPaths({ entryFile, dev, platform }) { getOrderedDependencyPaths({ entryFile, dev, platform }) {
@ -454,7 +489,7 @@ class Bundler {
); );
} }
_transformModule(bundle, response, module, platform = null, dev = true, hot = false) { _transformModule(bundle, module, platform = null, dev = true, hot = false) {
if (module.isAsset_DEPRECATED()) { if (module.isAsset_DEPRECATED()) {
return this._generateAssetModule_DEPRECATED(bundle, module); return this._generateAssetModule_DEPRECATED(bundle, module);
} else if (module.isAsset()) { } else if (module.isAsset()) {

View File

@ -103,7 +103,7 @@ class ResolutionRequest {
); );
} }
getOrderedDependencies(response, mocksPattern) { getOrderedDependencies(response, mocksPattern, recursive = true) {
return this._getAllMocks(mocksPattern).then(allMocks => { return this._getAllMocks(mocksPattern).then(allMocks => {
const entry = this._moduleCache.getModule(this._entryPath); const entry = this._moduleCache.getModule(this._entryPath);
const mocks = Object.create(null); const mocks = Object.create(null);
@ -163,7 +163,9 @@ class ResolutionRequest {
p = p.then(() => { p = p.then(() => {
if (!visited[modDep.hash()]) { if (!visited[modDep.hash()]) {
visited[modDep.hash()] = true; visited[modDep.hash()] = true;
return collect(modDep); if (recursive) {
return collect(modDep);
}
} }
return null; return null;
}); });

View File

@ -158,7 +158,7 @@ class DependencyGraph {
return this._moduleCache.getModule(entryFile); return this._moduleCache.getModule(entryFile);
} }
getDependencies(entryPath, platform) { getDependencies(entryPath, platform, recursive = true) {
return this.load().then(() => { return this.load().then(() => {
platform = this._getRequestPlatform(entryPath, platform); platform = this._getRequestPlatform(entryPath, platform);
const absPath = this._getAbsolutePath(entryPath); const absPath = this._getAbsolutePath(entryPath);
@ -177,7 +177,11 @@ class DependencyGraph {
const response = new ResolutionResponse(); const response = new ResolutionResponse();
return Promise.all([ return Promise.all([
req.getOrderedDependencies(response, this._opts.mocksPattern), req.getOrderedDependencies(
response,
this._opts.mocksPattern,
recursive,
),
req.getAsyncDependencies(response), req.getAsyncDependencies(response),
]).then(() => response); ]).then(() => response);
}); });

View File

@ -186,7 +186,8 @@ describe('Resolver', function() {
return depResolver.getDependencies('/root/index.js', { dev: true }) return depResolver.getDependencies('/root/index.js', { dev: true })
.then(function(result) { .then(function(result) {
expect(result.mainModuleId).toEqual('index'); expect(result.mainModuleId).toEqual('index');
expect(DependencyGraph.mock.instances[0].getDependencies).toBeCalledWith('/root/index.js', undefined); expect(DependencyGraph.mock.instances[0].getDependencies)
.toBeCalledWith('/root/index.js', undefined, true);
expect(result.dependencies[0]).toBe(Polyfill.mock.instances[0]); expect(result.dependencies[0]).toBe(Polyfill.mock.instances[0]);
expect(result.dependencies[result.dependencies.length - 1]) expect(result.dependencies[result.dependencies.length - 1])
.toBe(module); .toBe(module);

View File

@ -64,6 +64,10 @@ const getDependenciesValidateOpts = declareOpts({
type: 'boolean', type: 'boolean',
default: false default: false
}, },
recursive: {
type: 'boolean',
default: true,
},
}); });
class Resolver { class Resolver {
@ -116,15 +120,17 @@ class Resolver {
getDependencies(main, options) { getDependencies(main, options) {
const opts = getDependenciesValidateOpts(options); const opts = getDependenciesValidateOpts(options);
return this._depGraph.getDependencies(main, opts.platform).then( return this._depGraph.getDependencies(
resolutionResponse => { main,
this._getPolyfillDependencies().reverse().forEach( opts.platform,
polyfill => resolutionResponse.prependDependency(polyfill) opts.recursive,
); ).then(resolutionResponse => {
this._getPolyfillDependencies().reverse().forEach(
polyfill => resolutionResponse.prependDependency(polyfill)
);
return resolutionResponse.finalize(); return resolutionResponse.finalize();
} });
);
} }
getModuleSystemDependencies(options) { getModuleSystemDependencies(options) {

View File

@ -137,6 +137,10 @@ const dependencyOpts = declareOpts({
type: 'string', type: 'string',
required: true, required: true,
}, },
recursive: {
type: 'boolean',
default: true,
},
}); });
class Server { class Server {
@ -269,6 +273,7 @@ class Server {
opts.entryFile, opts.entryFile,
opts.dev, opts.dev,
opts.platform, opts.platform,
opts.recursive,
); );
}); });
} }