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

@ -132,9 +132,9 @@ is passed to `ReactPackager.middleware`
To get verbose output when running the packager, define an environment variable:
export DEBUG=ReactNativePackager:*
You can combine this with other values, e.g. `DEBUG=babel,ReactNativePackager:*`. Under the hood this uses the [`debug`](https://www.npmjs.com/package/debug) package, see its documentation for all the available options.
The `/debug` endpoint discussed above is also useful.
## FAQ

View File

@ -25,6 +25,8 @@ class Bundle extends BundleBase {
this._numPrependedModules = 0;
this._numRequireCalls = 0;
this._minify = minify;
this._ramBundle = null; // cached RAM Bundle
}
addModule(resolver, resolutionResponse, module, moduleTransport) {
@ -73,6 +75,7 @@ class Bundle extends BundleBase {
virtual: true,
sourceCode: code,
sourcePath: name + '.js',
meta: {preloaded: true},
}));
this._numRequireCalls += 1;
}
@ -103,7 +106,46 @@ class Bundle extends BundleBase {
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 prependedModules = this._numPrependedModules;
const requireCalls = this._numRequireCalls;

View File

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

View File

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

View File

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