From 6aa9936529bfc3224e7c5e8b4f2718ec8deae314 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 20 Mar 2017 14:52:03 +0000 Subject: [PATCH] improved how bundles are built by exposing externals and making the process less "magical", also tidied up code and added more comments --- scripts/rollup/build.js | 33 ++++---- scripts/rollup/bundles.js | 33 ++++++++ scripts/rollup/modules.js | 169 +++++++++++++++++++++++++++----------- 3 files changed, 173 insertions(+), 62 deletions(-) diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 14218114f6..cd5a08ba03 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -14,13 +14,14 @@ const rimraf = require('rimraf'); const argv = require('minimist')(process.argv.slice(2)); const { createModuleMap, - getExternalModules, + getNodeModules, getInternalModules, replaceInternalModules, getFbjsModuleAliases, replaceFbjsModuleAliases, ignoreFBModules, ignoreReactNativeModules, + getExternalModules, } = require('./modules'); const { bundles, @@ -31,7 +32,7 @@ function getAliases(paths, bundleType) { return Object.assign( createModuleMap(paths), getInternalModules(bundleType), - getExternalModules(bundleType), + getNodeModules(bundleType), getFbjsModuleAliases(bundleType) ); } @@ -82,7 +83,8 @@ function updateBabelConfig(babelOpts, bundleType) { function handleRollupWarnings(warning) { if (warning.code === 'UNRESOLVED_IMPORT') { - return; + console.error(warning.message); + process.exit(1); } console.warn(warning.message || warning); } @@ -217,6 +219,8 @@ function createBundle({ name, hasteName, bundleTypes: bundleTypesToUse, + isRenderer, + externals, }, bundleType) { if ((inputBundleType && inputBundleType !== bundleType) || bundleTypesToUse.indexOf(bundleType) === -1) { @@ -227,25 +231,26 @@ function createBundle({ const format = getFormat(bundleType); return rollup({ entry: bundleType === bundleTypes.FB ? fbEntry : entry, - plugins: getPlugins(entry, babelOpts, paths, filename, bundleType), + external: getExternalModules(externals, bundleType, isRenderer), onwarn: handleRollupWarnings, + plugins: getPlugins(entry, babelOpts, paths, filename, bundleType), }).then(({write}) => write( updateBundleConfig(config, filename, format, bundleType, hasteName) - )).catch(console.error); + )).catch(error => { + console.error(error); + process.exit(1); + }); } // clear the build folder rimraf(join('build', 'rollup'), () => { bundles.forEach(bundle => - createBundle(bundle, bundleTypes.DEV).then(() => - createBundle(bundle, bundleTypes.PROD).then(() => - createBundle(bundle, bundleTypes.NODE).then(() => - createBundle(bundle, bundleTypes.FB).then(() => - createBundle(bundle, bundleTypes.RN) - ) - ) - ) - ) + Promise.resolve() + .then(() => createBundle(bundle, bundleTypes.DEV)) + .then(() => createBundle(bundle, bundleTypes.PROD)) + .then(() => createBundle(bundle, bundleTypes.NODE)) + .then(() => createBundle(bundle, bundleTypes.FB)) + .then(() => createBundle(bundle, bundleTypes.RN)) ); }); diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 1141ace7e8..901e584b4d 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -35,8 +35,10 @@ const bundles = [ sourceMap: false, }, entry: 'src/umd/ReactUMDEntry.js', + externals: [], fbEntry: 'src/fb/ReactFBEntry.js', hasteName: 'React-fb', + isRenderer: false, name: 'react', paths: [ 'src/umd/ReactUMDEntry.js', @@ -64,8 +66,10 @@ const bundles = [ sourceMap: false, }, entry: 'src/umd/ReactDOMUMDEntry.js', + externals: [], fbEntry: 'src/fb/ReactDOMFBEntry.js', hasteName: 'ReactDOMStack-fb', + isRenderer: true, name: 'react-dom', paths: [ 'src/umd/ReactDOMUMDEntry.js', @@ -90,8 +94,10 @@ const bundles = [ sourceMap: false, }, entry: 'src/renderers/dom/fiber/ReactDOMFiber.js', + externals: [], fbEntry: 'src/fb/ReactDOMFiberFBEntry.js', hasteName: 'ReactDOMFiber-fb', + isRenderer: true, name: 'react-dom-fiber', paths: [ 'src/renderers/dom/**/*.js', @@ -116,8 +122,10 @@ const bundles = [ sourceMap: false, }, entry: 'src/umd/ReactDOMServerUMDEntry.js', + externals: [], fbEntry: 'src/umd/ReactDOMServerUMDEntry.js', hasteName: 'ReactDOMServerStack', + isRenderer: true, // TODO: this is taken. Do we change the build task // to understand react-dom/server? name: 'react-dom-server', @@ -146,8 +154,14 @@ const bundles = [ sourceMap: false, }, entry: 'src/renderers/art/ReactARTStack.js', + externals: [ + 'art/modes/current', + 'art/modes/fast-noSideEffects', + 'art/core/transform', + ], fbEntry: 'src/renderers/art/ReactARTStack.js', hasteName: 'ReactARTStack', + isRenderer: true, name: 'react-art', paths: [ // TODO: it relies on ReactDOMFrameScheduling. Need to move to shared/? @@ -171,8 +185,14 @@ const bundles = [ sourceMap: false, }, entry: 'src/renderers/art/ReactARTFiber.js', + externals: [ + 'art/modes/current', + 'art/modes/fast-noSideEffects', + 'art/core/transform', + ], fbEntry: 'src/renderers/art/ReactARTFiber.js', hasteName: 'ReactARTFiber', + isRenderer: true, name: 'react-art', paths: [ // TODO: it relies on ReactDOMFrameScheduling. Need to move to shared/? @@ -195,7 +215,20 @@ const bundles = [ sourceMap: false, }, entry: 'src/renderers/native/ReactNative.js', + externals: [ + 'InitializeCore', + 'RCTEventEmitter', + 'UIManager', + 'react/lib/ReactComponentTreeHook', + 'react/lib/checkPropTypes', + 'react/lib/ReactDebugCurrentFrame', + 'deepDiffer', + 'flattenStyle', + 'TextInputState', + 'deepFreezeAndThrowOnMutationInDev', + ], hasteName: 'ReactNative', + isRenderer: true, name: 'react-native-renderer', paths: [ 'src/renderers/native/**/*.js', diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index ad2461cea7..351ec957de 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -12,6 +12,31 @@ const exclude = [ 'src/**/__mocks__/**/*.js', ]; + +// 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', +]; + +// 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) { const moduleMap = {}; @@ -27,7 +52,10 @@ function createModuleMap(paths) { return moduleMap; } -function getExternalModules(bundleType) { +function getNodeModules(bundleType) { + // 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 bundleTypes.DEV: case bundleTypes.PROD: @@ -55,6 +83,77 @@ function ignoreFBModules() { ]; } +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 = []; + + switch (bundleType) { + case bundleTypes.DEV: + case bundleTypes.PROD: + if (isRenderer) { + externalModules = [ + 'react', + ]; + } + break; + case bundleTypes.NODE: + case bundleTypes.RN: + externalModules = [ + 'object-assign', + 'fbjs/lib/warning', + 'fbjs/lib/emptyObject', + 'fbjs/lib/emptyFunction', + 'fbjs/lib/invariant', + 'react/lib/ReactCurrentOwner', + ]; + + if (isRenderer) { + externalModules.push( + 'react', + 'ReactCurrentOwner', + ...fbjsModules + ); + } + break; + case bundleTypes.FB: + externalModules = [ + // note: we don't put "ReactCurrentOwner" in here + // as we we're ignoring it from being processed + // by rollup so it remains inline in the bundle + 'warning', + 'emptyObject', + 'emptyFunction', + 'invariant', + ]; + if (isRenderer) { + const replacedFbModuleAliases = replaceFbjsModuleAliases(bundleType); + const aliases = Object.keys(replacedFbModuleAliases).map( + alias => replacedFbModuleAliases[alias] + ); + externalModules.push( + 'React', + ...aliases + ); + } + break; + } + externalModules.push(...externals); + return externalModules; +} + function getCommonInternalModules() { // we tell Rollup where these files are located internally, otherwise // it doesn't pick them up and assumes they're external @@ -105,26 +204,14 @@ function getFbjsModuleAliases(bundleType) { switch (bundleType) { case bundleTypes.DEV: case bundleTypes.PROD: - return { - // we want to bundle these modules, so we re-alias them to the actual - // file so Rollup can bundle them up - 'fbjs/lib/warning': resolve('./node_modules/fbjs/lib/warning.js'), - 'fbjs/lib/invariant': resolve('./node_modules/fbjs/lib/invariant.js'), - 'fbjs/lib/emptyFunction': resolve('./node_modules/fbjs/lib/emptyFunction.js'), - 'fbjs/lib/emptyObject': resolve('./node_modules/fbjs/lib/emptyObject.js'), - 'fbjs/lib/hyphenateStyleName': resolve('./node_modules/fbjs/lib/hyphenateStyleName.js'), - 'fbjs/lib/getUnboundedScrollPosition': resolve('./node_modules/fbjs/lib/getUnboundedScrollPosition.js'), - 'fbjs/lib/camelizeStyleName': resolve('./node_modules/fbjs/lib/camelizeStyleName.js'), - 'fbjs/lib/containsNode': resolve('./node_modules/fbjs/lib/containsNode.js'), - 'fbjs/lib/shallowEqual': resolve('./node_modules/fbjs/lib/shallowEqual.js'), - 'fbjs/lib/getActiveElement': resolve('./node_modules/fbjs/lib/getActiveElement.js'), - 'fbjs/lib/focusNode': resolve('./node_modules/fbjs/lib/focusNode.js'), - 'fbjs/lib/EventListener': resolve('./node_modules/fbjs/lib/EventListener.js'), - 'fbjs/lib/memoizeStringOnly': resolve('./node_modules/fbjs/lib/memoizeStringOnly.js'), - 'fbjs/lib/ExecutionEnvironment': resolve('./node_modules/fbjs/lib/ExecutionEnvironment.js'), - 'fbjs/lib/createNodesFromMarkup': resolve('./node_modules/fbjs/lib/createNodesFromMarkup.js'), - 'fbjs/lib/performanceNow': resolve('./node_modules/fbjs/lib/performanceNow.js'), - }; + // 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 bundleTypes.NODE: case bundleTypes.FB: case bundleTypes.RN: @@ -134,14 +221,6 @@ function getFbjsModuleAliases(bundleType) { } } -function ignoreReactNativeModules() { - return [ - // This imports NativeMethodsMixin, causing - // a circular dependency. - 'View', - ]; -} - function replaceFbjsModuleAliases(bundleType) { switch (bundleType) { case bundleTypes.DEV: @@ -152,36 +231,30 @@ function replaceFbjsModuleAliases(bundleType) { case bundleTypes.FB: // the diff for Haste to support fbjs/lib/* hasn't landed, so this // re-aliases them back to the non fbjs/lib/* versions - return { - 'fbjs/lib/warning': 'warning', - 'fbjs/lib/invariant': 'invariant', - 'fbjs/lib/emptyFunction': 'emptyFunction', - 'fbjs/lib/emptyObject': 'emptyObject', - 'fbjs/lib/hyphenateStyleName': 'hyphenateStyleName', - 'fbjs/lib/getUnboundedScrollPosition': 'getUnboundedScrollPosition', - 'fbjs/lib/camelizeStyleName': 'camelizeStyleName', - 'fbjs/lib/containsNode': 'containsNode', - 'fbjs/lib/shallowEqual': 'shallowEqual', - 'fbjs/lib/getActiveElement': 'getActiveElement', - 'fbjs/lib/focusNode': 'focusNode', - 'fbjs/lib/EventListener': 'EventListener', - 'fbjs/lib/memoizeStringOnly': 'memoizeStringOnly', - 'fbjs/lib/ExecutionEnvironment': 'ExecutionEnvironment', - 'fbjs/lib/createNodesFromMarkup': 'createNodesFromMarkup', - 'fbjs/lib/performanceNow': 'performanceNow', + // to do this, we ge the fbjsModules list of names and put them in + // an alias object removing the fbjs/lib/ bit from the name + const fbjsModulesAlias = {}; + fbjsModules.forEach(fbjsModule => { + fbjsModulesAlias[fbjsModule] = fbjsModule.replace('fbjs/lib/', ''); + }); + + // additionally we add mappings for "ReactCurrentOwner" and "react" + // so they work correctly on FB + return Object.assign(fbjsModulesAlias, { 'react/lib/ReactCurrentOwner': 'ReactCurrentOwner', "'react'": "'React'", - }; + }); } } module.exports = { createModuleMap, - getExternalModules, + getNodeModules, replaceInternalModules, getInternalModules, getFbjsModuleAliases, replaceFbjsModuleAliases, ignoreFBModules, ignoreReactNativeModules, + getExternalModules, };