Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify the way we fork modules #11711

Merged
merged 5 commits into from
Nov 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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/.*
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var React;
var ReactNativeCS;

jest.mock('shared/ReactFeatureFlags', () =>
require('../ReactNativeCSFeatureFlags'),
require('shared/forks/ReactFeatureFlags.native-cs'),
);

describe('ReactNativeCS', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
41 changes: 19 additions & 22 deletions scripts/rollup/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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`.'
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -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());
Expand Down
2 changes: 0 additions & 2 deletions scripts/rollup/bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ const bundles = [
'deepFreezeAndThrowOnMutationInDev',
'flattenStyle',
],
featureFlags: 'react-native-renderer/src/ReactNativeFeatureFlags',
},

/******* React Native RT *******/
Expand All @@ -156,7 +155,6 @@ const bundles = [
entry: 'react-cs-renderer',
global: 'ReactCSRenderer',
externals: ['CSStatefulComponent'],
featureFlags: 'react-cs-renderer/src/ReactNativeCSFeatureFlags',
},

/******* React Test Renderer *******/
Expand Down
71 changes: 71 additions & 0 deletions scripts/rollup/forks.js
Original file line number Diff line number Diff line change
@@ -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;
97 changes: 23 additions & 74 deletions scripts/rollup/modules.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 = {};
Expand All @@ -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;
}

function getImportSideEffects() {
return importSideEffects;
// 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;
}

// 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;
function getImportSideEffects() {
return importSideEffects;
}

module.exports = {
getImportSideEffects,
getPeerGlobals,
getDependencies,
getShims,
getForks,
};
Loading