-
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
node should execute files without extension as ESM if "type": "module",
is specified
#34049
Comments
This is so that `node` doesn't traverse up the directory structure to find the `package.json` that the user is working in and use its settings. Since we are using commonjs in the script it will crash if `node` finds a `package.json` file with `type: module`. nodejs/node#34049
I can't believe it's been a year and this is still not fixed. How are you supposed to use simple executable with a shebang that you want to add to $PATH? You don't want to use myprogram.js in your terminal. Not even --input-type works (not that it would be useful for $PATH executables):
|
I had exactly the same problem, try deleting repository folder and cloning it again, it helped me, or as a last resort, you can try wsl |
@wsehl I don't think proposed solutions would help. In an empty folder this works:
But not in a folder with package.json containing
|
Here's a workaround assuming cd bin
mv program program.mjs
echo '{ "type": "commonjs" }' > package.json
printf '#!/usr/bin/env node\n"use strict";import("./program.mjs");\n' > program Then you can add it to your |
@aduh95 Thank you for the workaround. It worked 🎉 I had to add manual handling of rejections to make TLA errors in
However it's very sad to have to use this workaround in node and it's going to make backwards compatibility with already released node versions hard even if it's fixed. I hope that node team will fix this soon before everyone starts using this workaround although some package maintainers that care about being compatible with all node versions will always have to. |
I also encountered this problem, but I was able to work around it by adding |
(This comment is more fitting for #37512, but that one was closed as a duplicate of this, so posting it here.) Perhaps it is best to repost this as a new issue, since I haven't since any serious discussion about the plethora of bogus workaround suggestions. I'm surprised at the fact that writing an esm script is still such a huge pita. But this is not new, since it seems that everyone and their cats are surprised at this too. What's also surprising is the constant flood of "workaround" hacks that are fundamentally and utterly broken (like the above, or that one). Here's a list of what I can remember:
A proper way (which some imaginary For reference, here's the only sane prefix that I found so far. It's pretty horrible, but it works. I'd be very happy if there's a better way to make stuff run.
|
FWIW you could achieve the same thing with a smaller boilerplate using the experimental loader API (Node.js 16.12+): #!/bin/sh
/*.................................................... 2>/dev/null # -*- js -*-
exec node --experimental-loader 'data:text/javascript,let%20t%3D!0%3Bexport%20async%20function%20resolve(e%2Co%2Cn)%7Bconst%20r%3Dawait%20n(e%2Co)%3Breturn%20t%26%26(r.format%3D%22module%22%2Ct%3D!1)%2Cr%7D' "$0" "$@"
*/ Un-minified loader codelet entryPoint = true;
export async function resolve(url, context, next) {
const nextResolve = await next(url, context);
if (entryPoint) {
nextResolve.format = 'module';
entryPoint = false;
}
return nextResolve;
}
Having a separate binary comes with its own challenges and drawbacks, we wouldn't want to end up in a Python 2 / Python 3 scenario. If you feel strongly about this, you could create that binary yourself (which could be a simple Bash script that calls |
@aduh95: thanks for the suggestion. I saw this mentioned, but ignored it since it's very clearly experimental. But it still fails:
BTW, the imaginary |
@aduh95, Update: looks like the value is a promise rather than a value, so the following is more likely what you wanted to do (I added the
|
I guess you could add the
My bad, I forgot the #!/bin/sh
/*.................................................... 2>/dev/null # -*- js -*-
exec node --experimental-loader 'data:text/javascript,let%20t%3D!0%3Bexport%20async%20function%20resolve(e%2Co%2Cn)%7Bconst%20r%3Dawait%20n(e%2Co)%3Breturn%20t%26%26(r.format%3D%22module%22%2Ct%3D!1)%2Cr%7D' "$0" "$@"
*/
The loader API is your best hope to make |
@elibarzilay I admire your persistence. I have lost way too much time due to this issue. I'm disappointed that there needs to be a discussion about this after such a long time and it was not addressed from day one of es6 modules introduction in node. Seems such a basic requirement to consider for introducing modules in node. @aduh95 Clever but still a workaround. We should provide a workaround in the meantime but most importantly we should actually fix this issue. We should be able to have an isolated es6 script in a system bin folder work out of the box without a file extension as it works on most systems. You don't type cp.exe or composer.php in a terminal to invoke them. Also package.json should not be a requirement since a package.json is not required to make a node program work. Some long term solutions that I see:
The main value is simple for me: provide a very straightforward way to use es6 modules as first class citizen in the future of js world and keep the scripting standards from python, perl, bash etc. of making executable files that can change language without needing to rewrite all the scripts that call those executables. Also make sure that stdin, stdout, stderr, signals and exit code are handled transparently. It would be such a pity to have to write a bash wrapper for every executable exposed by a packages for the years to come 😢 Some projects have many executable entry points. Of course the best solution should be discussed but I would love to see a focus on fixing the root problem once and for all the years to come. |
@aduh95: Bah. I tried an And I think that you misunderstood me mumbling about an experimental feature. The thing is that as long as it's not publicly available (without an "experimental" name and without a warning), then it's essentially a private api, and therefore should not be used by external code. But it could be part of node somehow (some contrib script maybe), since then using private functionality would be perfectly fine. @aalexgabi, FWIW, I do see the argument for avoiding a second binary, even if it's as mild as some |
@elibarzilay from my understanding some kernels ignore everything after the first argument to executable (/usr/bin/env) in shebangs
Would end up in the kernel exec call as:
That's the reason for which I proposed a different executable and not another argument for node. But I guess it can be a symlink if node can check through which name it's called indeed. More info here https://stackoverflow.com/a/46674720 |
Heh, yes, I know that such problems can happen, though I think that a more obvious breakage (at least on linux) is that it treats the whole rest of the line as the name of the executable and will therefore complain that there is no file named But as long as there is some simple way to do this, then going around such problems falls under the usual kind of problems which are easy to solve. (For example, with the kind of multi-language shebang lines as in what I'm using in the above). |
I quite like the symlink idea, but unfortunately it wouldn't work on Windows I don't think 😕
FYI these have already been discussed, and would need change at the language level, so that's not something that can happen in Node.js (see
There are already working solutions for this today, e.g. if you define |
Yep, sorry, written communication is hard. FYI the goal is to expose the loader API to end users once it's stable. There are discussions on how to make the UX more seemless to not require the use of a CLI flag, and some function signatures may still change at some point, and some features are still missing, which is why it's still experimental. But that workaround snippet is not using anything that's likely to change.
Yep that'd be great, it's a long requested feature: #30810. |
Any news?
It can be used in shebang also. |
This is my current workaround: |
Please fix this. This is a pain when migrating from CJS to ESM with a package that has |
Any resolution for this? |
Since Node doesn't provide a way to cleanly run ESM programs¹, I choose what seemed like the least-bad workaround and renamed the file to use a .js extension. This goes against best practice for program names, but at least this is an internal-level thing where the impact is more limited. We'll still have to go update our internal doc elsewhere to refer to the new name. ¹ nodejs/node#34049
Since Node doesn't provide a way to cleanly run ESM programs¹, I choose what seemed like the least-bad workaround and renamed the file to use a .js extension. This goes against best practice for program names, but at least this is an internal-level thing where the impact is more limited and we can symlink to avoid updating external references. ¹ nodejs/node#34049 Co-authored-by: Thomas Sibley <[email protected]>
Since Node doesn't provide a way to cleanly run ESM programs¹, I choose what seemed like the least-bad workaround and renamed the file to use a .js extension. This goes against best practice for program names, but at least this is an internal-level thing where the impact is more limited and we can symlink to avoid updating external references. ¹ nodejs/node#34049 Co-authored-by: Thomas Sibley <[email protected]>
Since Node doesn't provide a way to cleanly run ESM programs¹, I choose what seemed like the least-bad workaround and renamed the file to use a .js extension. This goes against best practice for program names, but at least this is an internal-level thing where the impact is more limited and we can symlink to avoid updating external references. ¹ nodejs/node#34049 Co-authored-by: Thomas Sibley <[email protected]>
Since Node doesn't provide a way to cleanly run ESM programs¹, I choose what seemed like the least-bad workaround and renamed the file to use a .js extension. This goes against best practice for program names, but at least this is an internal-level thing where the impact is more limited and we can symlink to avoid updating external references. ¹ nodejs/node#34049 Co-authored-by: Thomas Sibley <[email protected]>
Quite a wide wide variety of
Adding Te current status quo of being unable to write ESM scripts that fit in with everything else on the computer is so painful & ugly. A lot of the discssion in this thread is around exposing better loader APIs, & hacking out shims, but I think most users just want to be able to write scripts in ESM & have a Node.js that can run those. This sure seems like a direct & simple approach, from the user perspective. |
busybox env does not (the default variant on Alpine Linux). |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
We are working on solving this feature request via #49432 and #49629. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Resolved by #49974. |
What steps will reproduce the bug?
I'm creating a file called
foo
(without extension) at/app/bin/foo
. I have a/app/package.json
file with"type": "module",
.The
foo
file has a#!/usr/bin/env node
shebang and/app/bin
is in thePATH
.Now I want to run
foo
.What is the expected behavior?
I would expect node to execute the file as ESM because of the
"type": "module",
in the parents folderpackage.json
.What do you see instead?
I get the following error:
I assume node wants to check the file extension to see if its a
.cjs
extension and would require to execute it as common js. However IMHO if there is no file extension it should just respect thepackage.json
in the parent folder and run it as ESM.The text was updated successfully, but these errors were encountered: