require implementations: Make module map an array, preset length, if number of modules is known

Summary: For specific cases, Metro will write the number of modules to the bundle. The require implementation can take advantage of that, and construct an array with the target size.

Reviewed By: mjesun

Differential Revision: D7696290

fbshipit-source-id: a7be74c02960dc089e4d3c1accd7c732b762c8b5
This commit is contained in:
David Aurelio 2018-04-21 02:47:48 -07:00 committed by Facebook Github Bot
parent 8aa96e6a3e
commit 18507aecc9
6 changed files with 75 additions and 22 deletions

View File

@ -52,7 +52,12 @@ async function build(options: BuildOptions): Promise<BuildResult> {
]); ]);
const {entryModules} = graph; const {entryModules} = graph;
const preludeScript = virtualModule(getPreludeCode({isDev: !optimize})); const preludeScript = virtualModule(
getPreludeCode({
extraVars: {__NUM_MODULES__: graph.modules.length},
isDev: !optimize,
}),
);
const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills]; const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills];
return { return {
entryModules, entryModules,

View File

@ -98,9 +98,9 @@ export type Module = {|
|}; |};
export type PostProcessModules = ( export type PostProcessModules = (
modules: Iterable<Module>, modules: $ReadOnlyArray<Module>,
entryPoints: Array<string>, entryPoints: Array<string>,
) => Iterable<Module>; ) => $ReadOnlyArray<Module>;
export type OutputFn< export type OutputFn<
M: FBSourceMap | MetroSourceMap = FBSourceMap | MetroSourceMap, M: FBSourceMap | MetroSourceMap = FBSourceMap | MetroSourceMap,

View File

@ -1,17 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic_bundle bundles package with polyfills 1`] = ` exports[`basic_bundle bundles package with polyfills 1`] = `
"var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV='production'; "var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV=\\"production\\";
(function (global) { (function (global) {
'use strict'; 'use strict';
var PRINT_REQUIRE_PATHS = false; var PRINT_REQUIRE_PATHS = false;
global.require = metroRequire; global.require = metroRequire;
global.__d = define; global.__d = define;
var modules = Object.create(null); var modules = typeof __NUM_MODULES__ === 'number' ? Array(__NUM_MODULES__ | 0) : Object.create(null);
function define(factory, moduleId, dependencyMap) { function define(factory, moduleId, dependencyMap) {
if (moduleId in modules) { if (modules[moduleId] != null) {
return; return;
} }
@ -204,17 +204,17 @@ require(0);"
`; `;
exports[`basic_bundle bundles package without polyfills 1`] = ` exports[`basic_bundle bundles package without polyfills 1`] = `
"var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV='production'; "var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV=\\"production\\";
(function (global) { (function (global) {
'use strict'; 'use strict';
var PRINT_REQUIRE_PATHS = false; var PRINT_REQUIRE_PATHS = false;
global.require = metroRequire; global.require = metroRequire;
global.__d = define; global.__d = define;
var modules = Object.create(null); var modules = typeof __NUM_MODULES__ === 'number' ? Array(__NUM_MODULES__ | 0) : Object.create(null);
function define(factory, moduleId, dependencyMap) { function define(factory, moduleId, dependencyMap) {
if (moduleId in modules) { if (modules[moduleId] != null) {
return; return;
} }

View File

@ -16,22 +16,44 @@ const vm = require('vm');
['development', 'production'].forEach(mode => { ['development', 'production'].forEach(mode => {
describe(`${mode} mode`, () => { describe(`${mode} mode`, () => {
const isDev = mode === 'development';
it('sets up `process.env.NODE_ENV` and `__DEV__`', () => { it('sets up `process.env.NODE_ENV` and `__DEV__`', () => {
const sandbox: $FlowFixMe = {}; const sandbox: $FlowFixMe = {};
vm.createContext(sandbox); vm.createContext(sandbox);
vm.runInContext(getPreludeCode({isDev: mode == 'development'}), sandbox); vm.runInContext(getPreludeCode({isDev}), sandbox);
expect(sandbox.process.env.NODE_ENV).toEqual(mode); expect(sandbox.process.env.NODE_ENV).toEqual(mode);
expect(sandbox.__DEV__).toEqual(mode == 'development'); expect(sandbox.__DEV__).toEqual(isDev);
}); });
it('does not override an existing `process.env`', () => { it('does not override an existing `process.env`', () => {
const nextTick = () => {}; const nextTick = () => {};
const sandbox: $FlowFixMe = {process: {nextTick, env: {FOOBAR: 123}}}; const sandbox: $FlowFixMe = {process: {nextTick, env: {FOOBAR: 123}}};
vm.createContext(sandbox); vm.createContext(sandbox);
vm.runInContext(getPreludeCode({isDev: mode == 'development'}), sandbox); vm.runInContext(getPreludeCode({isDev}), sandbox);
expect(sandbox.process.env.NODE_ENV).toEqual(mode); expect(sandbox.process.env.NODE_ENV).toEqual(mode);
expect(sandbox.process.env.FOOBAR).toEqual(123); expect(sandbox.process.env.FOOBAR).toEqual(123);
expect(sandbox.process.nextTick).toEqual(nextTick); expect(sandbox.process.nextTick).toEqual(nextTick);
}); });
it('allows to define additional variables', () => {
const sandbox: $FlowFixMe = {};
const FOO = '1';
const BAR = 2;
vm.createContext(sandbox);
vm.runInContext(getPreludeCode({isDev, extraVars: {FOO, BAR}}), sandbox);
expect(sandbox.FOO).toBe(FOO);
expect(sandbox.BAR).toBe(BAR);
});
it('does not override core variables with additional variables', () => {
const sandbox: $FlowFixMe = {};
vm.createContext(sandbox);
vm.runInContext(
getPreludeCode({isDev, extraVars: {__DEV__: 123}}),
sandbox,
);
expect(sandbox.__DEV__).toBe(isDev);
});
}); });
}); });

View File

@ -10,13 +10,36 @@
'use strict'; 'use strict';
function getPreludeCode({isDev}: {|+isDev: boolean|}): string { function getPreludeCode({
return ( extraVars,
`var __DEV__=${String(isDev)},` + isDev,
'__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),' + }: {|
'process=this.process||{};process.env=process.env||{};' + +extraVars?: {[string]: mixed},
`process.env.NODE_ENV='${isDev ? 'development' : 'production'}';` +isDev: boolean,
); |}): string {
const vars = [
...formatExtraVars(extraVars),
`__DEV__=${String(isDev)}`,
'__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now()',
'process=this.process||{}',
];
return `var ${vars.join(',')};${processEnv(
isDev ? 'development' : 'production',
)}`;
}
function formatExtraVars(extraVars) {
let assignments = [];
for (const key in extraVars) {
assignments.push(`${key}=${JSON.stringify(extraVars[key])}`);
}
return assignments;
}
function processEnv(nodeEnv) {
return `process.env=process.env||{};process.env.NODE_ENV=${JSON.stringify(
nodeEnv,
)};`;
} }
module.exports = getPreludeCode; module.exports = getPreludeCode;

View File

@ -14,6 +14,7 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
declare var __DEV__: boolean; declare var __DEV__: boolean;
declare var __NUM_MODULES__: mixed;
type DependencyMap = Array<ModuleID>; type DependencyMap = Array<ModuleID>;
type Exports = any; type Exports = any;
@ -47,7 +48,6 @@ type ModuleDefinition = {|
verboseName?: string, verboseName?: string,
path?: string, path?: string,
|}; |};
type ModuleMap = {[key: ModuleID]: ModuleDefinition, __proto__: null};
type PatchedModules = {[ModuleID]: boolean}; type PatchedModules = {[ModuleID]: boolean};
type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports;
type VerboseModuleNameForDev = string; type VerboseModuleNameForDev = string;
@ -60,7 +60,10 @@ const PRINT_REQUIRE_PATHS = false;
global.require = metroRequire; global.require = metroRequire;
global.__d = define; global.__d = define;
const modules: ModuleMap = Object.create(null); const modules =
typeof __NUM_MODULES__ === 'number'
? (Array(__NUM_MODULES__ | 0): Array<ModuleDefinition>)
: (Object.create(null): {[number]: ModuleDefinition, __proto__: null});
if (__DEV__) { if (__DEV__) {
var verboseNamesToModuleIds: { var verboseNamesToModuleIds: {
[key: string]: number, [key: string]: number,
@ -73,7 +76,7 @@ function define(
moduleId: number, moduleId: number,
dependencyMap?: DependencyMap, dependencyMap?: DependencyMap,
) { ) {
if (moduleId in modules) { if (modules[moduleId] != null) {
if (__DEV__) { if (__DEV__) {
// (We take `inverseDependencies` from `arguments` to avoid an unused // (We take `inverseDependencies` from `arguments` to avoid an unused
// named parameter in `define` in production. // named parameter in `define` in production.