babel: transform-runtime is not removing regenerator-runtime import added by preset-env
Hello!
Thank you for this great tool!
Problem
However, I’m looking at situation when the transform-runtime
plugin is not removing global regenerator-runtime/runtime
import, which is introduced by the preset-env
preset.
Use case
We were working on some libraries and were using preset-env
to actually transpile the code to the required target execution environment as well as to add needed global core-js
and regenerator-runtime
imports (usage mode). We were also using transform-runtime
to optimize babel helper imports.
And now we would like to migrate from using global regeneratorRuntime
usages to the explicitly imported ones (pure), so we’ve enabled regenerator
option of the transform-runtime
.
More details
Source code
// src/main.js
export async function foo() {
return await new Promise(resolve => setTimeout(resolve, 1000));
}
Babel config
{
"presets": [
[
"@babel/preset-env",
{
"targets": "IE 11",
"useBuiltIns": "usage",
"corejs": 3,
"modules": false,
"debug": true
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"helpers": true,
"regenerator": true
}
]
],
"exclude": "node_modules/**"
}
Transpiled result
// dist/main.js
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.promise";
import "regenerator-runtime/runtime";
import _regeneratorRuntime from "@babel/runtime/regenerator";
export function foo() {
return _regeneratorRuntime.async(function foo$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _regeneratorRuntime.awrap(new Promise(function (resolve) {
return setTimeout(resolve, 1000);
}));
case 2:
return _context.abrupt("return", _context.sent);
case 3:
case "end":
return _context.stop();
}
}
});
}
As you can see, the generated file contains both pure and global imports for regenerator-runtime
, however, only explicit (pure) import should be left:
import "regenerator-runtime/runtime";
import _regeneratorRuntime from "@babel/runtime/regenerator";
I believe, that transform-runtime
should remove such global imports (isn’t it it’s purpose in the first place?), or at least there should be an option for the preset-env
to skip adding the global import for regenerator-runtime
(I couldn’t find any).
-
What do you think about this issue?
-
Do you know of any workarounds I can use right now to actually remove such superfluous imports?
Thank you.
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 4
- Comments: 23 (10 by maintainers)
No, it won’t. Adding import to
core-js
will pollute the global environment, and the single line import will convert to more granular imports from thecore-js
library based on your targets, thus not adding polyfills that are not needed. TheuseBuiltIns: 'usage'
option also pollutes the global environment, but it adds polyfills based on whether you use a feature that is not supported on your targets in your code or not (this is problematic if some of the dependencies use a feature that you don’t use in your code, because it won’t be polyfilled)The only way to work around this that I’ve found so far is to use babel-plugin-transform-remove-imports plugin:
Yes, that’s true. But the benefit is that you will have the polyfills bundled with your library/app (its more useful for libraries, not so much for apps, because you can allow global pollution in your app, the problem is that you don’t want your library users to have global scope pollution), and avoid global scope pollution.
The main thing and the main point of confusion is how to handle polyfills, i.e mixing
@babel/preset-env
withuseBuiltIns
with@babel/transform-runtime
. You can remember it like this: Whenever you use@babel/preset-env
for polyfill handling you pollute, whenever you use@babel/transform-runtime
for polyfill handling you are safe, although including useless polyfills.@JMarkoski where is preset-env config?
@dangreen You don’t need it, moreover, you shouldn’t use
@babel/preset-env
withuseBuiltIns
with@babel/transform-runtime
(although you can use it in a config whereuseBuiltIns: 'entry'
and@babel/transform-runtime
is used only for helpers, but here we talk aboutuseBuiltIns: 'usage'
). I’ll link again my answer that answers exactly the question why you shouldn’t use it: https://github.com/babel/babel/issues/9853#issuecomment-619587386EDIT: To correct myself about what I said above, you need
@babel/preset-env
to transpile the syntax though. I was talking about handling polyfills (aboutuseBuiltIns
to be specific). See the corrected answer above.@JMarkoski in your answer no word about preset-env. I want to know how to combine preset-env with transform-runtime.
@dangreen I’m not sure I understood correctly what is the problem you are trying to solve, but I’ll try to explain the philosophy of using the
@babel/preset-env
withuseBuiltIns: 'usage'
and@babel/transform-runtime
.So, we should note the main important difference between the two: you use
@babel/preset-env
with{useBuiltIns: 'usage', corejs: *}
when you want globally polluting polyfilling, using the babel built-ins for that, and you use@babel/transform-runtime
when you want to bundle the polyfills with your app, so you don’t pollute the global environment.Now, having that philosophy in mind about the above mentioned usage of
@babel/preset-env
, the polyfilling process also includes theregenerator-runtime/runtime
polyfill, together with the babel helpers likeclassCallCheck
and the feature polyfills from thecore-js
library. In other words, with this config you say:Now, when you include the
@babel/transform-runtime
in the context, you are somewhat doing the same stuff but differently. First you said that you want globally polluting polyfills for everything, and now you say, I want polyfills/helpers for everything bundled with my app, not polluting the global environment.Actually you want to have this scenario, which I will explain why @nicolo-ribaudo says here that it doesn’t make much sense: https://github.com/babel/babel/pull/10768#pullrequestreview-324863979:
When
@babel/preset-env
with{useBuiltIns: 'usage', corejs: *}
or@babel/transform-runtime
is used, each one of them adds a set of things:classCallCheck
core-js
regenerator-runtime/runtime
This set is the runtime your app needs in order to work in your target environments. It is a set of features that have to be included in order for your app to work, and it can be done in two ways: global pollution and globally safe manner.
In the case of
@babel/preset-env
this is globally polluting, and in the case of@babel/transform-runtime
is not, it adds references to the@babel/runtime
or@babel/runtime-corejs*
. But the important note is:You can’t add globally polluting polyfills from
core-js
and addregenerator-runtime/runtime
that is not globally polluting. You can’t pick things from both sets and mix them, because how something includes/uses other thing is connected, and AFAIK mixing will lead to some unpredictability, or is hard to reason about. So, as @nicolo-ribaudo says in the comment, it doesn’t even make sense to mix globally polluting with globally safe pollyfilling, you either are fully globally polluting or fully globally safe.EDIT: An answer about mixing both
@babel/preset-env
and@babel/transform-runtime
for polyfill handling: https://github.com/babel/babel/issues/9853#issuecomment-619587386This is my limited understanding of the matter, @nicolo-ribaudo feel free to correct me if I am wrong somewhere.
Another solution is to switch from
useBuiltIns: 'usage'
touseBuiltIns: 'entry'
, since you are okay with globalcore-js
pollution. You can add onlyimport 'core-js'
in the entry point, and leave theregenerator: true
option on the@babel/transform-runtime
.