diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 24b007f69a2..ca1672d0878 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -8,6 +8,8 @@ const path = require('path'); +const getRequiredPlugins = require('./helpers/getRequiredPlugins'); + const validateBoolOption = (name, value, defaultValue) => { if (typeof value === 'undefined') { value = defaultValue; @@ -64,15 +66,24 @@ module.exports = function(api, opts, env) { ); } + const targets = isEnvTest ? { node: 'current' } : undefined; + + // Some plugins need custom overrides which inadvertantly enables them for + // browsers that don't really need them. Here we check whether these plugins + // are actually required and skip them if they are not. + const { + 'transform-destructuring': isDestructuringTransformRequired, + 'transform-regenerator': isRegeneratorTransformRequired, + 'proposal-object-rest-spread': isObjectRestSpreadProposalRequired, + } = getRequiredPlugins(targets); + return { presets: [ isEnvTest && [ // ES features necessary for user's Node version require('@babel/preset-env').default, { - targets: { - node: 'current', - }, + targets, }, ], (isEnvProduction || isEnvDevelopment) && [ @@ -96,9 +107,12 @@ module.exports = function(api, opts, env) { // Adds component stack to warning messages // Adds __self attribute to JSX which React will use for some warnings development: isEnvDevelopment || isEnvTest, - // Will use the native built-in instead of trying to polyfill + // Use native spread when possible. + // Or use the native built-in instead of trying to polyfill // behavior for any plugins that require one. - useBuiltIns: true, + [isObjectRestSpreadProposalRequired + ? 'useBuiltIns' + : 'useSpread']: true, }, ], isTypeScriptEnabled && [require('@babel/preset-typescript').default], @@ -117,10 +131,11 @@ module.exports = function(api, opts, env) { // Experimental macros support. Will be documented after it's had some time // in the wild. require('babel-plugin-macros'), - // Necessary to include regardless of the environment because - // in practice some other transforms (such as object-rest-spread) - // don't work without it: https://github.com/babel/babel/issues/7215 - [ + // Historically there was a bug that made this plugin required. + // https://github.com/babel/babel/issues/7215 + // This plugin is no longer required to make plugin-proposal-object-rest-spread work: + // https://github.com/babel/babel/pull/10275 + isDestructuringTransformRequired && [ require('@babel/plugin-transform-destructuring').default, { // Use loose mode for performance: @@ -156,15 +171,6 @@ module.exports = function(api, opts, env) { ], // Adds Numeric Separators require('@babel/plugin-proposal-numeric-separator').default, - // The following two plugins use Object.assign directly, instead of Babel's - // extends helper. Note that this assumes `Object.assign` is available. - // { ...todo, completed: true } - [ - require('@babel/plugin-proposal-object-rest-spread').default, - { - useBuiltIns: true, - }, - ], // Polyfills the runtime needed for async/await, generators, and friends // https://babeljs.io/docs/en/babel-plugin-transform-runtime [ @@ -176,7 +182,7 @@ module.exports = function(api, opts, env) { // explicitly resolving to match the provided helper functions. // https://github.com/babel/babel/issues/10261 version: require('@babel/runtime/package.json').version, - regenerator: true, + regenerator: isRegeneratorTransformRequired, // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules // We should turn this on once the lowest version of Node LTS // supports ES Modules. diff --git a/packages/babel-preset-react-app/dependencies.js b/packages/babel-preset-react-app/dependencies.js index 6d29ba69a4f..a2b94903fb1 100644 --- a/packages/babel-preset-react-app/dependencies.js +++ b/packages/babel-preset-react-app/dependencies.js @@ -8,6 +8,8 @@ const path = require('path'); +const getRequiredPlugins = require('./helpers/getRequiredPlugins'); + const validateBoolOption = (name, value, defaultValue) => { if (typeof value === 'undefined') { value = defaultValue; @@ -60,6 +62,16 @@ module.exports = function(api, opts) { ); } + const targets = isEnvTest ? { node: 'current' } : undefined; + + // Some plugins need custom overrides which inadvertantly enables them for + // browsers that don't really need them. Here we check whether these plugins + // are actually required and skip them if they are not. + const { + 'transform-destructuring': isDestructuringTransformRequired, + 'transform-regenerator': isRegeneratorTransformRequired, + } = getRequiredPlugins(targets); + return { // Babel assumes ES Modules, which isn't safe until CommonJS // dies. This changes the behavior to assume CommonJS unless @@ -71,9 +83,7 @@ module.exports = function(api, opts) { // ES features necessary for user's Node version require('@babel/preset-env').default, { - targets: { - node: 'current', - }, + targets, // Do not transform modules to CJS modules: false, // Exclude transforms that make all code slower @@ -97,10 +107,7 @@ module.exports = function(api, opts) { ], ].filter(Boolean), plugins: [ - // Necessary to include regardless of the environment because - // in practice some other transforms (such as object-rest-spread) - // don't work without it: https://github.com/babel/babel/issues/7215 - [ + isDestructuringTransformRequired && [ require('@babel/plugin-transform-destructuring').default, { // Use loose mode for performance: @@ -127,7 +134,7 @@ module.exports = function(api, opts) { { corejs: false, helpers: areHelpersEnabled, - regenerator: true, + regenerator: isRegeneratorTransformRequired, // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules // We should turn this on once the lowest version of Node LTS // supports ES Modules. diff --git a/packages/babel-preset-react-app/helpers/getRequiredPlugins.js b/packages/babel-preset-react-app/helpers/getRequiredPlugins.js new file mode 100644 index 00000000000..90d106171eb --- /dev/null +++ b/packages/babel-preset-react-app/helpers/getRequiredPlugins.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-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 { + default: getTargets, + isBrowsersQueryValid, + isRequired, +} = require('@babel/helper-compilation-targets'); +const data = require('@babel/compat-data/plugins'); + +// Copying normalizeTargets (because it is not exported) +// https://github.com/babel/babel/blob/04354d155689405ba688d4b400702710f9cccc97/packages/babel-preset-env/src/normalize-options.js#L121-L129 +const normalizeTargets = targets => { + // TODO: Allow to use only query or strings as a targets from next breaking change. + if (isBrowsersQueryValid(targets)) { + return { browsers: targets }; + } + return { + ...targets, + }; +}; + +// Test which plugins our target browsers require +module.exports = function getRequiredPlugins(targets) { + const requiredPlugins = {}; + const currentTargets = getTargets(normalizeTargets(targets)); + for (const name of Object.keys(data)) { + requiredPlugins[name] = isRequired(name, currentTargets); + } + return requiredPlugins; +}; diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index 62f509fbb1c..a8e25876356 100644 --- a/packages/babel-preset-react-app/package.json +++ b/packages/babel-preset-react-app/package.json @@ -12,6 +12,7 @@ "url": "https://github.com/facebook/create-react-app/issues" }, "files": [ + "helpers/getRequiredPlugins.js", "create.js", "dependencies.js", "dev.js", @@ -21,7 +22,9 @@ "test.js" ], "dependencies": { + "@babel/compat-data": "7.8.1", "@babel/core": "7.7.5", + "@babel/helper-compilation-targets": "7.8.3", "@babel/plugin-proposal-class-properties": "7.7.4", "@babel/plugin-proposal-decorators": "7.7.4", "@babel/plugin-proposal-nullish-coalescing-operator": "7.7.4",