react/scripts/rollup/modules.js

320 lines
8.8 KiB
JavaScript

'use strict';
const resolve = require('path').resolve;
const basename = require('path').basename;
const sync = require('glob').sync;
const bundleTypes = require('./bundles').bundleTypes;
const extractErrorCodes = require('../error-codes/extract-errors');
const exclude = [
'src/**/__benchmarks__/**/*.js',
'src/**/__tests__/**/*.js',
'src/**/__mocks__/**/*.js',
];
const UMD_DEV = bundleTypes.UMD_DEV;
const UMD_PROD = bundleTypes.UMD_PROD;
const NODE_DEV = bundleTypes.NODE_DEV;
const NODE_PROD = bundleTypes.NODE_PROD;
const FB_DEV = bundleTypes.FB_DEV;
const FB_PROD = bundleTypes.FB_PROD;
const RN_DEV = bundleTypes.RN_DEV;
const RN_PROD = bundleTypes.RN_PROD;
const errorCodeOpts = {
errorMapFilePath: 'scripts/error-codes/codes.json',
};
// these are the FBJS modules that are used throughout our bundles
const fbjsModules = [
'fbjs/lib/warning',
'fbjs/lib/invariant',
'fbjs/lib/emptyFunction',
'fbjs/lib/emptyObject',
'fbjs/lib/hyphenateStyleName',
'fbjs/lib/getUnboundedScrollPosition',
'fbjs/lib/camelizeStyleName',
'fbjs/lib/containsNode',
'fbjs/lib/shallowEqual',
'fbjs/lib/getActiveElement',
'fbjs/lib/focusNode',
'fbjs/lib/EventListener',
'fbjs/lib/memoizeStringOnly',
'fbjs/lib/ExecutionEnvironment',
'fbjs/lib/createNodesFromMarkup',
'fbjs/lib/performanceNow',
];
const devOnlyFilesToStubOut = [
"'ReactDebugCurrentFrame'",
"'ReactComponentTreeHook'",
"'ReactPerf'",
"'ReactTestUtils'",
];
const legacyModules = [
'create-react-class',
'create-react-class/factory',
'prop-types',
'prop-types/checkPropTypes',
];
// this function builds up a very niave Haste-like moduleMap
// that works to create up an alias map for modules to link
// up to their actual disk location so Rollup can properly
// bundle them
function createModuleMap(paths, extractErrors, bundleType) {
const moduleMap = {};
paths.forEach(path => {
const files = sync(path, {ignore: exclude});
files.forEach(file => {
if (extractErrors) {
extractErrors(file);
}
const moduleName = basename(file, '.js');
moduleMap[moduleName] = resolve(file);
});
});
// if this is FB, we want to remove ReactCurrentOwner and lowPriorityWarning,
// so we can handle it with a different case
if (bundleType === FB_DEV || bundleType === FB_PROD) {
delete moduleMap.ReactCurrentOwner;
delete moduleMap.lowPriorityWarning;
}
return moduleMap;
}
function getNodeModules(bundleType, isRenderer) {
// rather than adding the rollup node resolve plugin,
// we can instead deal with the only node module that is used
// for UMD bundles - object-assign
switch (bundleType) {
case UMD_DEV:
case UMD_PROD:
return {
// Bundle object-assign once in the isomorphic React, and then use
// that from the renderer UMD. Avoids bundling it in both UMDs.
'object-assign': isRenderer
? resolve('./scripts/rollup/shims/rollup/assign.js')
: resolve('./node_modules/object-assign/index.js'),
// include the ART package modules directly by aliasing them from node_modules
'art/modes/current': resolve('./node_modules/art/modes/current.js'),
'art/modes/fast-noSideEffects': resolve(
'./node_modules/art/modes/fast-noSideEffects.js'
),
'art/core/transform': resolve('./node_modules/art/core/transform.js'),
};
case NODE_DEV:
case NODE_PROD:
case FB_DEV:
case FB_PROD:
case RN_DEV:
case RN_PROD:
return {};
}
}
function ignoreFBModules() {
return [
// At FB, we don't know them statically:
'ReactFeatureFlags',
'ReactDOMFeatureFlags',
// In FB bundles, we preserve an inline require to ReactCurrentOwner.
// See the explanation in FB version of ReactCurrentOwner in www:
'ReactCurrentOwner',
'lowPriorityWarning',
];
}
function ignoreReactNativeModules() {
return [
// This imports NativeMethodsMixin, causing a circular dependency.
'View',
];
}
function getExternalModules(externals, bundleType, isRenderer) {
// external modules tell Rollup that we should not attempt
// to bundle these modules and instead treat them as
// external depedencies to the bundle. so for CJS bundles
// this means having a require("name-of-external-module") at
// the top of the bundle. for UMD bundles this means having
// both a require and a global check for them
let externalModules = externals.slice();
switch (bundleType) {
case UMD_DEV:
case UMD_PROD:
if (isRenderer) {
externalModules.push('react');
}
break;
case NODE_DEV:
case NODE_PROD:
case RN_DEV:
case RN_PROD:
fbjsModules.forEach(module => externalModules.push(module));
externalModules.push('object-assign');
if (isRenderer) {
externalModules.push('react');
}
break;
case FB_DEV:
case FB_PROD:
fbjsModules.forEach(module => externalModules.push(module));
externalModules.push('object-assign');
externalModules.push('ReactCurrentOwner');
externalModules.push('lowPriorityWarning');
if (isRenderer) {
externalModules.push('React');
if (externalModules.indexOf('react-dom') > -1) {
externalModules.push('ReactDOM');
}
}
break;
}
return externalModules;
}
function getInternalModules() {
// we tell Rollup where these files are located internally, otherwise
// it doesn't pick them up and assumes they're external
return {
reactProdInvariant: resolve('./src/shared/utils/reactProdInvariant.js'),
};
}
function getFbjsModuleAliases(bundleType) {
switch (bundleType) {
case UMD_DEV:
case UMD_PROD:
// we want to bundle these modules, so we re-alias them to the actual
// file so Rollup can bundle them up
const fbjsModulesAlias = {};
fbjsModules.forEach(fbjsModule => {
fbjsModulesAlias[fbjsModule] = resolve(`./node_modules/${fbjsModule}`);
});
return fbjsModulesAlias;
case NODE_DEV:
case NODE_PROD:
case FB_DEV:
case FB_PROD:
case RN_DEV:
case RN_PROD:
// for FB we don't want to bundle the above modules, instead keep them
// as external require() calls in the bundle
return {};
}
}
function replaceFbjsModuleAliases(bundleType) {
switch (bundleType) {
case FB_DEV:
case FB_PROD:
// Haste at FB doesn't currently allow case sensitive names,
// and product code already uses "React". In the future,
// we will either allow both variants or migrate to lowercase.
return {
"'react'": "'React'",
"'react-dom'": "'ReactDOM'",
};
default:
return {};
}
}
const devOnlyModuleStub = `'${resolve('./scripts/rollup/shims/rollup/DevOnlyStubShim.js')}'`;
function replaceDevOnlyStubbedModules(bundleType) {
switch (bundleType) {
case UMD_DEV:
case NODE_DEV:
case FB_DEV:
case RN_DEV:
case RN_PROD:
return {};
case FB_PROD:
case UMD_PROD:
case NODE_PROD:
const devOnlyModuleAliases = {};
devOnlyFilesToStubOut.forEach(devOnlyModule => {
devOnlyModuleAliases[devOnlyModule] = devOnlyModuleStub;
});
return devOnlyModuleAliases;
}
}
function replaceLegacyModuleAliases(bundleType) {
switch (bundleType) {
case UMD_DEV:
case UMD_PROD:
const modulesAlias = {};
legacyModules.forEach(legacyModule => {
const modulePath = legacyModule.includes('/')
? legacyModule
: `${legacyModule}/index`;
const resolvedPath = resolve(`./node_modules/${modulePath}`);
modulesAlias[`'${legacyModule}'`] = `'${resolvedPath}'`;
});
return modulesAlias;
case NODE_DEV:
case NODE_PROD:
case FB_DEV:
case FB_PROD:
case RN_DEV:
case RN_PROD:
return {};
}
}
function replaceBundleStubModules(bundleModulesToStub) {
const stubbedModules = {};
if (Array.isArray(bundleModulesToStub)) {
bundleModulesToStub.forEach(module => {
stubbedModules[`'${module}'`] = devOnlyModuleStub;
});
}
return stubbedModules;
}
function getAliases(paths, bundleType, isRenderer, extractErrors) {
return Object.assign(
createModuleMap(
paths,
extractErrors && extractErrorCodes(errorCodeOpts),
bundleType
),
getInternalModules(),
getNodeModules(bundleType, isRenderer),
getFbjsModuleAliases(bundleType)
);
}
function getDefaultReplaceModules(bundleType, bundleModulesToStub) {
return Object.assign(
{},
replaceFbjsModuleAliases(bundleType),
replaceDevOnlyStubbedModules(bundleType),
replaceLegacyModuleAliases(bundleType),
replaceBundleStubModules(bundleModulesToStub)
);
}
function getExcludedHasteGlobs() {
return exclude;
}
module.exports = {
getExcludedHasteGlobs,
getDefaultReplaceModules,
getAliases,
ignoreFBModules,
ignoreReactNativeModules,
getExternalModules,
};