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

ESM: exports not utilised when importing CJS. #29537

Closed
joeyhub opened this issue Sep 12, 2019 · 18 comments
Closed

ESM: exports not utilised when importing CJS. #29537

joeyhub opened this issue Sep 12, 2019 · 18 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. question Issues that look for answers.

Comments

@joeyhub
Copy link

joeyhub commented Sep 12, 2019

Version: v12.10.0
Platform: msdos, x86_16
Subsystem: ESM

Using "type": "module" and an mjs as an entry point, module isn't uses from package.json and when falling back to main (CJS), using purely exports rather than assigning module.exports doesn't appear to work.

Seen when trying to use rollup-plugin-analyzer.

@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

Also doesn't work with:

import traverse from "@babel/traverse";

Despite docs using include and despite working for core which looks about the same (strange thing with default being set).

@shobhitchittora
Copy link
Contributor

@joeyhub sorry but I couldn't understand the problem here correctly. Can you share some code sample of your issue here? Thanks.

@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

"type": "module",

Just set that in JSON.

Even just running node and then require('@babel/traverse') needs me to do traverse.default.

Both of these libraries do this:

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = some_func;

Both also define the script as the main.

@devsnek
Copy link
Member

devsnek commented Sep 16, 2019

I'm seeing this, which is the expected output

@devsnek devsnek added esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. labels Sep 16, 2019
@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

What happens if you:

node --experimental-modules --input-type=module -e 'import traverse from "@babel/traverse";traverse();

I get TypeError: traverse is not a function. It's some weird fallback thing using __esModule that's not coming into play with the ESM for picking up default.

With one case I tried just assigning module.exports instead of just exports.default instead and it worked.

@devsnek
Copy link
Member

devsnek commented Sep 16, 2019

@joeyhub its not a function, its an object. think of it as export default require('@babel/traverse');

@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

According to a google search, I think it's something to do with this:

Something like this was the first hint many of us detected that there was a family of squirrels living under our wallpaper.

Is the __esModule thing an old fallback prior to full ESM support to at least allow require to return a default as well as a nest of object entries?

Did node ever support that or was it something people were doing and introducing an unnecessary transpile pass inadvertently for people that wanted to use the library straight up?

If so do npm packages with an entry point like this need to be pointed out (they should otherwise dist with it compiled properly to cjs/es)?

@devsnek
Copy link
Member

devsnek commented Sep 16, 2019

__esModule is a nonstandard babel (and later typescript) thing. In real native esm, the exports of a module have to be declared before it is evaluated, so we have no way of mapping cjs exports to esm (and even if we could, i'd still argue that export default require() makes more sense)

@devsnek devsnek added the question Issues that look for answers. label Sep 16, 2019
@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

So basically I need to raise this with the NPM packagers for those libraries to fix their build and maybe push for it to be recognised as a problematic practice.

@devsnek
Copy link
Member

devsnek commented Sep 16, 2019

i don't have a one-size-fits-all solution, but i will note that require('some babel tool').default has bothered me for years.

@joeyhub
Copy link
Author

joeyhub commented Sep 16, 2019

It's really problematic if you look for example at the babel manual. They have straight up ESM examples with none of that and no impression that you have to babel compile their library before you can use it in the way they specify (otherwise the ugly .default).

Even is packages do use that function, I'm certain they're making a mistake at this point now with proper ESM not compiling fully to a CJS (main, legacy?) and ESM (module) entry point.

If they were giving examples using as though that ESM style hack was in use before but not documenting to do that you need a whole transpile pass then I think they were also making a mistake even then (with a lot of users probably stumbling around to find .default or worse making an unnecessary build of something already meant to be built for you).

If it was never supported by node then it should be gradually purged.

@Nainterceptor
Copy link

Hello guys,

@joeyhub Please confirm that issue is the same : https://github.com/Nainterceptor/node-issue-29537 ?

Quick explain :
root has a "type": "module" and is .mjs.
dependency has both cjs and esm (typically like when you have a typescript build).
dependency has main and module keys pointing on cjs and esm parts
root load the cjs part, ignoring "module" key.
If I add "type": "module" in the dependency, app is trying to load the main key (so the cjs script) as an esm module.

@joeyhub
Copy link
Author

joeyhub commented Sep 29, 2019

I'm using type as module and mjs for the entry point (but after that I stick with js).

I am noticing it appears to load the cjs (main) even though there's also a module in many cases though I have a tendency to get mixed up as well using ESM native locally but rollup to ship off the the browser.

Maybe it is some inconsistent behaviour then and not just some annoyance with a stop gap getting in the way?

I wonder if the resolve hook can bump it to the module version if present (probably not trivially / without repeating stuff, would probably want the loader hook but that's a bit of a mess right now).

I wonder if this:

[13fa966] - module: error for CJS .js load within type: module (Guy Bedford) #29492

@devsnek
Copy link
Member

devsnek commented Sep 29, 2019

@Nainterceptor if you have type: module you need to change any cjs files to use .cjs instead of .js.

i think this issue has been resolved at this point.

@devsnek devsnek closed this as completed Sep 29, 2019
@Nainterceptor
Copy link

@devsnek What ? In my code example, I don't want to load cjs, but cjs is loaded.
NodeJS is not reading the "module" key of the dependency's package.json, so he's trying to import the main path as esm. I don't know if it's the expected behavior but it seems weird.

@devsnek
Copy link
Member

devsnek commented Sep 30, 2019

@Nainterceptor node doesn't use the module field, there is too much code using it that is not compatible with native esm.

@Nainterceptor
Copy link

@devsnek Thanks for the explaination ! So we've to use the main field, with files .js (or .cjs) and .mjs side-by-side, and let node choose the right one.

@matiasg77
Copy link

i don't have a one-size-fits-all solution, but i will note that require('some babel tool').default has bothered me for years.

this is what is works for me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. question Issues that look for answers.
Projects
None yet
Development

No branches or pull requests

5 participants