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 preludeScript = virtualModule(getPreludeCode({isDev: !optimize}));
const preludeScript = virtualModule(
getPreludeCode({
extraVars: {__NUM_MODULES__: graph.modules.length},
isDev: !optimize,
}),
);
const prependedScripts = [preludeScript, ...moduleSystem, ...polyfills];
return {
entryModules,

View File

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

View File

@ -1,17 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
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) {
'use strict';
var PRINT_REQUIRE_PATHS = false;
global.require = metroRequire;
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) {
if (moduleId in modules) {
if (modules[moduleId] != null) {
return;
}
@ -204,17 +204,17 @@ require(0);"
`;
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) {
'use strict';
var PRINT_REQUIRE_PATHS = false;
global.require = metroRequire;
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) {
if (moduleId in modules) {
if (modules[moduleId] != null) {
return;
}

View File

@ -16,22 +16,44 @@ const vm = require('vm');
['development', 'production'].forEach(mode => {
describe(`${mode} mode`, () => {
const isDev = mode === 'development';
it('sets up `process.env.NODE_ENV` and `__DEV__`', () => {
const sandbox: $FlowFixMe = {};
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.__DEV__).toEqual(mode == 'development');
expect(sandbox.__DEV__).toEqual(isDev);
});
it('does not override an existing `process.env`', () => {
const nextTick = () => {};
const sandbox: $FlowFixMe = {process: {nextTick, env: {FOOBAR: 123}}};
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.FOOBAR).toEqual(123);
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';
function getPreludeCode({isDev}: {|+isDev: boolean|}): string {
return (
`var __DEV__=${String(isDev)},` +
'__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),' +
'process=this.process||{};process.env=process.env||{};' +
`process.env.NODE_ENV='${isDev ? 'development' : 'production'}';`
);
function getPreludeCode({
extraVars,
isDev,
}: {|
+extraVars?: {[string]: mixed},
+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;

View File

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