'use strict'; const rollup = require('rollup').rollup; const babel = require('rollup-plugin-babel'); const commonjs = require('rollup-plugin-commonjs'); const alias = require('rollup-plugin-alias'); const uglify = require('rollup-plugin-uglify'); const replace = require('rollup-plugin-replace'); const chalk = require('chalk'); const join = require('path').join; const resolve = require('path').resolve; const resolvePlugin = 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 Stats = require('./stats'); const extractErrorCodes = require('../error-codes/extract-errors'); const syncReactDom = require('./sync').syncReactDom; const syncReactNative = require('./sync').syncReactNative; const syncReactNativeRT = require('./sync').syncReactNativeRT; const syncReactNativeCS = require('./sync').syncReactNativeCS; const Packaging = require('./packaging'); const Header = require('./header'); const closure = require('rollup-plugin-closure-compiler-js'); const UMD_DEV = Bundles.bundleTypes.UMD_DEV; const UMD_PROD = Bundles.bundleTypes.UMD_PROD; const NODE_DEV = Bundles.bundleTypes.NODE_DEV; const NODE_PROD = Bundles.bundleTypes.NODE_PROD; const FB_DEV = Bundles.bundleTypes.FB_DEV; const FB_PROD = Bundles.bundleTypes.FB_PROD; const RN_DEV = Bundles.bundleTypes.RN_DEV; const RN_PROD = Bundles.bundleTypes.RN_PROD; const RECONCILER = Bundles.moduleTypes.RECONCILER; const reactVersion = require('../../package.json').version; const requestedBundleTypes = (argv.type || '') .split(',') .map(type => type.toUpperCase()); const requestedBundleNames = (argv._[0] || '') .split(',') .map(type => type.toLowerCase()); const syncFbsource = argv['sync-fbsource']; const syncWww = argv['sync-www']; const shouldExtractErrors = argv['extract-errors']; const errorCodeOpts = { errorMapFilePath: 'scripts/error-codes/codes.json', }; function getHeaderSanityCheck(bundleType, globalName) { switch (bundleType) { case FB_DEV: case FB_PROD: case RN_DEV: case RN_PROD: let hasteFinalName = globalName; switch (bundleType) { case FB_DEV: case RN_DEV: hasteFinalName += '-dev'; break; case FB_PROD: case RN_PROD: hasteFinalName += '-prod'; break; } return hasteFinalName; case UMD_DEV: case UMD_PROD: return reactVersion; default: return null; } } function getBanner(bundleType, globalName, filename, moduleType) { if (moduleType === RECONCILER) { // Standalone reconciler is only used by third-party renderers. // It is handled separately. return getReconcilerBanner(bundleType, filename); } switch (bundleType) { // UMDs are not wrapped in conditions. case UMD_DEV: case UMD_PROD: return Header.getHeader(filename, reactVersion); // CommonJS DEV bundle is guarded to help weak dead code elimination. case NODE_DEV: let banner = Header.getHeader(filename, reactVersion); // Wrap the contents of the if-DEV check with an IIFE. // Block-level function definitions can cause problems for strict mode. banner += `'use strict';\n\n\nif (process.env.NODE_ENV !== "production") {\n(function() {\n`; return banner; case NODE_PROD: return Header.getHeader(filename, reactVersion); // All FB and RN bundles need Haste headers. // DEV bundle is guarded to help weak dead code elimination. case FB_DEV: case FB_PROD: case RN_DEV: case RN_PROD: const isDev = bundleType === FB_DEV || bundleType === RN_DEV; const hasteFinalName = globalName + (isDev ? '-dev' : '-prod'); // Wrap the contents of the if-DEV check with an IIFE. // Block-level function definitions can cause problems for strict mode. return ( Header.getProvidesHeader(hasteFinalName) + (isDev ? `\n\n'use strict';\n\n\nif (__DEV__) {\n(function() {\n` : '') ); default: throw new Error('Unknown type.'); } } function getFooter(bundleType, filename, moduleType) { if (moduleType === RECONCILER) { // Standalone reconciler is only used by third-party renderers. // It is handled separately. return getReconcilerFooter(bundleType); } // Only need a footer if getBanner() has an opening brace. switch (bundleType) { // Non-UMD DEV bundles need conditions to help weak dead code elimination. case NODE_DEV: case FB_DEV: case RN_DEV: return '\n})();\n}\n'; default: return ''; } } // TODO: this is extremely gross. // But it only affects the "experimental" standalone reconciler build. // The goal is to avoid having any shared state between renderers sharing it on npm. // Ideally we should just remove shared state in all Fiber modules and then lint against it. // But for now, we store the exported function in a variable, and then put the rest of the code // into a closure that makes all module-level state private to each call. const RECONCILER_WRAPPER_INTRO = `var $$$reconciler;\nmodule.exports = function(config) {\n`; const RECONCILER_WRAPPER_OUTRO = `return ($$$reconciler || ($$$reconciler = module.exports))(config);\n};\n`; function getReconcilerBanner(bundleType, filename) { let banner = `${Header.getHeader(filename, reactVersion)}\n\n'use strict';\n\n\n`; switch (bundleType) { case NODE_DEV: banner += `if (process.env.NODE_ENV !== "production") {\n${RECONCILER_WRAPPER_INTRO}`; break; case NODE_PROD: banner += RECONCILER_WRAPPER_INTRO; break; default: throw new Error( 'Standalone reconciler does not support ' + bundleType + ' builds.' ); } return banner; } function getReconcilerFooter(bundleType) { switch (bundleType) { case NODE_DEV: return `\n${RECONCILER_WRAPPER_OUTRO}\n}`; case NODE_PROD: return `\n${RECONCILER_WRAPPER_OUTRO}`; default: throw new Error( 'Standalone reconciler does not support ' + bundleType + ' builds.' ); } } function getBabelConfig(updateBabelOptions, bundleType, filename) { let options = { exclude: 'node_modules/**', presets: [], plugins: [], }; if (updateBabelOptions) { options = updateBabelOptions(options); } switch (bundleType) { case FB_DEV: case FB_PROD: case RN_DEV: case RN_PROD: return Object.assign({}, options, { plugins: options.plugins.concat([ // Wrap warning() calls in a __DEV__ check so they are stripped from production. require('./plugins/wrap-warning-with-env-check'), ]), }); case UMD_DEV: case UMD_PROD: case NODE_DEV: case NODE_PROD: return Object.assign({}, options, { plugins: options.plugins.concat([ // Use object-assign polyfill in open source 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. require('./plugins/wrap-warning-with-env-check'), ]), }); default: return options; } } function handleRollupWarnings(warning) { if (warning.code === 'UNRESOLVED_IMPORT') { console.error(warning.message); process.exit(1); } if (warning.code === 'UNUSED_EXTERNAL_IMPORT') { const match = warning.message.match(/external module '([^']+)'/); if (!match || typeof match[1] !== 'string') { throw new Error( 'Could not parse a Rollup warning. ' + 'Fix this method.' ); } const importSideEffects = Modules.getImportSideEffects(); const path = match[1]; if (typeof importSideEffects[path] !== 'boolean') { throw new Error( 'An external module "' + path + '" 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`.' ); } // Don't warn. We will remove side effectless require() in a later pass. return; } console.warn(warning.message || warning); } function getRollupOutputOptions( filename, format, bundleType, globals, globalName, moduleType ) { return Object.assign( {}, { banner: getBanner(bundleType, globalName, filename, moduleType), destDir: 'build/', file: 'build/' + Packaging.getOutputPathRelativeToBuildFolder( bundleType, filename, globalName ), footer: getFooter(bundleType, filename, moduleType), format, globals, interop: false, name: globalName, sourcemap: false, } ); } function stripEnvVariables(production) { return { __DEV__: production ? 'false' : 'true', 'process.env.NODE_ENV': production ? "'production'" : "'development'", }; } function getFormat(bundleType) { switch (bundleType) { case UMD_DEV: case UMD_PROD: return `umd`; case NODE_DEV: case NODE_PROD: case FB_DEV: case FB_PROD: case RN_DEV: case RN_PROD: return `cjs`; } } function getFilename(name, globalName, bundleType) { // we do this to replace / to -, for react-dom/server name = name.replace('/', '-'); switch (bundleType) { case UMD_DEV: return `${name}.development.js`; case UMD_PROD: return `${name}.production.min.js`; case NODE_DEV: return `${name}.development.js`; case NODE_PROD: return `${name}.production.min.js`; case FB_DEV: case RN_DEV: return `${globalName}-dev.js`; case FB_PROD: case RN_PROD: return `${globalName}-prod.js`; } } function getUglifyConfig(configs) { var mangle = configs.mangle; var preserveVersionHeader = configs.preserveVersionHeader; var removeComments = configs.removeComments; var headerSanityCheck = configs.headerSanityCheck; return { warnings: false, compress: { screw_ie8: true, dead_code: true, unused: true, drop_debugger: true, // we have a string literal