question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using @babel/runtime-corejs2 and @babel/runtime-corejs3 leads to larger bundle sizes

See original GitHub issue

Bug 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:

Screen Shot 2019-04-12 at 11 41 52 AM

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:7
  • Comments:23 (9 by maintainers)

github_iconTop GitHub Comments

117reactions
JMarkoskicommented, May 14, 2020

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 and core-js-pure. Now, core-js defines global polyfills, and core-js-pure provides polyfills that don’t pollute the global environment. What this means in simpler words is that if you write this import "core-js/stable/set";, you import from the package that defines polluting polyfills, and as a result you have global.Set. On the other hand, if you write this import Set from "core-js-pure/stable/set";, you avoid polluting the global environment, you import Set and bundle it with your app.

Now, what does useBuiltIns: 'usage' together with the corejs option set on @babel/preset-env do? If you have var p = new Promise() in your app, babel will transform it to something like this:

"use strict";

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.promise");

var p = new Promise();

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 that babel injects the Promise polyfill from core-js. Note that you should have core-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 the corejs option set. If you have the same code above, that is var p = new Promise(), babel transpiles that to somethings like this:

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

var p = new _promise["default"]();

As we can see, the imports come from @babel/runtime-corejs3. This babel package doesn’t contain any source code. Instead it only lists core-js and regenerator-runtime as dependencies. The @babel/transform-runtime plugin does some magic and inserts several folders with files in this package in node_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 from core-js-pure. So when you use @babel/transform-runtime plugin, it includes polyfills from the core-js-pure folder, and doesn’t pollute the global environment.

Answer to the question:

Why when using @babel/transform-runtime my app gets large?

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 your targets. 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:

Why with using useBuiltIns: "usage" on @babel/preset-env the code is smaller than when using @babel/transform-runtime?

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 like global.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:

Should I use useBuiltIns: 'usage' and corejs option on @babel/preset-env together with @babel/transform-runtime with core-js option set to false?

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 when corejs: *! And not when you include @babel/runtime, which happens when corejs: 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:

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

require("regenerator-runtime/runtime");

var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));

function f() {
  return _f.apply(this, arguments);
}

function _f() {
  _f = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() {
    return _regenerator["default"].wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  }));
  return _f.apply(this, arguments);
}

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 is Promise. The code in @babel/runtime/helpers/asyncToGenerator uses Promises!. Now you may think: But with useBuiltIns: 'usage' I included polyfills for my targeted browsers?. Yes, that’s true, but with that config babel includes polyfills when you use that feature in your code! And you haven’t used Promise 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 use Promise in your app, because then globally pollutable polyfill for Promise 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 from core-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:

What is a good config then?

I’ll go like this:

App: If you are authoring an app, use import 'core-js at the top of your app with useBuiltIns set to entry 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 process node_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 with corejs option plus @babel/runtime-corejs3 as dependency, and @babel/preset-env for syntax transpilation with useBuiltIns: false. Also I would transpile packages I would use from node_modules. For this you will need to set the absoluteRuntime 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 the node_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!

11reactions
zloirockcommented, Jun 12, 2019

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 in node_modules, if you use useBuiltIns: 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 recommend useBuiltIns: entry with required parts of core-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.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found