Move preloaded modules to startup code section

Summary:We found that moving the preloaded modules to the startup section of the RAM Bundle improves TTI quite a bit by saving lots of through the bridge calls and injecting multiple modules at once on JSC. However, doing this on a non hacky way required a lot of work. The main changes this diff does are:
  - Add to `BundleBase` additional bundling options. This options are fetched based on the entry file we're building by invoking a module that exports a function (`getBundleOptionsModulePath`).
  - Implement `BundleOptions` module to include the `numPreloadedModules` attribute as a bundle additional option. This value is computed by getting the dependencies the entry file has and looking for the first module that exports a module we don't want to preload. The `numPreloadedModules` attribute is then used to decide where to splice the array of modules.
- Additional kung fu to make sure sourcemaps work for both preloaded and non preloaded modules.

Reviewed By: davidaurelio

Differential Revision: D3046534

fb-gh-sync-id: 80b676222ca3bb8b9eecc912a7963be94d3dee1a
shipit-source-id: 80b676222ca3bb8b9eecc912a7963be94d3dee1a
This commit is contained in:
Martín Bigio 2016-03-23 09:27:19 -07:00 committed by Facebook Github Bot 1
parent 18612273be
commit c0f446d7e2
5 changed files with 89 additions and 23 deletions

View File

@ -25,6 +25,8 @@ class Bundle extends BundleBase {
this._numPrependedModules = 0; this._numPrependedModules = 0;
this._numRequireCalls = 0; this._numRequireCalls = 0;
this._minify = minify; this._minify = minify;
this._ramBundle = null; // cached RAM Bundle
} }
addModule(resolver, resolutionResponse, module, moduleTransport) { addModule(resolver, resolutionResponse, module, moduleTransport) {
@ -73,6 +75,7 @@ class Bundle extends BundleBase {
virtual: true, virtual: true,
sourceCode: code, sourceCode: code,
sourcePath: name + '.js', sourcePath: name + '.js',
meta: {preloaded: true},
})); }));
this._numRequireCalls += 1; this._numRequireCalls += 1;
} }
@ -103,7 +106,46 @@ class Bundle extends BundleBase {
return source; return source;
} }
getUnbundle() { getUnbundle(type) {
if (this._ramBundle) {
return this._ramBundle;
}
switch (type) {
case 'INDEX':
this._ramBundle = this._getAsIndexedFileUnbundle();
break;
case 'ASSETS':
this._ramBundle = this._getAsAssetsUnbundle();
break;
default:
throw new Error('Unkown RAM Bundle type:', type);
}
return this._ramBundle;
}
_getAsIndexedFileUnbundle() {
const modules = this.getModules();
// separate modules we need to preload from the ones we don't
const shouldPreload = (module) => module.meta && module.meta.preloaded;
const preloaded = modules.filter(module => shouldPreload(module));
const notPreloaded = modules.filter(module => !shouldPreload(module));
// code that will be executed on bridge start up
const startupCode = preloaded.map(({code}) => code).join('\n');
return {
// include copy of all modules on the order they're writen on the bundle:
// polyfills, preloaded, additional requires, non preloaded
allModules: preloaded.concat(notPreloaded),
startupCode, // no entries on the index for these modules, only the code
modules: notPreloaded, // we include both the code and entries on the index
};
}
_getAsAssetsUnbundle() {
const allModules = this.getModules().slice(); const allModules = this.getModules().slice();
const prependedModules = this._numPrependedModules; const prependedModules = this._numPrependedModules;
const requireCalls = this._numRequireCalls; const requireCalls = this._numRequireCalls;

View File

@ -60,9 +60,8 @@ class BundleBase {
finalize(options) { finalize(options) {
Object.freeze(this._modules); Object.freeze(this._modules);
Object.seal(this._modules);
Object.freeze(this._assets); Object.freeze(this._assets);
Object.seal(this._assets);
this._finalized = true; this._finalized = true;
} }
@ -97,9 +96,8 @@ class BundleBase {
bundle.setMainModuleId(json.mainModuleId); bundle.setMainModuleId(json.mainModuleId);
Object.freeze(bundle._modules); Object.freeze(bundle._modules);
Object.seal(bundle._modules);
Object.freeze(bundle._assets); Object.freeze(bundle._assets);
Object.seal(bundle._assets);
bundle._finalized = true; bundle._finalized = true;
} }
} }

View File

@ -356,13 +356,13 @@ class Bundler {
const modulesByName = Object.create(null); const modulesByName = Object.create(null);
if (!resolutionResponse) { if (!resolutionResponse) {
let onProgess = noop; let onProgress = noop;
if (process.stdout.isTTY && !this._opts.silent) { if (process.stdout.isTTY && !this._opts.silent) {
const bar = new ProgressBar( const bar = new ProgressBar(
'transformed :current/:total (:percent)', 'transformed :current/:total (:percent)',
{complete: '=', incomplete: ' ', width: 40, total: 1}, {complete: '=', incomplete: ' ', width: 40, total: 1},
); );
onProgess = (_, total) => { onProgress = (_, total) => {
bar.total = total; bar.total = total;
bar.tick(); bar.tick();
}; };
@ -373,7 +373,7 @@ class Bundler {
dev, dev,
platform, platform,
hot, hot,
onProgess, onProgress,
minify, minify,
generateSourceMaps: unbundle, generateSourceMaps: unbundle,
}); });
@ -383,10 +383,22 @@ class Bundler {
Activity.endEvent(findEventId); Activity.endEvent(findEventId);
onResolutionResponse(response); onResolutionResponse(response);
// get entry file complete path (`entryFile` is relative to roots)
let entryFilePath;
if (response.dependencies.length > 0) {
const numModuleSystemDependencies =
this._resolver.getModuleSystemDependencies({dev, unbundle}).length;
entryFilePath = response.dependencies[
response.numPrependedDependencies +
numModuleSystemDependencies
].path;
}
const toModuleTransport = module => const toModuleTransport = module =>
this._toModuleTransport({ this._toModuleTransport({
module, module,
bundle, bundle,
entryFilePath,
transformOptions: response.transformOptions, transformOptions: response.transformOptions,
}).then(transformed => { }).then(transformed => {
modulesByName[transformed.name] = module; modulesByName[transformed.name] = module;
@ -432,7 +444,7 @@ class Bundler {
hot = false, hot = false,
recursive = true, recursive = true,
generateSourceMaps = false, generateSourceMaps = false,
onProgess, onProgress,
}) { }) {
return this.getTransformOptions( return this.getTransformOptions(
entryFile, entryFile,
@ -450,11 +462,12 @@ class Bundler {
platform, platform,
transform: transformSpecificOptions, transform: transformSpecificOptions,
}; };
return this._resolver.getDependencies( return this._resolver.getDependencies(
entryFile, entryFile,
{dev, platform, recursive}, {dev, platform, recursive},
transformOptions, transformOptions,
onProgess, onProgress,
); );
}); });
} }
@ -491,7 +504,7 @@ class Bundler {
); );
} }
_toModuleTransport({module, bundle, transformOptions}) { _toModuleTransport({module, bundle, entryFilePath, transformOptions}) {
let moduleTransport; let moduleTransport;
if (module.isAsset_DEPRECATED()) { if (module.isAsset_DEPRECATED()) {
moduleTransport = this._generateAssetModule_DEPRECATED(bundle, module); moduleTransport = this._generateAssetModule_DEPRECATED(bundle, module);
@ -509,15 +522,24 @@ class Bundler {
module.read(transformOptions), module.read(transformOptions),
]).then(( ]).then((
[name, {code, dependencies, dependencyOffsets, map, source}] [name, {code, dependencies, dependencyOffsets, map, source}]
) => new ModuleTransport({ ) => {
name, const preloaded =
id: this._getModuleId(module), module.path === entryFilePath ||
code, module.isPolyfill() || (
map, transformOptions.transform.preloadedModules &&
meta: {dependencies, dependencyOffsets}, transformOptions.transform.preloadedModules.hasOwnProperty(module.path)
sourceCode: source, );
sourcePath: module.path
})); return new ModuleTransport({
name,
id: this._getModuleId(module),
code,
map,
meta: {dependencies, dependencyOffsets, preloaded},
sourceCode: source,
sourcePath: module.path
})
});
} }
getGraphDebugInfo() { getGraphDebugInfo() {

View File

@ -136,7 +136,11 @@ class SocketServer {
_reply(sock, id, type, data) { _reply(sock, id, type, data) {
debug('request finished', type); debug('request finished', type);
data = toJSON(data); try {
data = toJSON(data);
} catch (e) {
console.error('SocketServer exception:', e);
}
sock.write(bser.dumpToBuffer({ sock.write(bser.dumpToBuffer({
id, id,