From 4751e33e6f1a4ae505bb3df20dfbeb15fbff6a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 25 Apr 2020 18:46:59 +0200 Subject: [PATCH 01/10] Move "targets"-related options to `@babel/core` --- rfcs/0000-top-level-browserslist.md | 258 ++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 rfcs/0000-top-level-browserslist.md diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md new file mode 100644 index 0000000..ff5d91a --- /dev/null +++ b/rfcs/0000-top-level-browserslist.md @@ -0,0 +1,258 @@ +- Repo: `babel/babel` +- Start Date: 2020-04-25 +- RFC PR: +- Related Issues: [babel/babel#10008](https://github.com/babel/babel/issues/10008) +- Authors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) +- Champion: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) +- Implementors: + +# Summary + +This RFC proposes moving the [Browserslist](https://github.com/browserslist/browserslist) integration from `@babel/preset-env` to `@babel/core`. This means deprecating all the Browserslists-related option from the preset and introducing new top-level configuration options. + +# Basic example + +This example shows how a simple existing configuration can be rewritten using the new API. + +```json5 +// Existing +{ + "presets": [ + ["@babel/preset-env", { + "targets": ">1%, not ie 11" + }] + ] +} +``` + +```json5 +// Proposal +{ + "targets": ">1%, not ie 11", + "presets": ["@babel/preset-env"] +} +``` + +# Motivation + +`targets` is the option that makes `@babel/preset-env` the most powerful preset we have ever had. + +However, there are other plugins and presets that would benefit from knowing what engines the user is targeting. The main motivation for me to write this RFC is to be able to share the same targets between `@babel/preset-env` and the [polyfill plugins](). + +Currently, you have to specify the targets twice: +```json5 +// Existing +{ + "presets": [ + ["@babel/preset-env", { + "targets": ">1%, not ie 11" + }] + ], + "plugins": [ + ["polyfill-es-shims", { + "targets": ">1%, not ie 11" + }] + ] +} +``` + +Moving `targets` to the top-level options can also be useful for: +- plugins that minify the source code, so that they can decide, for exampe, to transform function expressions in arrow functions; +- generic transform plugins, that can emit a smaller output if they know that the user is targeting modern engines. + +# Detailed design + +`@babel/preset-env` has 3 Browserslist-related options: +- [`targets`](https://babeljs.io/docs/en/babel-preset-env#targets) +- [`configPath`](https://babeljs.io/docs/en/babel-preset-env#configpath) +- [`ignoreBrowserslistConfig`](https://babeljs.io/docs/en/babel-preset-env#ignorebrowserslistconfig) + +[babel/babel#11434](https://github.com/babel/babel/pull/11434), which will land in Babel 7.10.0, introduces a new `browserslistEnv` option. + +## New Babel options + +### `targets` + +The `targets` option of `@babel/preset-env` can be used in different ways: +1. As a string ar as an array of strings, to specify a Browserslist query +2. As an object mapping engine names to their target versions. This map can also contain `node: "current"`, to target the Node.js version the users is running Babel with; `esmodules: true`, which is expanded to [the browsers with native support for ECMAScript modules](https://github.com/babel/babel/blob/master/packages/babel-compat-data/data/native-modules.json); `browsers: string | Array`, which can specify a Browserslist query; `uglify: true`, which forces `@babel/preset-env` to compile everything. + +We should move this option to `@babel/core` without limiting any of its current capabilities, except for `targets.uglify`. It has been deprecated since `babel-preset-env` 2.0, for two reasons: if you want to force all the possible syntax transforms regardless of your targets you can use `@babel/preset-env`'s `forceAllTransform` option, or you can dynamically set your `targets` correctly so that the option reflects what you need. + +### `browserslistConfigFile` + +Both the `configPath` and `ignoreBrowserslistConfig` options of `@babel/preset-env` change the Browserslist config loading behavior. +- `configPath` is the file or directory where Browserslist's resolution algorithm starts from. It is the equivalent of Browserslist's [`path` option](https://github.com/browserslist/browserslist#js-api). It defaults to `process.cwd()`. +- `ignoreBrowserslistConfig` disables Browserslist's config resolution algorithm. + +Those options conflict with each other, and this is solved by ignoring `configPath` when `ignoreBrowserslistConfig`. However, when the `targets.browsers` option is set, `configPath` takes precedence over `ignoreBrowserslistConfig`. + +Given that we are moving them to `@babel/core`, we should consolidate them in a single option: `browserslistConfigFile`. This option would behave exactly like the existing `configFile` option, which controls `babel.config.json`'s loading. + +- If set to `false`, it disables config loading for Browserslist. +- If set to a string, it specifies the exact config file path. This is equivalent to Browserslist's [`config` option](https://github.com/browserslist/browserslist#js-api). + +If someone needs the old behavior of `configPath`, they can still use Browserslist's config loading API: +```js +// babel.config.mjs +import browserslist from "browserslist"; + +const configPath = "./folder/to/start/searching/from"; + +export default { + presets: ["@babel/preset-env"] + browserslistConfigFile: browserslist.findConfig(configPath), +}; +``` + +Even if we are removing Browserslist's `path` option from our options, we should still set it internally to allow Browserslist to correctly resolve the config file by default. However, `process.cwd()` (the current default value in `@babel/preset-env`) is a **bad** default. We should choose one of these: +- If Browserslist config files are meant to be project-wide configs, we should use Babel's [`root` option](https://babeljs.io/docs/en/options#root) which represents the root directory of the whole project. It's default value is Babel's [`cwd` option](https://babeljs.io/docs/en/options#cwd), whose default value is `process.cwd()`. Using `process.cwd()` directly would completely ignore the `root` and `cwd` options. +- If Browserslist config files are meant to be file-relative configs, we should default to the input filename. If the input filename is not provided, `browserslistConfigFile` should default to `false` to disable config resolution. + +### `browserslistEnv` + +The new `browserslistEnv` option of `@babel/preset-env` is the [`env` option](https://github.com/browserslist/browserslist#js-api) of Browserslist. I think we should move it as-is to `@babel/core`'s options. + +## Allowed options placements and conflicts resolution + +These three new options should be allowed at least in the programmatic options, in `babel.config.*` files and in `.babelrc.*` files. + +If Browserslist options are specified multiple times, the most relevant one should completely overwrite the previous one. This is because it's not clear if we should merge or intersect the resulting engines sets. + +We should use this precedence, where "a > b" means that the option specified in a overwrites the option specified in b: +- Programmatic options > close `.babelrc.*` > distant `.babelrc.*` > `babel.config.js` + +This is identical to what we already do when a plugin is enabled multiple times. + +`browserslist` shouldn't be called every time we find a `targets` or `browserslistConfigFile` option, but only once after merging all the Babel configuration sources. + +## Plugins and presets API + +The first parameter passed to plugins and presets (usually named `babel`, or `api`) already exposes different APIs from `@babel/core` to the plugins/presets. [source](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/full.js#L225-L228). + +We can add a `targets` property to that object containing the _resolved_ targets. For example, if the user specifies `targets: ">2%"` in their configuration, a plugin would receive a `targets` object similar to this one: + +```js +api.targets = { + chrome: "80.0.0", + ios: "13.3.0", + safari: "13.0.0", + samsung: "11.1.0", +} +``` + +The current implementation, uses the [`makeAPI` function](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L28) to build both the [`plugin API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/full.js#L227) and the [`config API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/files/configuration.js#L206). They also share [the same Flow type](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L20-L26), even if it's called `PluginApi`. + +Since `targets` is resolved _after_ loading the config files, we can only pass it to the plugins and presets. We should split the `makeAPI` function in `makePluginAPI` and `makeConfigAPI`, and we should add a `ConfigApi` Flow type. Since this is an internal file, it won't be a breaking change. + +# Drawbacks + +- This proposal increase the downloaded `node_modules` size even for users who use `@babel/core` without using `@babel/preset-env`. It won't increase `@babel/standalone`'s size, because it already bundles Browserslist. + +- We already have two top-level options containing `env` in their name. After this RFC, we would have `env`, `envName` and `browserslistEnv`. They are all different: + - `envName` is used to overwrite the `BABEL_ENV` and `NODE_ENV` environment variables. It can only be used in the programmatic options, because it affects the config loading behavior. + - `env` is used to specify custom configs depending on the value of the `envName` option, or on the `BABEL_ENV` and `NODE_ENV` environment variables. + - `browserslistEnv` is similar to `envName`, but it can also be set in config files. + +- Some presets (for example, [`babel-preset-react-app`]((https://github.com/facebook/create-react-app/blob/e89f153224cabd67efb0175103244e0b7f702767/packages/babel-preset-react-app/dependencies.js#L74-L76))) include `@babel/preset-env`, and specify some targets. + If we move the `targets` option from `@babel/preset-env` from `@babel/core`, then they won't be able to easily specify it anymore because presets would be "`targets` consumers" and not "`targets` providers". + + How big is this drawback? `create-react-app` can move the `targets` logic from the preset to the generated Babel config, but are there any other presets that are defining `targets` for the user and passing it to `@babel/preset-env`? + + Note that, as a workaround, it would be possible to overwrite the `targets` for any plugin or preset by using an higher order function: + + ```js + export default { + targets: [">2%"], + presets: [ + withTargets("@babel/preset-env", { chrome: 42 }) + ] + }; + + function withTargets(pkg, targets) { + const fn = require(pkg).default; + return (api, options, dirname) => + fn({ __proto__: api, targets }, options, dirname); + } + ``` + +# Alternatives + +## Keep the current situation + +Now that we have extracted the `@babel/helper-compilation-targets` package out of `@babel/preset-env` ([babel/babel#10899](https://github.com/babel/babel/pull/10899)), it's easy for plugin and presets authors add the `targets` option to their packages without needing a new top-level option. However, this requires the users to specify their targets multiple times. + +## Merge the new options in a single object + +Instead of adding `targets`, `browserslistEnv` and `browserslistConfigFile`, we could decide to have a single `browserslist` option that includes them. We could then use `envName` and `configFile` as the option names, to exactly match the names we alread have for Babel: + +```json5 +{ + "browserslist": { + "targets": ">2%", + "envName": "production", + "configFile": "./.browserslistrc" + } +} +``` + +However, this has two drawbacks: +1. If you explicitly set your `targets` using the "name -> version" object map, it won't actually call Browserslist so the name might be misleading. +2. Most users will only use the `targets` option, and having to set `browserslist.targets` instead of `targets` seems like an unnecessary (even if very low) barrier: + ```json5 + { + "browserslist": { + "targets": ">2%" + } + } + + // vs + + { + "targets": ">2%" + } + ``` + +We could solve 2 by allowing a top-level `targets` option as an alias to `browserslist.targets`, or by only putting `envName` and `configFile` inside the `browserslist` object. + +# Adoption strategy + +We can implement this in a non-breaking way: we can add the new top-level options without removing the counterparts in `@babel/preset-env`. We can mark the existing options as deprecated in the docs, and remove them either in the next major version or in the following one. + +During this migration period, we can make `@babel/preset-env` throw if the options are duplicated inside the preset and in the top-level. + +# How we teach this + +We have a problem here: almost all the documentation, articles and tutorials about `@babel/preset-env` show (correctly) the `targets` option. We will update our documentation to document the new top-level options instead of the current one, but this will not be enough. + +In the next major release, if we won't remove the `@babel/preset-env` options yet, we should add a warning asking the user to use the top-level options and explaining the benefits. The benefits will be much more clearer when we'll extract the polyfills injection logic from `@babel/preset-env` to a separate plugin. + +When we will finally remove the options from `@babel/preset-env`, if they are still set we shouldn't ignore them but throw an error explaining the new top-level options. + +# Open questions + +1. Should `.browserslistrc` files be considered as project-wide or file-relative configs? +2. Is the "Merge the new options in a single object" better than having three new top-level options? +3. Should we also move the `forceAllTransform` option to `@babel/core`? + + + + + +## Related Discussions + +- [babel/babel#10008](https://github.com/babel/babel/issues/10008), where the "top-level targets" idea first came out. +- [2020/04/14](https://github.com/babel/notes/blob/master/2020/04-15.md) and [2020/04/02](https://github.com/babel/notes/blob/master/2020/04-02.md) meeting notes, the first times we started thinking about this idea. + + From ac41955022f415678b5298922a3833156ca68535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 26 Apr 2020 11:12:33 +0200 Subject: [PATCH 02/10] Update 0000-top-level-browserslist.md --- rfcs/0000-top-level-browserslist.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index ff5d91a..b56f991 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -1,7 +1,7 @@ - Repo: `babel/babel` - Start Date: 2020-04-25 - RFC PR: -- Related Issues: [babel/babel#10008](https://github.com/babel/babel/issues/10008) +- Related Issues: - Authors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Champion: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Implementors: @@ -248,6 +248,7 @@ When we will finally remove the options from `@babel/preset-env`, if they are st ## Related Discussions - [babel/babel#10008](https://github.com/babel/babel/issues/10008), where the "top-level targets" idea first came out. +- Docs of [`babel-polyfills`](https://github.com/babel/babel-polyfills/blob/master/docs/usage.md#targets-ignorebrowserslistconfig-configpath-debug), where I had to duplicate the Browserslist-related options in all the plugins. - [2020/04/14](https://github.com/babel/notes/blob/master/2020/04-15.md) and [2020/04/02](https://github.com/babel/notes/blob/master/2020/04-02.md) meeting notes, the first times we started thinking about this idea. +- Implementors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) # Summary From 4d3fc6acaad698a62697be0d73fabe295a731850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 17 Oct 2020 00:43:30 +0200 Subject: [PATCH 04/10] Update 0000-top-level-browserslist.md --- rfcs/0000-top-level-browserslist.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index acc2299..f87055d 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -126,14 +126,18 @@ This is identical to what we already do when a plugin is enabled multiple times. `browserslist` shouldn't be called every time we find a `targets` or `browserslistConfigFile` option, but only once after merging all the Babel configuration sources. +## Targets resolution time + +The `targets` are resolved after loading and merging all the config files, but before returing from `loadPartialConfig`. This guarantees that the object returned by `loadPartialConfig` doesn't rely on additional configuration files (i.e. `.browserslistrc`) or on environment variables that could affect the result of calling `browserslist()`. + ## Plugins and presets API The first parameter passed to plugins and presets (usually named `babel`, or `api`) already exposes different APIs from `@babel/core` to the plugins/presets. [source](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/full.js#L225-L228). -We can add a `targets` property to that object containing the _resolved_ targets. For example, if the user specifies `targets: ">2%"` in their configuration, a plugin would receive a `targets` object similar to this one: +We can add a `targets` **method** to that object containing the _resolved_ targets. For example, if the user specifies `targets: ">2%"` in their configuration, a plugin would receive a `targets` object similar to this one: ```js -api.targets = { +api.targets() == { chrome: "80.0.0", ios: "13.3.0", safari: "13.0.0", @@ -141,7 +145,11 @@ api.targets = { } ``` -The current implementation, uses the [`makeAPI` function](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L28) to build both the [`plugin API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/full.js#L227) and the [`config API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/files/configuration.js#L206). They also share [the same Flow type](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L20-L26), even if it's called `PluginApi`. +`api.targets()` must to be a function because, similarly to `api.env()` and `api.caller()`, it affects plugins and presets caching: `@babel/core` needs to know when a plugin/preset relies on `targets` so that when they change it can properly invalidate the cache and re-instantiate such plugin/preset. + +### Internal implementation details + +The current implementation uses the [`makeAPI` function](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L28) to build both the [`plugin API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/full.js#L227) and the [`config API`](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/files/configuration.js#L206). They also share [the same Flow type](https://github.com/babel/babel/blob/83d365acb60db2943279cb6f3914c55f52b5702d/packages/babel-core/src/config/helpers/config-api.js#L20-L26), even if it's called `PluginApi`. Since `targets` is resolved _after_ loading the config files, we can only pass it to the plugins and presets. We should split the `makeAPI` function in `makePluginAPI` and `makeConfigAPI`, and we should add a `ConfigApi` Flow type. Since this is an internal file, it won't be a breaking change. @@ -165,14 +173,14 @@ Since `targets` is resolved _after_ loading the config files, we can only pass i export default { targets: [">2%"], presets: [ - withTargets("@babel/preset-env", { chrome: 42 }) + withTargets("@babel/preset-env", { chrome: "42.0.0" }) ] }; function withTargets(pkg, targets) { const fn = require(pkg).default; return (api, options, dirname) => - fn({ __proto__: api, targets }, options, dirname); + fn({ __proto__: api, targets: () => targets }, options, dirname); } ``` From ff7acc957c9d69772a7d60d22c2c821c56ab6278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 22 Oct 2020 21:41:24 +0200 Subject: [PATCH 05/10] Add explanation from about targets "merging" --- rfcs/0000-top-level-browserslist.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index f87055d..448cc7c 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -113,18 +113,39 @@ Even if we are removing Browserslist's `path` option from our options, we should The new `browserslistEnv` option of `@babel/preset-env` is the [`env` option](https://github.com/browserslist/browserslist#js-api) of Browserslist. I think we should move it as-is to `@babel/core`'s options. -## Allowed options placements and conflicts resolution +## Allowed options placements and merging -These three new options should be allowed at least in the programmatic options, in `babel.config.*` files and in `.babelrc.*` files. +These three new options are be allowed in the programmatic options, in `babel.config.*` files and in `.babelrc.*` files. -If Browserslist options are specified multiple times, the most relevant one should completely overwrite the previous one. This is because it's not clear if we should merge or intersect the resulting engines sets. +If Browserslist options are specified multiple times, the most relevant one should completely overwrite the previous one. This makes sure that both the following examples have the expected behavior: +```jsonc +{ + "targets": "> ie 10", + "presets": ["@babel/env"], + + "env": { + "modern": { "targets": "supports es6-module" } + } +} + +{ + "targets": "supports es6-module", + "presets": ["@babel/env"], + + "env": { + "legacy": { "targets": "> ie 10" } + } +} +``` + +If we choosed to merge `targets` by computing the _union_ of the specified targets, then the first example would be resolved to `{ "ie": "10" }` also when `BABEL_ENV=modern`. If we instead choosed to merge them by computing the _intersection_, then the second example would be resolved to the equivalent of `supports es6-module` even when `BABEL_ENV=legacy`. We should use this precedence, where "a > b" means that the option specified in a overwrites the option specified in b: - Programmatic options > close `.babelrc.*` > distant `.babelrc.*` > `babel.config.js` This is identical to what we already do when a plugin is enabled multiple times. -`browserslist` shouldn't be called every time we find a `targets` or `browserslistConfigFile` option, but only once after merging all the Babel configuration sources. +`browserslist()` shouldn't be called every time we find a `targets` or `browserslistConfigFile` option, but only once after merging all the Babel configuration sources. ## Targets resolution time From ed6240e86a0381cda8a373b1d6d56b15f606e44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 25 Oct 2020 01:22:32 +0200 Subject: [PATCH 06/10] typo --- rfcs/0000-top-level-browserslist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index 448cc7c..b2aede7 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -166,7 +166,7 @@ api.targets() == { } ``` -`api.targets()` must to be a function because, similarly to `api.env()` and `api.caller()`, it affects plugins and presets caching: `@babel/core` needs to know when a plugin/preset relies on `targets` so that when they change it can properly invalidate the cache and re-instantiate such plugin/preset. +`api.targets()` must be a function because, similarly to `api.env()` and `api.caller()`, it affects plugins and presets caching: `@babel/core` needs to know when a plugin/preset relies on `targets` so that when they change it can properly invalidate the cache and re-instantiate such plugin/preset. ### Internal implementation details From 6e51231516b576518c8c044a01307b08f5c6dacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 10 Nov 2020 17:33:51 +0100 Subject: [PATCH 07/10] Update 0000-top-level-browserslist.md --- rfcs/0000-top-level-browserslist.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index b2aede7..c637e64 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -274,6 +274,21 @@ When we will finally remove the options from `@babel/preset-env`, if they are st in this section. --> +## Possible follow-ups + +- (ht [**@developit**](https://github.com/developit)) We might give access to `@babel/compat-data` to plugins through `@babel/core`, possibly with an API similar to this one: + ```js + function plugin(api) { + if (api.targets.supports("proposal-optional-chaining")) { + /* ... */ + } + } + ``` + This should be kept as a possible future RFC because we first need to know: + - If this would be actually useful + - What kind of relational operators we need between the config targets and a feature's support data (≥ and <? However the order isn't always well defined) + - How does it interact with bugfix-style plugins, that change the resulting targets + ## Related Discussions - [babel/babel#10008](https://github.com/babel/babel/issues/10008), where the "top-level targets" idea first came out. From 5a65c7c4badbc7734d347741383c1521f7b80141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 10 Dec 2020 13:33:13 +0100 Subject: [PATCH 08/10] Updates to match the conclusions --- rfcs/0000-top-level-browserslist.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0000-top-level-browserslist.md index c637e64..0788b8a 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0000-top-level-browserslist.md @@ -1,7 +1,7 @@ - Repo: `babel/babel` - Start Date: 2020-04-25 -- RFC PR: -- Related Issues: +- RFC PR: [babel/babel#12189](https://github.com/babel/babel/pull/12189) +- Related Issues: [babel/babel#8809](https://github.com/babel/babel/pull/8809), [babel/babel#10008](https://github.com/babel/babel/issues/10008) - Authors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Champion: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Implementors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) @@ -75,9 +75,20 @@ Moving `targets` to the top-level options can also be useful for: The `targets` option of `@babel/preset-env` can be used in different ways: 1. As a string ar as an array of strings, to specify a Browserslist query -2. As an object mapping engine names to their target versions. This map can also contain `node: "current"`, to target the Node.js version the users is running Babel with; `esmodules: true`, which is expanded to [the browsers with native support for ECMAScript modules](https://github.com/babel/babel/blob/master/packages/babel-compat-data/data/native-modules.json); `browsers: string | Array`, which can specify a Browserslist query; `uglify: true`, which forces `@babel/preset-env` to compile everything. +2. As an object mapping engine names to their target versions. This map can also contain `node: "current"`, to target the Node.js version the users is running Babel with; `esmodules: true`, which is expanded to [the browsers with native support for ECMAScript modules](https://github.com/babel/babel/blob/master/packages/babel-compat-data/data/native-modules.json) and replaces the other targets; `browsers: string | Array`, which can specify a Browserslist query; `uglify: true`, which forces `@babel/preset-env` to compile everything. -We should move this option to `@babel/core` without limiting any of its current capabilities, except for `targets.uglify`. It has been deprecated since `babel-preset-env` 2.0, for two reasons: if you want to force all the possible syntax transforms regardless of your targets you can use `@babel/preset-env`'s `forceAllTransform` option, or you can dynamically set your `targets` correctly so that the option reflects what you need. +We should move this option to `@babel/core` without limiting any of its current capabilities, with the following differences: except for `targets.uglify`. + +#### `targets.uglify` + +This option shouldn't be ported to `@babel/core`. It has been deprecated since `babel-preset-env` 2.0, for two reasons: if you want to force all the possible syntax transforms regardless of your targets you canuse `@babel/preset-env`'s `forceAllTransform` option, or you can dynamically set your `targets` correctly so that the option reflects what you need. + +#### `targets.esmodules` + +In `@babel/preset-env` this option completely replaces the other targets: `{ targets: "chrome 40, firefox 70", "esmodules": true }` is resolved to `{ chrome: 61, firefox: 60 }`. +This behavior was a good idea when only recent version of browsers had support for native ECMAScript modules, thus `"esmodules": true` always lifted up the supported targets. However, it now happens that the specified targets are partially more recent than the browsers with native ECMAScript modules support: in this case `"esmodules": true` is unexpectedly causing more features to be compiled. + +`@babel/core`'s `targets.esmodules` option will instead always list up the resolved targets, by intersecting the existing targets: `{ targets: "chrome 40, firefox 70", "esmodules": true }` is resolved to `{ chrome: 61, firefox: 70 }`. ### `browserslistConfigFile` @@ -261,8 +272,11 @@ When we will finally remove the options from `@babel/preset-env`, if they are st # Open questions 1. Should `.browserslistrc` files be considered as project-wide or file-relative configs? + [**ANSWER**](https://github.com/babel/rfcs/pull/2#issuecomment-619573888): They are file-relative config files. 2. Is the "Merge the new options in a single object" better than having three new top-level options? + **ANSWER**: No, most configs will use at most one of these options anyway. 3. Should we also move the `forceAllTransform` option to `@babel/core`? + **ANSWER**: No, it's an option speficially for syntax transforms, necessary to feed Babel's output into tools that only support ES5 syntax. From fcff906abd642ad32a2e463054fc86df23a4cd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 13 Feb 2021 00:47:22 +0100 Subject: [PATCH 09/10] Update and rename 0000-top-level-browserslist.md to 0001-top-level-targets.md --- ...{0000-top-level-browserslist.md => 0001-top-level-targets.md} | 1 + 1 file changed, 1 insertion(+) rename rfcs/{0000-top-level-browserslist.md => 0001-top-level-targets.md} (99%) diff --git a/rfcs/0000-top-level-browserslist.md b/rfcs/0001-top-level-targets.md similarity index 99% rename from rfcs/0000-top-level-browserslist.md rename to rfcs/0001-top-level-targets.md index 0788b8a..a2cf3c5 100644 --- a/rfcs/0000-top-level-browserslist.md +++ b/rfcs/0001-top-level-targets.md @@ -5,6 +5,7 @@ - Authors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Champion: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) - Implementors: [@nicolo-ribaudo](https://github.com/nicolo-ribaudo) +- Not implemented yet # Summary From 44f0094649fc3f2685532ff9a657879d766b317b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 13 Feb 2021 00:47:54 +0100 Subject: [PATCH 10/10] Rename 0001-top-level-targets.md to 0002-top-level-targets.md --- rfcs/{0001-top-level-targets.md => 0002-top-level-targets.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rfcs/{0001-top-level-targets.md => 0002-top-level-targets.md} (100%) diff --git a/rfcs/0001-top-level-targets.md b/rfcs/0002-top-level-targets.md similarity index 100% rename from rfcs/0001-top-level-targets.md rename to rfcs/0002-top-level-targets.md