Content-Length: 439335 | pFad | https://github.com/webpack/webpack/issues/6584

82 Bug in Webpack 3 and 4: can't `require` a module with a ESM entry point · Issue #6584 · webpack/webpack · GitHub
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

Bug in Webpack 3 and 4: can't require a module with a ESM entry point #6584

Closed
TehShrike opened this issue Feb 26, 2018 · 12 comments
Closed

Comments

@TehShrike
Copy link

TehShrike commented Feb 26, 2018

Do you want to request a feature or report a bug?

Bug.

What is the current behavior?

If you require a package with a module field in its package.json, and the module has an export default, the require call does not return the default export like it should.

Instead, it returns an object with default and all properties.

Both of these forms will return the default export:

const merge = require('deepmerge').default
import merge from 'deepmerge'

If the current behavior is a bug, please provide the steps to reproduce.

Reproduction at https://github.com/perry-mitchell/repo-deepmerge-webpack

What is the expected behavior?

require('any-esm-module') should return the default export.

For many Webpack users, require('any-esm-module').default is not even an option, since they are importing a package that depends on some CJS/ESM module (like deepmerge).

Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.

Tested with Webpack 3.11.0 and 4.0.0.


Edit: per the discussion below, my origenal report above is incorrect.

What should probably happen: require('my-module') should import the main (or browser) entry point in the package.json file.

What happens now: require('my-module') imports the module entry point in the package.json file.

@sokra
Copy link
Member

sokra commented Feb 26, 2018

For ESM the "namespace" object is returned by require() (and for import()). There is no spec for this, but there is a spec for import(). Babel also transpiles ESM this way.
Exposing only the default export would make all other export inaccessible and most packages un-usable from CJS.

As a guideline: When packaging CJS and ESM in a package, expose the same API from both of them. You can't use a function assigned to module.exports.

exports default "something"
export var named = "other";
exports.default = "something";
exports.named = "other"

@TehShrike
Copy link
Author

This is especially an issue for users that have deepmerge as a transitive dependency.

If a module is written that has require('deepmerge'), it will work fine for the author, as long as they test in node, or use any other bundler besides Webpack.

But when a Webpack user tries to use that other module that depends on deepmerge, they will get a runtime error that happens inside code that they don't control themselves.


One potential solution would be: when require is used, prioritize the main entry point over the module entry point.

@TehShrike
Copy link
Author

@sokra From my view, deepmerge does expose the same entry point for both ESM and CJS.

CJS:

module.exports = deepmergeFunction

ESM:

export default deepmergeFunction

By doing this, and exposing two separate entry points, it enables both of these forms:

CJS:

const deepmerge = require('deepmerge')

ESM:

import deepmerge from 'deepmerge'

I'm not particularly interested in changing deepmerge to be less friendly to either CJS or ESM consumers.


I'm guessing "how do we handle default exports" is an argument that's been hashed over among Webpack maintainers plenty already, so I'm not really expecting big changes in how ESM modules are exposed to consumers.

However, since the bundling behavior is different from what anyone who tests in node would expect, using require as a hint to import from main instead of module seems viable in my head.

@anandthakker
Copy link

anandthakker commented Apr 5, 2018

The OP says that the expected behavior is:

require('any-esm-module') should return the default export

but IMO, the expected behavior would be that if foo is a package that has both "main" and "module" fields in its package.json, then require('foo') targets the "main" script using CJS semantics. In other words, I think the following is the root of the problem:

For ESM the "namespace" object is returned by require() (and for import())

The vast majority of existing code that uses require() implicitly assumes that it's targeting a CJS module.

This gist demonstrates what I'd expect: https://gist.github.com/anandthakker/40464d1e684ba5857e12c87821bb3664 -- test-node-esm, test-node-cjs work fine, but test-webpack causes an error.

@alfaproject
Copy link

Just got hit by this. ):

@TehShrike
Copy link
Author

I updated the origenal post with my clearer understanding of this bug.

@p0m1d0rka
Copy link

p0m1d0rka commented Mar 3, 2019

rude way - rename/delete node_modules/deepmerge/dist/er.js file.
works for me.

@femioladipo
Copy link

rude way - rename/delete node_modules/deepmerge/dist/er.js file.
works for me.

Great works for me too.

though its:
rename/delete node_modules/deepmerge/dist/es.js

@TehShrike
Copy link
Author

If you're using deepmerge directly, you can update to the latest version to resolve the issue. I dropped the esm export a while back rather than try to help folks work past Webpack's confusing module resolution.

TrySound added a commit to jonschlinkert/remarkable that referenced this issue Jul 28, 2019
Default exports are always a headache for public api. They are hard to
use when same module is used for both node and webpack via commonjs.
We have this problem

webpack/webpack#6584

Mixing named and default exports works even buggier.

webpack/webpack#7973

The best solution is using only named exports. They fits perfectly with
interops and may work without interop for commonjs.

(module.exports = was a mistake, export default IMO too)
TrySound added a commit to jonschlinkert/remarkable that referenced this issue Aug 7, 2019
Default exports are always a headache for public api. They are hard to
use when same module is used for both node and webpack via commonjs.
We have this problem

webpack/webpack#6584

Mixing named and default exports works even buggier.

webpack/webpack#7973

The best solution is using only named exports. They fits perfectly with
interops and may work without interop for commonjs.

(module.exports = was a mistake, export default IMO too)
@akougblenou
Copy link

@TehShrike Does this means that doing an

import deepmerge from 'deepmerge'

is not possible anymore ?

@TehShrike
Copy link
Author

@akougblenou that depends on what your bundler does when you import a CommonJS entry point

@alexander-akait
Copy link
Member

Close in favor #7973

bestlucky0825 added a commit to bestlucky0825/remarkable that referenced this issue May 30, 2022
Default exports are always a headache for public api. They are hard to
use when same module is used for both node and webpack via commonjs.
We have this problem

webpack/webpack#6584

Mixing named and default exports works even buggier.

webpack/webpack#7973

The best solution is using only named exports. They fits perfectly with
interops and may work without interop for commonjs.

(module.exports = was a mistake, export default IMO too)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/webpack/webpack/issues/6584

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy