turndown icon indicating copy to clipboard operation
turndown copied to clipboard

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

Open andersk opened this issue 3 years ago • 3 comments

In Node, require("turndown") returns TurndownService, but in Rollup or Webpack, require("turndown") returns { default: TurndownService }. This means a Turndown dependent can’t be compatible with both environments without contortions.

$ npm i rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve turndown webpack webpack-cli
$ echo 'console.log(require("turndown"))' > src.js
$ node src.js
[Function: TurndownService]
$ npx rollup src.js -p node-resolve -p commonjs -f cjs -o out.js
$ node out.js
{ default: [Getter] }
$ npx webpack --mode=development --target=node
$ node dist/main.js 
Object [Module] { default: [Getter] }

This is because Node is using pkg.main (lib/turndown.cjs.js), while Rollup and Webpack are using pkg.module (lib/turndown.es.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.

Three potential solutions are:

  • use named export mode, so require("turndown").default would work everywhere; or
  • add TurndownService.default = TurndownService for compatibility; or
  • remove pkg.module (and presumably pkg["jsnext:main"]).

andersk avatar Aug 29 '20 04:08 andersk

I'm currently facing this issue in an environment that runs both in Node and a webworker. Is there a workaround to this issue?

jackmawer avatar Oct 27 '20 12:10 jackmawer

First of all, thanks for the steps to reproduce! Though they're not working with the current webpack.

There are also a few changes in packaging since 7.1.1 (and currently even Rollup outputs warning related to this), the current output is:

$ npm i rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve turndown webpack webpack-cli
added 151 packages, and audited 152 packages in 5s
[...]
$ echo 'console.log(require("turndown"))' > src.js
$ node src.js
[Function: TurndownService]
$ npx rollup src.js -p node-resolve -p commonjs -f cjs -o out.js

src.js → out.js...
(!) Entry module "src.js" is implicitly using "default" export mode, which means for CommonJS output that its default export is assigned to "module.exports". For many tools, such CommonJS output will not be interchangeable with the original ES module. If this is intended, explicitly set "output.exports" to either "auto" or "default", otherwise you might want to consider changing the signature of "src.js" to use named exports only.
https://rollupjs.org/guide/en/#outputexports
src.js
created out.js in 157ms
$ node out.js
{ default: [Getter] }
$ npx webpack -d
[webpack-cli] Error: Option '-d, --devtool <value>' argument missing
[webpack-cli] Run 'webpack --help' to see available commands and options

Would you mind looking into the issue again and suggest updated options to solve it if the problem persist?

martincizek avatar Jul 04 '21 12:07 martincizek

Your output demonstrates that the problem persists with Rollup. (The warning from rollup src.js is about the exports of src.js, which don’t matter in this example. It can be appeased with either --exports=named or --exports=default with no other effect. The problem is in Turndown’s Rollup configuration, not mine.)

For webpack-cli@4, replace -d with --mode=development --target=node.

andersk avatar Jul 04 '21 20:07 andersk