-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
Conflicting behavior with exports
and "type": "module"
#58057
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
Comments
Why would you expect that? Type module means, every js file in that package scope is treated as ESM. |
I would expect it because I am declaring explicitly in the conditional exports that this particular export should be used for commonjs (require). IMO the conditional exports and
The problem roots in the fact that most modern tooling relies on the |
You can require ESM now. The |
But the problem is that ESM cannot further Package authors create CJS and ESM flavors of their files and declare them accordingly. IMO it is unrealistic that one would specify a ESM file in the I am in a CJS execution scope, so
Thats one standpoint to see it, and it's flawed if you think of module graphs. The current behavior causes a reduced matrix of possible import/require scenarios which will successfully work. And the prioritization of transformed loading vs. same loader increases the risk of the "dual package hazard". I did a quick search and I think quite an amount of issues like these could have been avoided:
If Node.js really wants to be strict on "works like intended" for the current behavior: I strongly vote for a change of the intended behavior to match closer the real world needs due to given reasons. Package authors need a better way of controlling the load behavior for modules/exports than only using the file extension |
Yeah. changing
to
fixes the error |
If you avoid type module entirely, things are much simpler - .js for CJS and .mjs for ESM. I'd personally recommend that approach. |
This is not easily possible as a variety of tooling nowadays relies on the central configuration in the If there would be different settings for the local development workspace and built+published NPM package it would be likely less problematic. I'd say projects nowadays want to use mostly ESM when working within the development environment hence we specify |
What tooling? You don't need the "type" field whatsoever to use ESM - the only thing it does is change what ".js" means. |
The following list should give an insight of the impact of changing
If the recommendation is: Remove a) Rewrite the code to use CJS features only (no top-level-await, __dirname/__filename,...) |
next.js doesn’t support mjs, the standard file extension for ESM? That’s very surprising to me. |
From what I can tell from the problem description, a possible solution is not changing the current interpretation of
(I am somewhat puzzled by why the package is prioritizing CommonJS to use the Although on the other hand I think the conditional exports in the OP is still not quite idiomatic as explained in #52174 - using "which loader is loading this module" to differentiate what files to provide can already incur problems because that assumes to much about the loader, which the package doesn't get to control, the more optimal differantiation is "in which format this module can support being loaded", that's something the package can actually control. |
Also from the issue statement
It sounds like another possible solution could be moving the scripts and configurations to a different directory governed by a different |
Sorry for the delayed reply. In short: I'd be fine with a new dedicated conditional export which allows me to specify that a file should be loaded via the CJS or ESM loader. The approach would be good because other tools would not receive any change in the existing behavior they have to adopt, but rather would need to add support for the new condition.
If I use
I partially agree. At the same time: As a package author I ship my code in multiple flavors so that it can be consumed in compatible environments. e.g. UMD, AMD, CJS, globalThis, ESM. In a happy world, every flavor should stay in its own world. But realistically there is a need for cross-loading scenarios in module graphs making a loaders life harder. I'd say it is less prone to errors if packages can advertise: " My thinking might be too naive though and maybe there are many edge cases I'm not considering. The whole
It certainly solves some aspects of individual scripts. Unfortunately the approach is only solving things partially.
For my packages I'll try to reduce the commonjs/umd support to a minimum on the next major release to reduce the risk of having problems. I also have a workaround for me ( Looking at the ecosystem and others might make this still a relevant topic. Likely that's just the tip of the iceberg and problems are multiplying across the ecosystem. Every project affected receives bug reports and then somehow builds workarounds. |
this isn't accurate; you can have the root use type module, and make a subfolder with a package json that has nothing in it but |
Version
v22.14.0
Platform
Subsystem
internal/modules/cjs/loader
What steps will reproduce the bug?
type
is set tomodule
and the file extensions forrequire
exports arejs
.packages/pkg1/package.json
.js
files use commonjs code (require
)packages/pkg1/main.js
packages/consumer/package.json
packages/consumer/test.js
test.js
in node and get presented an error:How often does it reproduce? Is there a required condition?
Reproduces always.
What is the expected behavior? Why is that the expected behavior?
I would expect that node loads the
main.js
with the commonjs loader as it is and ignore the fact that.js
maps to ESM from a "type: module" spec.IMO the conditions of the conditional exports, need to also control which loader is used for the file. Its not just defining which file to use when resolving a module from CJS or ESM context.
What do you see instead?
Node.js only uses the exports for looking up the filename but then does a fallback to the default extension based loading. Due to
type: module
it treats it as ESM and wrongly loads it.Additional information
I know my setup is a bit special so I want to share some insights why I have it like this, and why changing is tricky.
In reality my files are not "commonjs" but rather Universal Module Definition (UMD) files. My library is also used in classical browser script tag setups. This is for backwards compatibility with the current major version.
Changing to
.cjs
is a workaround that could work. But its has these impacts:a) I considering the rename a breaking change for my library and I don't want to bump my major version just for this sake.
b) It could impact CDNs and Web Servers which might not have the extension registered as
text/javascript
and browsers triggering warning/errors on load due to wrong mimetypes. (e.g. jsdelivr serves it asapplication/node
, or IIS 10 in doesn't have it either)c) Its not the "correct" file extension for browser usage.
Another workaround is to remove
type: module
. But within my git repository I want to use primarily ESM (e.g. for scripts or configuration files). And this becomes problematic when using typescript configuration files. Some tools do not support something like a.mts
breaking a variety of things.I've looked through the following docs and generally I'd expect a
.js
to be loaded with the CJS loader if its declared accordingly in theexports
section.https://nodejs.org/api/packages.html#dual-commonjses-module-packages
https://nodejs.org/api/packages.html#conditional-exports
https://nodejs.org/api/packages.html#exports
Looking at the code I can also see that Node just resolves the plain path, but then ignores the "loader hint".
https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js#L1286
.js
always usingtype
from the package json: https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js#L1887Node should remember the loader type from the conditional export and load the file accordingly, not use to some alternative/automatic loading behaviors than specified. That's why I report this behavior rather as a bug than an improvement request.
The text was updated successfully, but these errors were encountered: