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

Imports in ES modules revert to loading CommonJS #9329

Closed
aldeed opened this issue Jun 25, 2019 · 6 comments
Closed

Imports in ES modules revert to loading CommonJS #9329

aldeed opened this issue Jun 25, 2019 · 6 comments
Labels

Comments

@aldeed
Copy link

aldeed commented Jun 25, 2019

Bug report

What is the current behavior?
In the latest version of Webpack 4 with default config, imports in .js files prefer a package module, whereas identical imports in .mjs files for some reason revert to loading the package main.

This is true within an app, but it's also true within node_modules. For example, if package A that exports ES modules depends on package B that also exports ES modules, Webpack will correctly prefer ES modules for package A, but will revert to bundling the CommonJS entry point for package B.

The most visible symptom of this issue is package code being in the bundle twice. This is bad for file size, but more importantly, some packages cause errors when they are loaded twice. We first saw this issue with the styled-components package, which depends on having only one copy of itself in the bundle.

If the current behavior is a bug, please provide the steps to reproduce.
Here's a very simple reproduction app: https://github.com/aldeed/webpack-es-module-resolution-issue

See the Readme there.

This is a reproduction of the issue within an app. I've also reproduced the package variation of this bug mentioned above. I can post that if it's helpful, but I suspect they're both the same root cause.

What is the expected behavior?
Webpack docs state that by default module will be preferred in v4+. This is true when imported from CommonJS only. I believe this should be true regardless of whether the import source is CommonJS or is itself a module.

Other relevant information:
webpack version: 4.35.0
Node.js version: 11.8.0
Operating System: OSX
Additional tools:

@sokra
Copy link
Member

sokra commented Jun 30, 2019

Since .mjs is an experimental node.js feature, it tries to behave identical to .mjs in node.js which doesn't support the module field.

EXPERIMENTAL: .mjs should not used in production.

@aldeed
Copy link
Author

aldeed commented Jul 1, 2019

This is true @sokra but a few counter-points:

  • Webpack default config prefers module now, so you are saying "don't use default Webpack config in production"?
  • The variation of this issue that happens with package imports is an issue through no fault of the app maintainer. They are not opting in to anything experimental. As long as two packages I'm using have module entry points (increasingly common), I'll experience the double loading issue.

Also, is there any config option that would fix this as a workaround for now? It seems more like a bug than a configuration thing.

@sokra
Copy link
Member

sokra commented Jul 2, 2019

  • Webpack default config prefers module now, so you are saying "don't use default Webpack config in production"?

The module field is fine. I said don't use .mjs files in production. It's an experimental node.js feature.

  • The variation of this issue that happens with package imports is an issue through no fault of the app maintainer. They are not opting in to anything experimental.

The library opts-in to an experimental feature by using the .mjs extension. But you are right the app developer didn't opt-in. It was a wrong decision to enable .mjs support by default in webpack 4. So webpack 5 disables experimental features by default and you must opt-in in config (experiments.mjs).

By using the .mjs extension you opt-in into the mjs way of resolving, which comes with it's own issues...

Also, is there any config option that would fix this as a workaround for now?

You can override this behavior with:

module.rules: [
  {
    test: /\.mjs$/,
    resolve: { mainFields: [ "browser", "module", "main" ] }
  }
]

@aldeed
Copy link
Author

aldeed commented Jul 12, 2019

Thank you @sokra, that is helpful. I was confusing module field with .mjs extension, assuming you had to use .mjs extension for a module field file. If I instead use module with a normal .js file, I don't see the double loading.

You mention that Webpack is trying to follow Node behavior. Digging into the Node docs, I assume you are referring to the fact that Node doesn't support module field, but rather expects a type: "module" or .mjs extension and always uses the main field.

https://nodejs.org/api/esm.html#esm_package_entry_points

I don't quite understand that reason, because Webpack does respect the module field, and it isn't Node. Why would it make sense for Webpack to respect module until it hits a .mjs file and then revert back to resolving from main?

To put the question another way, when I import "random_package" that has a module field, isn't it confusing that the bundle will be different just based on what file extension the package author used?

@webpack-bot
Copy link
Contributor

This issue had no activity for at least three months.

It's subject to automatic issue closing if there is no activity in the next 15 days.

@webpack-bot
Copy link
Contributor

Issue was closed because of inactivity.

If you think this is still a valid issue, please file a new issue with additional information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants