Using @babel/runtime-corejs2 and @babel/runtime-corejs3 leads to larger bundle sizes
See original GitHub issueBug Report
Current Behavior
When configuring @babel/plugin-transform-runtime
to use corejs2
or corejs3
, bundle sizes increase x7 (corejs2) and x11 (corejs3)
Input Code
const obj = {
a: 1,
b: 2,
};
const { b, ...rest } = obj;
const newObj = {...rest, a: b };
Expected behavior/code
I expect that the bundle sizes stay relatively close, when switching from @babel/runtime
to @babel/runtime-corejs2
or @babel/runtime-corejs3
Babel Configuration (.babelrc, package.json, cli command)
Configs to use @babel/runtime
module.exports = {
presets: [
["@babel/preset-env", {
modules: false,
useBuiltIns: 'usage',
corejs: 3
}]
],
plugins: [
"@babel/plugin-transform-runtime"
]
}
Configs to use @babel/runtime-corejs2
module.exports = {
presets: [
["@babel/preset-env", {
modules: false,
useBuiltIns: 'usage',
corejs: 2
}]
],
plugins: [
["@babel/plugin-transform-runtime", {
corejs: 2
}]
]
}
Configs to use @babel/runtime-corejs3
module.exports = {
presets: [
["@babel/preset-env", {
modules: false,
useBuiltIns: 'usage',
corejs: 3
}]
],
plugins: [
["@babel/plugin-transform-runtime", {
corejs: 3
}]
]
}
Environment
- Babel version(s): 7.4.3 (all babel packages)
- Node/npm version: Node v10
- OS: OSX 10.13.6
- Monorepo: no
- How you are using Babel: CLI, Webpack loader
Possible Solution
N/A
Additional context/Screenshots Add any other context about the problem here. If applicable, add screenshots to help explain.
Repro repo: https://github.com/GeorgeTaveras1231/corejs-size-regression-repro
Snapshot:

Issue Analytics
- State:
- Created 4 years ago
- Reactions:7
- Comments:23 (9 by maintainers)
Top Results From Across the Web
babel/runtime-corejs2
babel /runtime-corejs2` is a library that contain's Babel modular runtime helpers ... This is meant to be used as a runtime dependency along...
Read more >Reduce bundle size with @babel/plugin-transform-runtime
Let's take a look at what the @ babel /plugin-transform- runtime babel plugin can do for our bundle size !
Read more >Why does it seem the whole core-js library is being bundled ...
OK, feeling foolish. My webpack.config.js had this configuration for babel-loader : { test: /\.js$/, exclude: /node_modules/, ...
Read more >@babel/core - NPM Package Versions - Socket
Start using Socket to analyze @babel/core and its 16 dependencies to ... babel-runtime-corejs2 , babel-runtime-corejs3 , babel-runtime , babel-traverse.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I will try to clarify in details about what happens exactly and why.
First,
core-js
exposes two packages that are relevant in this context and they are:core-js
andcore-js-pure
. Now,core-js
defines global polyfills, andcore-js-pure
provides polyfills that don’t pollute the global environment. What this means in simpler words is that if you write thisimport "core-js/stable/set";
, you import from the package that defines polluting polyfills, and as a result you haveglobal.Set
. On the other hand, if you write thisimport Set from "core-js-pure/stable/set";
, you avoid polluting the global environment, you importSet
and bundle it with your app.Now, what does
useBuiltIns: 'usage'
together with thecorejs
option set on@babel/preset-env
do? If you havevar p = new Promise()
in your app,babel
will transform it to something like this:The ‘usage’ word suggests what
babel
does: If you use code that browsers you targeted with your browser configuration do not support,babel
will inject polyfills for that code. But the main question is: from where? If we inspect the transpiled code, we see thatbabel
injects thePromise
polyfill fromcore-js
. Note that you should havecore-js
installed as a dependency in your app.Babel
injects the polyfills that pollute the global environment with this setup.Ok, now let’s see what
@babel/transform-runtime
does with thecorejs
option set. If you have the same code above, that isvar p = new Promise()
,babel
transpiles that to somethings like this:As we can see, the imports come from
@babel/runtime-corejs3
. Thisbabel
package doesn’t contain any source code. Instead it only listscore-js
andregenerator-runtime
as dependencies. The@babel/transform-runtime
plugin does some magic and inserts several folders with files in this package innode_modules
. This is why@babel/runtime-corejs3/helpers
or@babel/runtime-corejs3/core-js-stable
folders exist in the first place. Now, the important part is that the files in these folders import things fromcore-js-pure
. So when you use@babel/transform-runtime
plugin, it includes polyfills from thecore-js-pure
folder, and doesn’t pollute the global environment.Answer to the question:
As explained above, your app code is bundled together with the polyfills for features like
Promise
. Important to note here is that@babel/transform-runtime
doesn’t care about yourtargets
. It doesn’t even have an option, to specify the targets, it just includes the polyfill even if you want to target new environments.Answer to the question:
As explained above, with this config, polyfills are included from the
core-js
package that pollutes global environment, and in the end you get something likeglobal.Promise
. Important to note here is that@babel/preset-env
respects your targets, and doesn’t include unnecessary pollyfills, where@babel/transform-runtime
will include every polyfillable feature, which leads to many unnecessary polyfills.Answer to the question:
The answer is NO. This is not obvious at first. The only case where you will get away with this usage is when you include
@babel/runtime-corejs3
or@babel/runtime-corejs2
in your app, and that is whencorejs: *
! And not when you include@babel/runtime
, which happens whencorejs: false
. Why? To explore this, we need a different example. Let’s say you have this code in your app:async function f() {}
The transpiled code is this:
Look at this line here:
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
Babel
includes helpers from@babel/runtime
! These helpers can depend on some global features to be available. In this case, that feature isPromise
. The code in@babel/runtime/helpers/asyncToGenerator
usesPromise
s!. Now you may think: But withuseBuiltIns: 'usage'
I included polyfills for my targeted browsers?. Yes, that’s true, but with that configbabel
includes polyfills when you use that feature in your code! And you haven’t usedPromise
anywhere! Now you have a problem. You need a way to transpile the babel helpers, and that’s not good. This is the case that @zloirock refers to, when he says that you need to transpile the helpers. You will get away here if you usePromise
in your app, because then globally pollutable polyfill forPromise
will be injected like this:require("core-js/modules/es6.promise");
As you can see, this is not predictable, and very difficult to configure. Note about the cases where I said that you will get away if you have@babel/runtime-corejs3
or@babel/runtime-corejs2
as dependencies instead of@babel/runtime
. In this case polyfills fromcore-js-pure
will be injected, like this:var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/asyncToGenerator"));
(corejs
option should be set on@babel/transform-runtime
as well)Answer to the question:
I’ll go like this:
App: If you are authoring an app, use
import 'core-js
at the top of your app withuseBuiltIns
set toentry
and@babel/transform-runtime
only for helpers (@babel/runtime
as dependency). This way you pollute the global environment but you don’t care, its your app. You will have the benefit of helpers aliased to@babel/runtime
and polyfills included at the top of your app. This way you also don’t need to processnode_modules
(except when a dependency uses a syntax that has to be transpiled) because if some dependency used a feature that needs a polyfill, you already included that polyfill at the top of your app.Library: If you are authoring a library, use only
@babel/transform-runtime
withcorejs
option plus@babel/runtime-corejs3
as dependency, and@babel/preset-env
for syntax transpilation withuseBuiltIns: false
. Also I would transpile packages I would use fromnode_modules
. For this you will need to set theabsoluteRuntime
option (https://babeljs.io/docs/en/babel-plugin-transform-runtime#absoluteruntime) to resolve the runtime dependency from a single place, because@babel/transform-runtime
imports from@babel/runtime-corejs3
directly, but that only works if@babel/runtime-corejs3
is in thenode_modules
of the file that is being compiled.This is my understanding, @zloirock , @nicolo-ribaudo feel free to correct me if I am wrong somewhere. Also feel free to ask if something is unclear from the explanations.
Useful links:
https://github.com/babel/babel/issues/10008 https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md https://babeljs.io/docs/en/babel-plugin-transform-runtime https://github.com/babel/babel/issues/10271
Thanks for reading!
transform-runtime
injects helpers and some helpers depends on globals which should be polyfilled. Since helpers will be injected by the link and their body will be innode_modules
, if you useuseBuiltIns: usage
, you should somehow transpile@babel/runtime
dependency. But since it could cause circular dependencies, you should be careful in configuration.useBuiltIns
+runtime
for helpers is preferable way for applications. However, I recommenduseBuiltIns: entry
with required parts ofcore-js
since it’s much simpler in configuration and much more predictable.useBuiltIns: usage
makes sense only for small applications and, for correct work, much harder in configuration.