-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
module: disable CommonJS support if entry point is ESM #39508
Conversation
Instead of a hack, why not give a very clear error message? Teaching newcomers how to fix the problem will be more helpful to them in the long run than silently/magically pretending this is the way it’s supposed to work. |
It’s not silent: currently it shows a warning advising to use .mjs extension before displaying the SyntaxError stack trace. With this PR, instead of the SyntaxError, it shows another warning to tell that CommonJS is being disabled and they it loads the entry point as ESM. To the point of the reporter of the issue: if Node.JS suspects that the file is using ESM, why not try to parse it as that at this point? |
Because it can’t know that for sure - it’s a heuristic, because the parsing goals are ambiguous. node shouldn’t ever guess. The modules group explicitly and intentionally avoided any chance of being incorrect about the parse goal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should not have something that makes the interpretation of a file change depending on if it is loaded via the CLI or via import/require
. This was discussed at length in the modules WG and in general the idea starts to cause more problems than it helps over time: from being a heuristic having issues with detection and problems with forwards compatibility, to having problems when run under wrappers like PM2/mocha/etc., to having a less clear at a glance or debuggable situation when trying to understand what a file will execute as. If we are to have such a change as this heuristic detection it should always be on to prevent these problems. However, I am unclear how we would do so. Perhaps an alternative is implementing something like the --package
flag described in #38028 that would allow a stable interpretation of what format a file is within the process still and is intended for development/testing purposes per discussion. Designs like that allow for a beginner to still lack a package.json
file but don't cause tech debt and allow things like wrapping processes to properly act the same as a CLI entry point. Still, heuristics are tricky and deserve their own discussion. Using them to enhance errors is one thing but I don't think we should be using them to alter runtime behavior.
I would argue this does not change the interpretation of a particular file, it disables CJS support altogether.
What if we add in the warning that this behavior is experimental and could be removed in a semver-patch release? Would that address this concern?
I'm all for it, I still think this PR would make node more beginner-friendly.
I know that, I don't say this would cover 100% of the case. What it does is to check if the file contains any import or export declaration, and if there is any, chances are pretty high that it is indeed ESM syntax and that the user simply used the wrong extension, so it tries that.
Is there a way we could have this feature and you would feel confident users will not rely on it? Maybe by making the warning more annoying? By rethrowing the SyntaxError after executing the file maybe? Or do you think there is no way at all? |
@aduh95 CJS support should never be disabled in any possible scenario where node is running. (also, what about |
The problem is similar still. I don't think stating that all support is altered makes any single file more stable with this feature.
That would not be addressed; various features of ESM have become more ambiguous with CJS over time (HTML comments,
I think we should step back and talk about what this PR means for beginners rather than making that claim. I think we should think about what this does to people trying to begin using Node.js , not all of which are novice programmers:
const opts = parse(process.argv);
if (opts.command === 'a') {
(await import('./esm-lib-a.js'))(opts)
} else if (opts.command === 'b') {
(await import('./esm-lib-b.js'))(opts)
} This would not disable CJS and so various files would likely be treated as CJS.
I'm stating that it does have more effect than using the wrong extension for a file, it has affects with other features of node and isn't really a good fix compared to an error message and a user choosing the best solution for their issue. Using the wrong extension and completely disabling parts of core that aren't intending to be removed/deprecated are pretty wild to treat as an expected entanglement. I agree they likely wanted to run as ESM, what I don't agree with is the result of trying to guess how they want the module system to work or how we help them avoid the problem. I don't see this PR as helping over time; it might help initial setup in some cases, but often it looks like it will cause problems down the line. This is why having a flag or something would basically opt-in to some behavior temporarily rather than by assumption.
I think asking how to make users feel that a feature is unreliable is a sign that maybe we should figure out how to make a feature reliable instead. I don't think I'm in general thinking this feature is reliable; questions about how to make users feel confident it shouldn't be used is a scary thing to be asked and I don't really want to approach it. |
This would make the interpretation of files for editors and tooling even more ambiguous than it already is - at least right now, the lack of a package file is a strong indicator of cjs. With this? Ehhhh.... We go to a world where a loose js file could be anything, which we very much wanted to avoid the ambiguity of. We cant reliably report IDE errors at that point. |
If an IDE has to handle a
FWIW It's already possible to do what this PR is doing using loaders. And I don't see why it would be the case, not everyone is using CJS.
// entryPoint.js
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
createRequire(import.meta.url)(fileURLToPath(import.meta.url)); Currently:
With this PR: In the end, users still see a
I'm not sure that's fair, there would be two warnings displayed each time they run their app with node:
And they would be back at the current situation, which is not great IMHO, but seems to be the best we are able to make 🤷♂️ |
They should be forced to learn what CJS is - it’s unavoidable. Most of npm is, and forever will be, CJS. |
I always thought of Parsing was discussed at length yes. Performance was sighted as a concern. We now run cjs-module-lexer over all CommonJS files though, since that was able to get around the performance problems with a low-overhead lexer and Wasm. In theory this same lexer could be doing ESM syntax detection providing a reliable way to determine if a given JS file definitely has esm syntax. That argument is new to this old discussion, and I'm open to extending cjs-module-lexer to permit this type of syntax detection, but that doesn't change the other arguments here that there is still the surprise that removing an export changes the fact that your source code is running in strict mode or not, which was always the ambiguity concern. Wesley's argument about IDE compatibility also is an important one re predictable patterns for tooling assistance which only grows more important each day. Happy to dig in if anyones opinions have changed on the above though. I really would have hoped by now that |
(the npm rfc calls decided npm init should not include type module because the type flag is confusing and problematic, and best left out - not a debate for this thread tho) |
That seems an unfortunate outcome. Do you have a link to more information here? We can give them more time sure, but at the end of the day tooling needs to solve tooling problems. |
This doesn't solve all of the other problems in my comment above and does have them fix the warning. It also changes from the entry point being seen as ESM, to instead thinking all files are ESM; which to me is non-intuitive.
To be clear I'm talking about when that warning disappears because cases that generate
Only being able to use node builtins is... not very useful when most of the tutorials on node out there use things in the ecosystem like React or Express. Those do not currently ship ESM implementations. |
Also, to be clear using |
Closing as stalled. |
If the entry point is using ESM syntax and there's no
package.json
file to determine the default"type"
, disable CJS support and try to load the file again.This is an attempt to help newcomers get started with Node.js without forcing them to use
.mjs
or have apackage.json
to use ESM syntax. It should be noted that using.mjs
(orpackge.json#type
) would give you much better startup performance, I wouldn't recommend anyone to use this hack on production code.Behavior on
master
:Behavior with this PR:
/cc @nodejs/modules
Fixes: #39353