babel / babel/babel

What do we need to make `usage-global` injection work with `@babel/runtime`?

Open
#16,391 2 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
44k
Forks
6k
Avg merge
5d 15h
Merged PRs (30d)
23

Description

### 💻

- [ ] Would you like to work on this feature?

### What problem are you trying to solve?

@nyngwang has been going around a bit checking how our polyfill injection works (thank you very much!), and after going through their comments I realized that our injection story for `usage-global` is pretty bad when it comes to helpers.

If you have this config:
```json
{
"plugins": [
"@babel/transform-runtime"
]
}
```

Babel will inject imports for helpers like `import _typeof from "@babel/runtime/helpers/typeof"`.

Then you decide that you want to inject global polyfills through `core-js`, so you update your config to this:
```json
{
"plugins": [
"@babel/transform-runtime",
["babel-plugin-polyfill-corejs3", { "method": "usage-global" }]
]
}
```

now Babel will inject imports like `import "core-js/modules/es.array.flat.js";`, but suddently your imports for helpers will become `import "@babel/runtime-corejs3/helpers/typeof"` (which depends on `core-js-pure`). This is to make sure that the helpers are also polyfilled, but:
- you asked for global-modifying polyfills, and now in your dependencies you have both `core-js` and `core-js-pure`
- injecting global polyfills forced you to change one of your dependencies.

If you still want to use `@babel/runtime` (because `@babel/runtime-corejs3` includes polyfills down to ES5), you have to instead use this config:
```json
{
"plugins": [
["@babel/transform-runtime", { "moduleName": "@babel/runtime" }],
["babel-plugin-polyfill-corejs3", { "method": "usage-global" }]
]
}
```

> NOTE: I think this behavior is good when it comes to `usage-pure`, because we are just changing the `@babel/runtime` imports to a different package that uses exactly the _pure_ polyfill the user asked for.

### Describe the solution you'd like

What if for each helper we maintained the list of built-ins that it relies on, and when injecting a `import "@babel/runtime/typeof/helpers"` we called the polyfill provider saying "hey, we included a helper that uses `Symbol` and `Array.prototype.flat`: if needed for the configured target, consider injecting imports to these polyfills".

Then, when compiling `typeof foo` and using `["babel-plugin-polyfill-corejs3", { "method": "usage-global" }]`, we would generate some code like
```js
import "core-js/modules/es.symbol.js";
import _typeof from "@babel/runtime/helpers/typeof";

_typeof(foo);
```

rather than
```js
import _typeof from "@babel/runtime-corejs3/helpers/typeof";

_typeof(foo);
```

This list would probably need to be manually-maintained by us for each helper, and:
- we could try linting using our existing polyfill provider infrastructure, to make sure that we aren't missing any builtin in our list
- even if we change the helper to not rely on X anymore, we still need to include X in the list of features for compatibility with older `@babel/runtime` versions

### Describe alternatives you've considered

Do nothing

### Documentation, Adoption, Migration Strategy

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the two plugin configurations with the `typeof` helper and compare their generated imports. Read the existing polyfill provider infrastructure and helper behavior described in the issue; done means `usage-global` keeps `@babel/runtime` imports while injecting required global `core-js` modules, without switching to `@babel/runtime-corejs3`.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
compilers, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.