mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Unify the way we fork modules (#11711)
* Unify the way we fork modules * Replace rollup-plugin-alias with our own plugin This does exactly what we need and doesn't suffer from https://github.com/rollup/rollup-plugin-alias/issues/34. * Move the new plugin to its own file * Rename variable for consistency I settled on calling them "forks" since we already have a different concept of "shims". * Move fork config into its own file
This commit is contained in:
@@ -8,9 +8,6 @@
|
||||
<PROJECT_ROOT>/scripts/rollup/shims/facebook-www/.*
|
||||
<PROJECT_ROOT>/scripts/rollup/shims/react-native/.*
|
||||
|
||||
# Note: intentionally *don't* ignore /scripts/rollup/shims/rollup/
|
||||
# because it is part of the build and isn't external.
|
||||
|
||||
<PROJECT_ROOT>/.*/node_modules/y18n/.*
|
||||
<PROJECT_ROOT>/node_modules/chrome-devtools-frontend/.*
|
||||
<PROJECT_ROOT>/node_modules/devtools-timeline-model/.*
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
"prop-types": "^15.6.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"rollup": "^0.51.7",
|
||||
"rollup-plugin-alias": "^1.2.1",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-closure-compiler-js": "^1.0.4",
|
||||
"rollup-plugin-commonjs": "^8.2.6",
|
||||
|
||||
@@ -13,7 +13,7 @@ var React;
|
||||
var ReactNativeCS;
|
||||
|
||||
jest.mock('shared/ReactFeatureFlags', () =>
|
||||
require('../ReactNativeCSFeatureFlags'),
|
||||
require('shared/forks/ReactFeatureFlags.native-cs'),
|
||||
);
|
||||
|
||||
describe('ReactNativeCS', () => {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as CSFeatureFlagsType from './ReactNativeCSFeatureFlags';
|
||||
import typeof * as CSFeatureFlagsType from './ReactFeatureFlags.native-cs';
|
||||
|
||||
export const debugRenderPhaseSideEffects = false;
|
||||
export const enableAsyncSubtreeAPI = true;
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as FeatureFlagsShimType from './ReactNativeFeatureFlags';
|
||||
import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native';
|
||||
|
||||
// Re-export dynamic flags from the fbsource version.
|
||||
export const {debugRenderPhaseSideEffects} = require('ReactFeatureFlags');
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as FeatureFlagsShimType from './ReactFeatureFlags-www';
|
||||
import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.www';
|
||||
|
||||
// Re-export dynamic flags from the www version.
|
||||
export const {
|
||||
+19
-22
@@ -4,20 +4,19 @@ const rollup = require('rollup').rollup;
|
||||
const babel = require('rollup-plugin-babel');
|
||||
const closure = require('rollup-plugin-closure-compiler-js');
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const alias = require('rollup-plugin-alias');
|
||||
const prettier = require('rollup-plugin-prettier');
|
||||
const replace = require('rollup-plugin-replace');
|
||||
const stripBanner = require('rollup-plugin-strip-banner');
|
||||
const chalk = require('chalk');
|
||||
const join = require('path').join;
|
||||
const resolve = require('path').resolve;
|
||||
const resolvePlugin = require('rollup-plugin-node-resolve');
|
||||
const path = require('path');
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const fs = require('fs');
|
||||
const rimraf = require('rimraf');
|
||||
const argv = require('minimist')(process.argv.slice(2));
|
||||
const Modules = require('./modules');
|
||||
const Bundles = require('./bundles');
|
||||
const sizes = require('./plugins/sizes-plugin');
|
||||
const useForks = require('./plugins/use-forks-plugin');
|
||||
const Stats = require('./stats');
|
||||
const extractErrorCodes = require('../error-codes/extract-errors');
|
||||
const syncReactDom = require('./sync').syncReactDom;
|
||||
@@ -88,7 +87,7 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) {
|
||||
return Object.assign({}, options, {
|
||||
plugins: options.plugins.concat([
|
||||
// Use object-assign polyfill in open source
|
||||
resolve('./scripts/babel/transform-object-assign-require'),
|
||||
path.resolve('./scripts/babel/transform-object-assign-require'),
|
||||
// Minify invariant messages
|
||||
require('../error-codes/replace-invariant-error-codes'),
|
||||
// Wrap warning() calls in a __DEV__ check so they are stripped from production.
|
||||
@@ -113,11 +112,11 @@ function handleRollupWarnings(warning) {
|
||||
);
|
||||
}
|
||||
const importSideEffects = Modules.getImportSideEffects();
|
||||
const path = match[1];
|
||||
if (typeof importSideEffects[path] !== 'boolean') {
|
||||
const externalModule = match[1];
|
||||
if (typeof importSideEffects[externalModule] !== 'boolean') {
|
||||
throw new Error(
|
||||
'An external module "' +
|
||||
path +
|
||||
externalModule +
|
||||
'" is used in a DEV-only code path ' +
|
||||
'but we do not know if it is safe to omit an unused require() to it in production. ' +
|
||||
'Please add it to the `importSideEffects` list in `scripts/rollup/modules.js`.'
|
||||
@@ -218,11 +217,10 @@ function getPlugins(
|
||||
bundleType,
|
||||
globalName,
|
||||
moduleType,
|
||||
modulesToStub,
|
||||
featureFlags
|
||||
modulesToStub
|
||||
) {
|
||||
const findAndRecordErrorCodes = extractErrorCodes(errorCodeOpts);
|
||||
const shims = Modules.getShims(bundleType, entry, featureFlags);
|
||||
const forks = Modules.getForks(bundleType, entry);
|
||||
const isProduction = isProductionBundleType(bundleType);
|
||||
const isInGlobalScope = bundleType === UMD_DEV || bundleType === UMD_PROD;
|
||||
const isFBBundle = bundleType === FB_DEV || bundleType === FB_PROD;
|
||||
@@ -236,10 +234,10 @@ function getPlugins(
|
||||
return source;
|
||||
},
|
||||
},
|
||||
// Shim some modules for www custom behavior and optimizations.
|
||||
alias(shims),
|
||||
// Shim any modules that need forking in this environment.
|
||||
useForks(forks),
|
||||
// Use Node resolution mechanism.
|
||||
resolvePlugin({
|
||||
resolve({
|
||||
skip: externals,
|
||||
}),
|
||||
// Remove license headers from individual modules
|
||||
@@ -384,8 +382,7 @@ async function createBundle(bundle, bundleType) {
|
||||
bundleType,
|
||||
bundle.global,
|
||||
bundle.moduleType,
|
||||
bundle.modulesToStub,
|
||||
bundle.featureFlags
|
||||
bundle.modulesToStub
|
||||
),
|
||||
// We can't use getters in www.
|
||||
legacy: bundleType === FB_DEV || bundleType === FB_PROD,
|
||||
@@ -439,9 +436,9 @@ rimraf('build', async () => {
|
||||
// create a new build directory
|
||||
fs.mkdirSync('build');
|
||||
// create the packages folder for NODE+UMD bundles
|
||||
fs.mkdirSync(join('build', 'packages'));
|
||||
fs.mkdirSync(path.join('build', 'packages'));
|
||||
// create the dist folder for UMD bundles
|
||||
fs.mkdirSync(join('build', 'dist'));
|
||||
fs.mkdirSync(path.join('build', 'dist'));
|
||||
|
||||
await Packaging.createFacebookWWWBuild();
|
||||
await Packaging.createReactNativeBuild();
|
||||
@@ -462,11 +459,11 @@ rimraf('build', async () => {
|
||||
}
|
||||
|
||||
if (syncFbsource) {
|
||||
await syncReactNative(join('build', 'react-native'), syncFbsource);
|
||||
await syncReactNativeRT(join('build', 'react-rt'), syncFbsource);
|
||||
await syncReactNativeCS(join('build', 'react-cs'), syncFbsource);
|
||||
await syncReactNative(path.join('build', 'react-native'), syncFbsource);
|
||||
await syncReactNativeRT(path.join('build', 'react-rt'), syncFbsource);
|
||||
await syncReactNativeCS(path.join('build', 'react-cs'), syncFbsource);
|
||||
} else if (syncWww) {
|
||||
await syncReactDom(join('build', 'facebook-www'), syncWww);
|
||||
await syncReactDom(path.join('build', 'facebook-www'), syncWww);
|
||||
}
|
||||
|
||||
console.log(Stats.printResults());
|
||||
|
||||
@@ -129,7 +129,6 @@ const bundles = [
|
||||
'deepFreezeAndThrowOnMutationInDev',
|
||||
'flattenStyle',
|
||||
],
|
||||
featureFlags: 'react-native-renderer/src/ReactNativeFeatureFlags',
|
||||
},
|
||||
|
||||
/******* React Native RT *******/
|
||||
@@ -156,7 +155,6 @@ const bundles = [
|
||||
entry: 'react-cs-renderer',
|
||||
global: 'ReactCSRenderer',
|
||||
externals: ['CSStatefulComponent'],
|
||||
featureFlags: 'react-cs-renderer/src/ReactNativeCSFeatureFlags',
|
||||
},
|
||||
|
||||
/******* React Test Renderer *******/
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
const bundleTypes = require('./bundles').bundleTypes;
|
||||
|
||||
const UMD_DEV = bundleTypes.UMD_DEV;
|
||||
const UMD_PROD = bundleTypes.UMD_PROD;
|
||||
const FB_DEV = bundleTypes.FB_DEV;
|
||||
const FB_PROD = bundleTypes.FB_PROD;
|
||||
|
||||
// If you need to replace a file with another file for a specific environment,
|
||||
// add it to this list with the logic for choosing the right replacement.
|
||||
const forks = Object.freeze({
|
||||
// Optimization: for UMDs, use object-assign polyfill that is already a part
|
||||
// of the React package instead of bundling it again.
|
||||
'object-assign': (bundleType, entry, dependencies) => {
|
||||
if (bundleType !== UMD_DEV && bundleType !== UMD_PROD) {
|
||||
// It's only relevant for UMD bundles since that's where the duplication
|
||||
// happens. Other bundles just require('object-assign') anyway.
|
||||
return null;
|
||||
}
|
||||
if (dependencies.indexOf('react') === -1) {
|
||||
// We can only apply the optimizations to bundle that depend on React
|
||||
// because we read assign() from an object exposed on React internals.
|
||||
return null;
|
||||
}
|
||||
// We can use the fork!
|
||||
return 'shared/forks/object-assign.umd.js';
|
||||
},
|
||||
|
||||
// We have a few forks for different environments.
|
||||
'shared/ReactFeatureFlags': (bundleType, entry) => {
|
||||
switch (entry) {
|
||||
case 'react-native-renderer':
|
||||
return 'shared/forks/ReactFeatureFlags.native.js';
|
||||
case 'react-cs-renderer':
|
||||
return 'shared/forks/ReactFeatureFlags.native-cs.js';
|
||||
default:
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case FB_PROD:
|
||||
return 'shared/forks/ReactFeatureFlags.www.js';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// This logic is forked on www to blacklist warnings.
|
||||
'shared/lowPriorityWarning': (bundleType, entry) => {
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case FB_PROD:
|
||||
return 'shared/forks/lowPriorityWarning.www.js';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// In FB bundles, we preserve an inline require to ReactCurrentOwner.
|
||||
// See the explanation in FB version of ReactCurrentOwner in www:
|
||||
'react/src/ReactCurrentOwner': (bundleType, entry) => {
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case FB_PROD:
|
||||
return 'react/src/forks/ReactCurrentOwner.www.js';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = forks;
|
||||
+23
-74
@@ -1,31 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const forks = require('./forks');
|
||||
const bundleTypes = require('./bundles').bundleTypes;
|
||||
|
||||
const UMD_DEV = bundleTypes.UMD_DEV;
|
||||
const UMD_PROD = bundleTypes.UMD_PROD;
|
||||
const FB_DEV = bundleTypes.FB_DEV;
|
||||
const FB_PROD = bundleTypes.FB_PROD;
|
||||
|
||||
// Bundles exporting globals that other modules rely on.
|
||||
const knownGlobals = Object.freeze({
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
});
|
||||
|
||||
// Redirect some modules to Haste forks in www.
|
||||
// Assuming their names in www are the same, and also match
|
||||
// imported names in corresponding ./shims/rollup/*-www.js shims.
|
||||
const forkedFBModules = Object.freeze([
|
||||
// At FB, we don't know them statically:
|
||||
'shared/ReactFeatureFlags',
|
||||
// This logic is also forked internally.
|
||||
'shared/lowPriorityWarning',
|
||||
// In FB bundles, we preserve an inline require to ReactCurrentOwner.
|
||||
// See the explanation in FB version of ReactCurrentOwner in www:
|
||||
'react/src/ReactCurrentOwner',
|
||||
]);
|
||||
|
||||
// For any external that is used in a DEV-only condition, explicitly
|
||||
// specify whether it has side effects during import or not. This lets
|
||||
@@ -41,6 +21,12 @@ const importSideEffects = Object.freeze({
|
||||
deepFreezeAndThrowOnMutationInDev: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
});
|
||||
|
||||
// Bundles exporting globals that other modules rely on.
|
||||
const knownGlobals = Object.freeze({
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
});
|
||||
|
||||
// Given ['react'] in bundle externals, returns { 'react': 'React' }.
|
||||
function getPeerGlobals(externals, moduleType) {
|
||||
const peerGlobals = {};
|
||||
@@ -62,72 +48,35 @@ function getDependencies(bundleType, entry) {
|
||||
path.dirname(require.resolve(entry))
|
||||
) + '/package.json');
|
||||
// Both deps and peerDeps are assumed as accessible.
|
||||
let deps = Array.from(
|
||||
return Array.from(
|
||||
new Set([
|
||||
...Object.keys(packageJson.dependencies || {}),
|
||||
...Object.keys(packageJson.peerDependencies || {}),
|
||||
])
|
||||
);
|
||||
// In www, forked modules are also require-able.
|
||||
if (bundleType === FB_DEV || bundleType === FB_PROD) {
|
||||
deps = [...deps, ...forkedFBModules.map(name => path.basename(name))];
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
// Hijacks some modules for optimization and integration reasons.
|
||||
function getForks(bundleType, entry) {
|
||||
const forksForBundle = {};
|
||||
Object.keys(forks).forEach(srcModule => {
|
||||
const dependencies = getDependencies(bundleType, entry);
|
||||
const targetModule = forks[srcModule](bundleType, entry, dependencies);
|
||||
if (targetModule === null) {
|
||||
return;
|
||||
}
|
||||
forksForBundle[srcModule] = targetModule;
|
||||
});
|
||||
return forksForBundle;
|
||||
}
|
||||
|
||||
function getImportSideEffects() {
|
||||
return importSideEffects;
|
||||
}
|
||||
|
||||
// Hijacks some modules for optimization and integration reasons.
|
||||
function getShims(bundleType, entry, featureFlags) {
|
||||
const shims = {};
|
||||
switch (bundleType) {
|
||||
case UMD_DEV:
|
||||
case UMD_PROD:
|
||||
if (getDependencies(bundleType, entry).indexOf('react') !== -1) {
|
||||
// Optimization: rely on object-assign polyfill that is already a part
|
||||
// of the React package instead of bundling it again.
|
||||
shims['object-assign'] = path.resolve(
|
||||
__dirname + '/shims/rollup/assign-umd.js'
|
||||
);
|
||||
}
|
||||
break;
|
||||
case FB_DEV:
|
||||
case FB_PROD:
|
||||
// FB forks a few modules in www that are usually bundled.
|
||||
// Instead of bundling them, they need to be kept as require()s in the
|
||||
// final bundles so that they import www modules with the same names.
|
||||
// Rollup doesn't make it very easy to rewrite and ignore such a require,
|
||||
// so we resort to using a shim that re-exports the www module, and then
|
||||
// treating shim's target destinations as external (see getDependencies).
|
||||
forkedFBModules.forEach(srcPath => {
|
||||
const fileName = path.parse(srcPath).name;
|
||||
const shimPath = path.resolve(
|
||||
__dirname + `/shims/rollup/${fileName}-www.js`
|
||||
);
|
||||
shims[srcPath] = shimPath;
|
||||
// <hack>
|
||||
// Unfortunately the above doesn't work for relative imports,
|
||||
// and Rollup isn't smart enough to understand they refer to the same file.
|
||||
// We should come up with a real fix for this, but for now this will do.
|
||||
// FIXME: this is gross.
|
||||
shims['./' + fileName] = shimPath;
|
||||
shims['../' + fileName] = shimPath;
|
||||
// We don't have deeper relative requires between source files.
|
||||
// </hack>
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (featureFlags) {
|
||||
shims['shared/ReactFeatureFlags'] = require.resolve(featureFlags);
|
||||
}
|
||||
return shims;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getImportSideEffects,
|
||||
getPeerGlobals,
|
||||
getDependencies,
|
||||
getShims,
|
||||
getForks,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const gzip = require('gzip-size');
|
||||
|
||||
module.exports = function sizes(options) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
let resolveCache = new Map();
|
||||
function useForks(forks) {
|
||||
let resolvedForks = {};
|
||||
Object.keys(forks).forEach(srcModule => {
|
||||
const targetModule = forks[srcModule];
|
||||
resolvedForks[require.resolve(srcModule)] = require.resolve(targetModule);
|
||||
});
|
||||
return {
|
||||
resolveId(importee, importer) {
|
||||
if (!importer || !importee) {
|
||||
return null;
|
||||
}
|
||||
let resolvedImportee = null;
|
||||
let cacheKey = `${importer}:::${importee}`;
|
||||
if (resolveCache.has(cacheKey)) {
|
||||
// Avoid hitting file system if possible.
|
||||
resolvedImportee = resolveCache.get(cacheKey);
|
||||
} else {
|
||||
try {
|
||||
resolvedImportee = require.resolve(importee, {
|
||||
paths: [path.dirname(importer)],
|
||||
});
|
||||
} catch (err) {
|
||||
// Not our fault, let Rollup fail later.
|
||||
}
|
||||
if (resolvedImportee) {
|
||||
resolveCache.set(cacheKey, resolvedImportee);
|
||||
}
|
||||
}
|
||||
if (resolvedImportee && resolvedForks.hasOwnProperty(resolvedImportee)) {
|
||||
// We found a fork!
|
||||
return resolvedForks[resolvedImportee];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = useForks;
|
||||
@@ -4122,12 +4122,6 @@ rimraf@^2.5.4:
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
|
||||
rollup-plugin-alias@^1.2.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-alias/-/rollup-plugin-alias-1.3.1.tgz#a9152fec4b6a6510dae93989517ca7853c32a6fa"
|
||||
dependencies:
|
||||
slash "^1.0.0"
|
||||
|
||||
rollup-plugin-babel@^2.7.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-2.7.1.tgz#16528197b0f938a1536f44683c7a93d573182f57"
|
||||
|
||||
Reference in New Issue
Block a user