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

pkg.main, pkg.module are incompatible, so require("quickselect") behaves differently under Rollup/Webpack #11

Open
andersk opened this issue Aug 29, 2020 · 2 comments
Labels

Comments

@andersk
Copy link

andersk commented Aug 29, 2020

In Node, require("quickselect") returns the function quickselect, but in Webpack, require("quickselect") returns a module { default: quickselect }. This means a quickselect dependent can’t be compatible with both environments without contortions.

$ echo 'console.log(require("quickselect"))' > src.js
$ npm i quickselect webpack webpack-cli
$ node src.js
[Function: quickselect]
$ npx webpack -d
$ node dist/main.js 
Object [Module] { default: [Getter] }

This is because Node is using pkg.main (quickselect.js) and Webpack is using pkg.module (index.js), and the two files do not provide compatible interfaces.

This problem is called out in the Rollup documentation:

Note: There are some tools such as Babel, TypeScript, Webpack, and @rollup/plugin-commonjs that are capable of resolving a CommonJS require(...) call with an ES module. If you are generating CommonJS output that is meant to be interchangeable with ESM output for those tools, you should always use named export mode. The reason is that most of those tools will by default return the namespace of an ES module on require where the default export is the .default property.

This was previously touched on in #6, but only in the context of the current quickselect in Webpack being incompatible with an old quickselect in Webpack (resolved by bumping the major version), not the current quickselect in Webpack being incompatible with the current quickselect in Node (still an issue).

Three potential solutions are:

  • use named export mode, so require("quickselect").default would work everywhere; or
  • add quickselect.default = quickselect for compatibility; or
  • remove pkg.module.
@mourner
Copy link
Owner

mourner commented Aug 29, 2020

To be honest, my preferred solution would be to not support the "require in Webpack" case at all, and leave things as is. I still strongly believe this is a major mistake on the Webpack side (using the module entry point for modules that are loaded with require) which the maintainers frustratingly refused to fix, putting the burden for this mess on maintainers of all other open source projects with dual exports.

@andersk
Copy link
Author

andersk commented Aug 29, 2020

Annoyance at Webpack is understandable, but I don’t think it had a better option. Using pkg.module for import and pkg.main for require could result in two independent copies of that module being bundled, which at best leads to bloat, and at worst unexpectedly bifurcates its mutable state. Using pkg.module for require but auto-resolving default would break modules with both default and non-default exports.

Perhaps this problem could have been anticipated at the specification level when ES6 modules were being designed, but that ship has sailed. 😐

Rollup does the same thing as Webpack.

$ npm i quickselect rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve
$ echo 'console.log(require("quickselect"))' > src.js
$ npx rollup src.js -p node-resolve -p commonjs -f cjs -o out.js
$ node out.js
[Object: null prototype] { default: [Function: quickselect] }

@andersk andersk changed the title pkg.main, pkg.module are incompatible, so require("quickselect") behaves differently under Webpack pkg.main, pkg.module are incompatible, so require("quickselect") behaves differently under Rollup/Webpack Aug 29, 2020
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

2 participants