Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The esmodules target should be more future friendly #8809

Closed
philipwalton opened this issue Oct 3, 2018 · 16 comments · Fixed by #12189
Closed

The esmodules target should be more future friendly #8809

philipwalton opened this issue Oct 3, 2018 · 16 comments · Fixed by #12189
Labels
i: enhancement outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@philipwalton
Copy link

Feature Request

I'm super glad to see the esmodules alias directly supported in preset-env, but I have concerns that it will very soon become mostly useless.

While it's true that today most module-supporting browsers are generally modern, that won't be true a few years from now. For example, when Chrome 86 ships in two years, Chrome 61 will seem horribly old, but people using the esmodules target will still be getting transpilations and polyfills applied for it.

Today the esmodules target is kind of like a whitelist for any browsers greater than or equal to the minimum module-supporting browsers versions specified here. But it'd actually be much better as a blacklist that filters out any browsers that don't support modules.

To understand the difference, consider what I currently do on my blog:

{
  targets: {
    browsers: [
      // The last two versions of each browser, excluding versions
      // that don't support <script type="module">.
      'last 2 Chrome versions', 'not Chrome < 60',
      'last 2 Safari versions', 'not Safari < 10.1',
      'last 2 iOS versions', 'not iOS < 10.3',
      'last 2 Firefox versions', 'not Firefox < 60',
      'last 2 Edge versions', 'not Edge < 16',
    ],
  },
}

I'd like to see the esmodule target work similarly. For example, I'd like to be able to do something like this:

{
  targets: {
    browsers: ['last 2 version, > 1%'],
    esmodules: true,
  },
}

And that would effectively turn my query into this:

{
  targets: {
    browsers: [
      'last 2 version, > 1%',
      'not Chrome < 60',
      'not Safari < 10.1',
      'not iOS < 10.3',
      'not Firefox < 60',
      'not Edge < 16',
      'not Opera < 48',
    ],
  },
}

This would allow me to specify my feature support exactly as I'm used to, but I'd be sure I wasn't getting any features not needed by browsers that work with <script type="module">.

/cc @kristoferbaxter

@babel-bot
Copy link
Collaborator

Hey @philipwalton! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

@kristoferbaxter
Copy link
Contributor

I'm not certain I see the difference between these two cases since the lower bar determines the transpilation required afaik.

edge >= 16, firefox >= 60, safari >= 10.1, opera >= 48, ios_safari >= 10.3

should be treated equivalently to

  'last 2 version, > 1%',
  'not Chrome < 60',
  'not Safari < 10.1',
  'not iOS < 10.3',
  'not Firefox < 60',
  'not Edge < 16',
  'not Opera < 48',

Am I missing something?

@philipwalton
Copy link
Author

There's a subtle difference between the two. In the former you're including Chrome 60-67 (since current Chrome is 69), and in the latter you're only including Chrome 68-69 (speaking specifically about Chrome). And my point above was, in two years when the current Chrome version is 86 the former will include Chrome versions 60-84, and the latter will only include Chrome 85-86.

As more and more JS features get added to the language and get implemented in modern browsers, the need to care about Chrome 60 will go away, but the need to support IE11 will likely still be real.

That's why I say the esmodules target should exclude browsers that don't support modules rather than include all browsers that do.

@kristoferbaxter
Copy link
Contributor

It's an interesting idea, but it does break one specific part of the pattern.

Let's use the Chrome 86 example:
When configuration is layered as explained here, devices that support modules are not all guaranteed to work with the file referenced as type module.

Let's pretend Chrome 85 adds a feature, and our configuration here means the feature is not transpiled by Babel (browsers: ['last 2 version, > 1%'], esmodules: true). Now visitors who are on Chrome 83 receive a bundle that cannot be leveraged. This breaks the configuration model as it stands today.

Progressive transpilation is likely a better long term answer. With clear input signals from the user-agent regarding supported JS features transpilation can be far more exact.

However, I'd love to hear others thoughts as well, perhaps compatibility isn't as strong of a reason to continue with the model as I believe it is.

@philipwalton
Copy link
Author

I mean, from my perspective the whole point of browserlist (and tools that use it like autoprefixer and babel-preset-env), is that you can specify a relative query and the required transforms/polyfills/vendor prefixes/etc. needed will slowly change over time.

Let's pretend Chrome 85 adds a feature, and our configuration here means the feature is not transpiled by Babel (browsers: ['last 2 version, > 1%'], esmodules: true). Now visitors who are on Chrome 83 receive a bundle that cannot be leveraged. This breaks the configuration model as it stands today.

It may break the new esmodules model, but I don't think it breaks the preset-env model, which (I believe) uses the browserlist default of > 0.5%, last 2 versions, Firefox ESR, not dead when no targets are specified.

That default is pretty inclusive, so if you exclude from that list any browsers that don't support modules, I think it'd be a fairly safe way to deploy ES module scripts.

@kristoferbaxter
Copy link
Contributor

kristoferbaxter commented Oct 4, 2018

The default configuration has a likely lowest common denominator of IE11 (https://browserl.ist/?q=%3E+0.5%25%2C+last+2+versions%2C+Firefox+ESR%2C+not+dead).

This is a pretty inclusive list, and is well served by the default nomodule script. However, if we narrow the definition of the module target its now possible to exclude support for newer user-agents supporting modules, but not a specific feature added post module support.

With the aforementioned recommendation, incompatibility is completely under the developer's control (I think that's really nice). But it's more likely to create scenarios where browsers are unsupported entirely. A tradeoff that is worth a bit of consideration.

Edit: This kind of incompatibility is why progressive transpilation is an interesting longer term solution for those willing to add a bit more complexity. And, hopefully, with clearer signals from browsers regarding supported features.

Double edit: Terms and language clarifications.

@schmod
Copy link

schmod commented May 21, 2019

Interestingly, the fix for #9465 now means that esmodules: true pulls in the (extremely-heavy) async/regenerator transform, because Safari 10 supports ESModules, but has a buggy implementation of async functions.

@developit
Copy link
Member

Regenerator inclusion seems like a good baromwter for whether this is going the right direction. I'm wondering if it's worth making a differentiation here between "widespread support" and "free from all browser bugs"?

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented May 21, 2019

Actually we have a test that ensures that generators aren't transpiled: https://github.com/babel/babel/tree/master/packages/babel-preset-env/test/fixtures/preset-options/esmodules-async-functions
If you could provide an example, maybe it is a bug 🤔

@schmod
Copy link

schmod commented May 22, 2019

@nicolo-ribaudo Here's a codesandbox showing generator functions being added to a very simple source file when esmodules: true is set on Babel v7.4.4:

https://codesandbox.io/embed/goofy-frost-1ened

On further examination, you're correct that regenerator isn't being included, but the transformation of async functions is still pretty heavy (and something we'd expect to be natively-supported in "modern" environments, which is what esmodules is meant to emulate).

@renchap
Copy link

renchap commented Nov 13, 2019

Can https://github.com/babel/preset-modules solve this?

@nicoqh
Copy link

nicoqh commented Oct 17, 2020

While it's true that today most module-supporting browsers are generally modern, that won't be true a few years from now. For example, when Chrome 86 ships in two years, Chrome 61 will seem horribly old, but people using the esmodules target will still be getting transpilations and polyfills applied for it.

Speaking as someone from the future you envisioned, the esmodules flag makes my bundle even bigger due to the transformations and polyfills that are needed by "modern" browsers like the three year old Chrome 61 (I'm on Chrome 86).

This made me look into the possibility of using my current browserslist query with the addition of esmodules: true in order to filter out browsers that don't support ES modules (just like @philipwalton proposed).

That's not how the esmodules flag works, so unless I've missed some important I can't use the esmodules flag if I want to 1) target only ESM browsers and 2) add my own target requirements (not_dead, > 0.5 % etc.) to filter out, say, Chrome 61.

@nicolo-ribaudo
Copy link
Member

Wdyt about adding esmodules: "intersect", that is intersected to your browserslist targets rather than replacing them?

@nicoqh
Copy link

nicoqh commented Oct 21, 2020

@nicolo-ribaudo, that would certainly solve the problem! I'm not sure if the current default behavior is useful (?), so perhaps the "intersects" option could eventually become the default.

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Oct 21, 2020

I have moved to discussion to babel/rfcs#2 (comment), since we are working on moving the targets option out of @babel/preset-env and this could be an opportunity to change the default behavior. (please write there if you have any opinion!)

@nicolo-ribaudo
Copy link
Member

Fixed by #12189. It will be released in the next minor version as part of the new top-level targets option.

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label May 3, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
i: enhancement outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants