parcel: Parcel does not include polyfill for older browsers even when `browserslist` is declared in the `package.json`

🐛 bug report

RT.

🎛 Configuration (.babelrc, package.json, cli command)

Reproduction: https://github.com/SukkaW/parcel-issue-7419/

🤔 Expected Behavior

Parcel should include corresponding polyfills for cutting edge JavaScript features (like WeakMap, Set and Object.fromEntries) from core-js, which is a dependency of @parcel/config-defaults.

SWC Playground

😯 Current Behavior

Parcel doesn’t include any polyfill at all, even though browserslist is declared in the package.json.

💁 Possible Solution

Really? swc already handle this correctly. You guys just don’t use coreJs and mode: usage option by default? And you are telling me the parcel can be used with zero-configuration?

💻 Code Sample

https://github.com/SukkaW/parcel-issue-7419/

🌍 Your Environment

Software Version(s)
Parcel 2.0.1
Node Node 17.2.0
npm/Yarn 8.1.3
Operating System macOS 12.0.1

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 6
  • Comments: 21 (12 by maintainers)

Most upvoted comments

So what is the current recommended solution here?

@devongovett @mischnic, any updates on this? Maybe you should fix documentation (at least add link to this issue)

https://parceljs.org/languages/javascript/#browser-compatibility

You can declare your app’s supported browsers using the browserslist field in your package.json. When this field is declared, Parcel will transpile your code accordingly to ensure compatibility with your supported browsers.

It doesn’t working as expected, misleading and people waste time trying to setup parcel to work correctly

what’s the best workaround for this? back to having babel but running parcel to pickup a babel.config.json ? This does work but wonder what is working for others

{
  "presets": [
    [
      "@parcel/babel-preset-env",
      {
        "targets": {
          "chrome": "69"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.9"
      }
    ]
  ]
}

Also FYI, the swc has evolved and the mode: usage detection is now accurate

Maybe it has improved, but I don’t believe it is possible for mode: 'usage' to be fully accurate without a type checker. For example, core-js includes polyfills for builtin methods like String.prototype.includes. In order to detect when to include this, they’d need to look for any call of any method named includes on any object. Without type information, it’s not possible to know whether the target is a string or not. This leads to including way too many polyfills, bloating the bundle. In addition, there are ways of accessing methods that are not statically analyzable, leading to polyfills not being included. Therefore, I’m not sure usage detection is a very good idea.

I think it’s better to manually include polyfills either by importing core-js and letting Parcel/SWC include all polyfills needed for your environment, or just including the ones you need. The latter will result in the smallest bundle size.

Just a sidenote, but I think Parcel docs are at least a little bit misleading when it says this (in the Browser compatibility section):

When this field is declared, Parcel will transpile your code accordingly to ensure compatibility with your supported browsers.

Compare with Vite docs (also the Browser compatibility section) where you can find the info about polyfills straightaway:

Note that by default, Vite only handles syntax transforms and does not cover polyfills by default.

Of course Parcel docs talk about transpilation only, but I’m not sure if many people make that connection.

Transpilation means syntax is compiled. It doesn’t say anything about runtime polyfills.

@devongovett

I have just updated the issue title to Parcel does not include polyfills for older browsers. The title should be accurate now.

Also FYI, the swc has evolved and the mode: usage detection is now accurate (Shout out to @Austaras. He has fixed this in swc 1.2.220).

The reason that we don’t use usage is that its detection of used language features isn’t that accurate. So this should work:

So you can install core-js and import it from entry point, and parcel (swc) will only include required parts of it for the browserslist you specified.

@mischnic The swc has evolved and the detection is now accurate (since 1.2.220). Does the parcel team have a plan to adopt it (and solve the issue)?

@avalanche1 As far as I can tell, this is actually working mostly as expected. The core-js modules that are included is based on what is in your package.json:browserslist value. If you set it to chrome 99 then the included polyfills will be substantially less. In my case, the resulting bundle was 51.81 KB with --no-optimize --no-scope-hoist. If I change the browserslist to be IE 6, then the resulting bundle is 654.59 KB. Quite a big difference.

Interestingly, in my opinion 51 KB is quite big for targeting only Chrome 99. Upon investigation, it appears that swc isn’t quite smart enough to remove polyfills that are depended on by core-js itself. For example, Chrome 99 includes the web.immediate module, when in turn requires object-get-own-property-descriptor despite not actually needing it in chrome 99.

This is the require hierarchy:

core-js/actual
  core-js/stable
    core-js/modules/web.immediate
      core-js/internals/export
        core-js/internals/object-get-own-property-descriptor
          core-js/internals/ie8-dom-define			

Though, since these are internals and not modules, maybe there’s a specific reason they aren’t removed. Regardless, I don’t think this is such a big deal since when you optimize the build, the file size is significantly smaller at 10.21 KB for Chrome 99, and 111.44 KB for IE 6.