Skip to content

exclude unnecessary plugins for target browsers #8030

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
44 changes: 25 additions & 19 deletions packages/babel-preset-react-app/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

const path = require('path');

const getRequiredPlugins = require('./helpers/getRequiredPlugins');

const validateBoolOption = (name, value, defaultValue) => {
if (typeof value === 'undefined') {
value = defaultValue;
Expand Down Expand Up @@ -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) && [
Expand All @@ -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,
Comment on lines +113 to +115
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need to use native spread whenever possible. This requires knowing if our targets require 'proposal-object-rest-spread'.

},
],
isTypeScriptEnabled && [require('@babel/preset-typescript').default],
Expand All @@ -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,
Comment on lines +134 to 139
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need to undo this custom override when it's not needed.

{
// Use loose mode for performance:
Expand Down Expand Up @@ -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
[
Expand All @@ -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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we want to disable regenerator when it's not needed.

// 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.
Expand Down
23 changes: 15 additions & 8 deletions packages/babel-preset-react-app/dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

const path = require('path');

const getRequiredPlugins = require('./helpers/getRequiredPlugins');

const validateBoolOption = (name, value, defaultValue) => {
if (typeof value === 'undefined') {
value = defaultValue;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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.
Expand Down
36 changes: 36 additions & 0 deletions packages/babel-preset-react-app/helpers/getRequiredPlugins.js
Original file line number Diff line number Diff line change
@@ -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;
};
3 changes: 3 additions & 0 deletions packages/babel-preset-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"url": "https://github.com/facebook/create-react-app/issues"
},
"files": [
"helpers/getRequiredPlugins.js",
"create.js",
"dependencies.js",
"dev.js",
Expand All @@ -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",
Expand Down