Add flow to require implementation, always warn when modules are accepted by name

Reviewed By: matryoshcow

Differential Revision: D4124228

fbshipit-source-id: 461087caf4add07db376bb71ae5ba42bf3536cd3
This commit is contained in:
David Aurelio 2016-11-03 05:45:49 -07:00 committed by Facebook Github Bot
parent 4ce09ce88a
commit cda4129dd3
1 changed files with 78 additions and 27 deletions

View File

@ -5,19 +5,54 @@
* This source code is licensed under the BSD-style license found in the * This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant * LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/ */
'use strict'; 'use strict';
type Exports = any;
type FactoryFn = (
global: Object,
require: RequireFn,
moduleObject: {exports: {}},
exports: {},
) => void;
type HotModuleReloadingAcceptFn = Function;
type HotModuleReloadingData = {|
acceptCallback: ?HotModuleReloadingAcceptFn,
accept: (callback: HotModuleReloadingAcceptFn) => void,
|};
type Module = {
exports: Exports,
hot?: HotModuleReloadingData,
};
type ModuleID = number;
type ModuleDefinition = {|
factory: FactoryFn,
hasError: boolean,
isInitialized: boolean,
exports: Exports,
verboseName?: string,
hot?: HotModuleReloadingData,
|};
type ModuleMap =
{[key: ModuleID]: (ModuleDefinition)};
type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports;
type VerboseModuleNameForDev = string;
global.require = require; global.require = require;
global.__d = define; global.__d = define;
const modules = Object.create(null); const modules: ModuleMap = Object.create(null);
if (__DEV__) { if (__DEV__) {
var verboseNamesToModuleIds = Object.create(null); var verboseNamesToModuleIds: {[key: string]: number} = Object.create(null);
} }
function define(moduleId, factory) { function define(
moduleId: number,
factory: FactoryFn,
) {
if (moduleId in modules) { if (moduleId in modules) {
// prevent repeated calls to `global.nativeRequire` to overwrite modules // prevent repeated calls to `global.nativeRequire` to overwrite modules
// that are already loaded // that are already loaded
@ -35,22 +70,38 @@ function define(moduleId, factory) {
// DEBUGGABLE MODULES NAMES // DEBUGGABLE MODULES NAMES
// avoid unnecessary parameter in prod // avoid unnecessary parameter in prod
const verboseName = modules[moduleId].verboseName = arguments[2]; const verboseName: string | void = arguments[2];
verboseNamesToModuleIds[verboseName] = moduleId; if (verboseName) {
modules[moduleId].verboseName = verboseName;
verboseNamesToModuleIds[verboseName] = moduleId;
}
} }
} }
function require(moduleId) { function require(moduleId: ModuleID | VerboseModuleNameForDev) {
const module = __DEV__ if (__DEV__ && typeof moduleId === 'string') {
? modules[moduleId] || modules[verboseNamesToModuleIds[moduleId]] const verboseName = moduleId;
: modules[moduleId]; moduleId = verboseNamesToModuleIds[moduleId];
if (moduleId == null) {
throw new Error(`Unknown named module: '${verboseName}'`);
} else {
console.warn(
`Requiring module '${verboseName}' by name is only supported for ` +
'debugging purposes and will BREAK IN PRODUCTION!'
);
}
} else {
moduleId = +moduleId;
}
const module = modules[moduleId];
return module && module.isInitialized return module && module.isInitialized
? module.exports ? module.exports
: guardedLoadModule(moduleId, module); : guardedLoadModule(moduleId, module);
} }
let inGuard = false; let inGuard = false;
function guardedLoadModule(moduleId, module) { function guardedLoadModule(moduleId: ModuleID , module) {
if (!inGuard && global.ErrorUtils) { if (!inGuard && global.ErrorUtils) {
inGuard = true; inGuard = true;
let returnValue; let returnValue;
@ -73,17 +124,6 @@ function loadModuleImplementation(moduleId, module) {
module = modules[moduleId]; module = modules[moduleId];
} }
if (__DEV__ && !module) {
// allow verbose module names to be passed as module ID
module = modules[verboseNamesToModuleIds[moduleId]];
if (module) {
console.warn(
`Requiring module '${moduleId}' by name is only supported for ` +
'debugging purposes and will break in production'
);
}
}
if (!module) { if (!module) {
throw unknownModuleError(moduleId); throw unknownModuleError(moduleId);
} }
@ -109,24 +149,28 @@ function loadModuleImplementation(moduleId, module) {
const {factory} = module; const {factory} = module;
try { try {
if (__DEV__) { if (__DEV__) {
// $FlowFixMe: we know that __DEV__ is const and `Systrace` exists
Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId)); Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId));
} }
const moduleObject = {exports}; const moduleObject: Module = {exports};
if (__DEV__ && module.hot) { if (__DEV__ && module.hot) {
moduleObject.hot = module.hot; moduleObject.hot = module.hot;
} }
// keep args in sync with with defineModuleCode in // keep args in sync with with defineModuleCode in
// packager/react-packager/src/Resolver/index.js // packager/react-packager/src/Resolver/index.js
// and packager/react-packager/src/ModuleGraph/worker.js
factory(global, require, moduleObject, exports); factory(global, require, moduleObject, exports);
// avoid removing factory in DEV mode as it breaks HMR // avoid removing factory in DEV mode as it breaks HMR
if (!__DEV__) { if (!__DEV__) {
// $FlowFixMe: This is only sound because we never access `factory` again
module.factory = undefined; module.factory = undefined;
} }
if (__DEV__) { if (__DEV__) {
// $FlowFixMe: we know that __DEV__ is const and `Systrace` exists
Systrace.endEvent(); Systrace.endEvent();
} }
return (module.exports = moduleObject.exports); return (module.exports = moduleObject.exports);
@ -156,14 +200,17 @@ if (__DEV__) {
// HOT MODULE RELOADING // HOT MODULE RELOADING
var createHotReloadingObject = function() { var createHotReloadingObject = function() {
const hot = { const hot: HotModuleReloadingData = {
acceptCallback: null, acceptCallback: null,
accept: callback => { hot.acceptCallback = callback; }, accept: callback => { hot.acceptCallback = callback; },
}; };
return hot; return hot;
}; };
const acceptAll = function(dependentModules, inverseDependencies) { const acceptAll = function(
dependentModules,
inverseDependencies,
) {
if (!dependentModules || dependentModules.length === 0) { if (!dependentModules || dependentModules.length === 0) {
return true; return true;
} }
@ -178,16 +225,20 @@ if (__DEV__) {
return false; return false;
} }
parents.pushAll(inverseDependencies[notAccepted[i]]); parents.push(...inverseDependencies[notAccepted[i]]);
} }
return acceptAll(parents, inverseDependencies); return acceptAll(parents, inverseDependencies);
}; };
const accept = function(id, factory, inverseDependencies) { const accept = function(
id: ModuleID,
factory?: FactoryFn,
inverseDependencies: {[key: ModuleID]: Array<ModuleID>},
) {
const mod = modules[id]; const mod = modules[id];
if (!mod) { if (!mod && factory) { // new modules need a factory
define(id, factory); define(id, factory);
return true; // new modules don't need to be accepted return true; // new modules don't need to be accepted
} }