-
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
Consider something clever for extension-less executables #32316
Comments
Duplicate of #23868 |
We can't pull data out of the hashbang (see the issue I linked), so I'm not sure as this issue is phrased there's anything actionable. In general, extensionless mjs is a place where improvement could be made though. |
This use case was explicitly enabled in early implementations, but disabled by the PRs by @bmeck and @GeoffreyBooth in #31021 and #31415. I disagreed with both of these PRs, but let them pass as I did not want to block new work unnecessarily. I still think the original approach with both of those PRs reverted would be better (31415 followed by 31021), but will not work on it personally. If anyone wants to put something together along those lines though it would have my full support and argument in the group, although many disagree with the idea that the process main can be used as resolution information. Personally I have no issue with that since it is environment-static, and any environment-static state is fine to assume in the resolver (the definition of what is allowed!). |
I've stated in the past my concerns with being clever about these things. Per #31021 , it ended up supporting files without extensions and delegated them to the For context, per #31415 , that was done because of #31388 which was made to support loading WASM entry points for WASI being complicated by extension-less mains. Reverting out any extension-less support was seen as preferred for now as some people didn't want to expand the @haraldrudell an alternative is to use symlinks that point to a file with an extension or have your |
Bin users writing bin files ending in |
@guybedford I don't understand the last comment, the concern is about |
@haraldrudell If you’re looking for something clever, this gist by @WebReflection certainly fits that bill and might fulfill your need: |
@GeoffreyBooth thanks for pointing at my good old gist! It looks like time is about right to improve that gist and place a temporary |
Hint: edit: |
@bmeck the previous code only affected files as the main entry point. So |
This must be done and it is actionable require is over; it should not be used anymore note: why I bring it up now, is that I came up with a toolchain where the source, config files, packages and output executable all use import rollup/rollup#3443 I know about that hack suggested in comment. I don't do hacks the question is what's the best way and who are we breaking. To me personally, I will just retranspile any failing executable using my fancy toolchain: I am good note that a shebang other than #!/usr/bin/env node is not portable. In particular other shebang, if done for Linux, will not work on macOS and Android erase the past |
@haraldrudell I have no idea what most of the last comment is trying to say except that you want to break things, but not how nor any response to the other comments in the thread except that gist. Is there are reason none of the alternatives work clearly / what is the clear use case that we are not fulfilling besides a specific solution to the use case? |
The use case is single-file deployment of extension-less ECMAScript module executables to bin directories on unix-like operating systems Linux macOS Android |
@haraldrudell I'm not convinced that is common in real world deployments given that even things installed via |
I do not know everything What would someone expect Node.js to do? execute ECMAScript Your concern seems to be that this expected behavior will make wasi wasm implementations more complicated. Apparently, pull requests referenced above were written based on a 2018 invariant. Code rot is a bitch |
@haraldrudell CommonJS is not being removed from Node. |
@haraldrudell This was discussed here: nodejs/modules#318 |
CommonJS, though still supported, is going the way of the dinosaurs ECMAScript modules for shebang and maybe other aspects is a question of when, and I think when is finally here. The discussions referenced are a year old, when that when was not yet here Looking at the wasi module documentation, there is use strict, require, self-executing function expressions, readFileSync and async functions without catch. That's a bizarre history lesson from ES5 of 2009, from as old as is Node.js. There is not going to be any sympathy from there esm for shebang will only break code that is not in or symlinked from a package because of the ingenious type field in package.json. Maybe it does not break anyone I have heard of the Node.js Technical Steering Committee from all the scandals. Is this not their decision purview? |
@haraldrudell Modules WG meetings are quite often, and notes are kept of them as well as recordings. I urge you to keep discussion on topic and civil. I was the champion for https://github.com/tc39/proposal-hashbang and am well acquainted with these topics. I can guarantee that all individuals involved in this project are working towards a better tomorrow. I am not sure what scandals you are referring to, but the TSC does get involved and has weighed in on several issues in the past; however, in the governance structure of the project and within their corresponding topics, working groups tend to be the leadership point for decisions. |
@haraldrudell If you read nodejs/modules#318 and the resulting proposal, that led to this: nodejs/modules#335 (comment). And the result was that existing options handled all of the discussed use cases except the one you mention, of a desire to have a “loose” (outside of any package/package scope) JavaScript file with no extension and with ESM syntax. That’s still a gap, because our first priority is to avoid breaking backward compatibility unnecessarily. While CommonJS may be considered legacy by some, there are millions of CommonJS packages out there and it will be many years before it becomes commonplace for folks to build Node apps with no CommonJS dependencies. Therefore we can’t simply deprecate CommonJS or make ESM the default in place of CommonJS, at least not for several years. So then the question becomes, how to fulfill your use case within these constraints? Currently, the best option is the technique in the gist. It has environment restrictions, though any shebang-based approach will inevitably have some environment concerns. Another option is to put a One thing we’ve considered off and on for years is a new binary, e.g. |
This decision should be based on accurate facts We're not breaking millions of CommonJS packages and such dependencies will still work The question is ECMAScript from the file system that has no extension, is not symlinked to or resides inside of a package.json hierarchy. Such files are unlikely to come from any registry or installed package. One thing @bmeck got right is that this is probably rare shebang ECMAScript modules is apparently the way things were between 4/23/2019 and 12/18/2019. Are there any known complaints from these 8 months of Node.js versions? from nodejs/modules#318 and nodejs/modules#335 (comment) require can still be used: node --input-type=commonjs --print "require('tty')". Add input-type or NODE_OPTIONS: options and environment variables are already there The default should be ESM. This is still a gap. It used to work until somebody broke it |
What do you think about having |
@WebReflection the problem is with the hashbang not being a way to pass CLI flags reliably and the desire not to use something like the |
@bmeck the |
@WebReflection I'm not stating that the feature shouldn't be discussed, but I think that would be a different issue thread than this one. |
It’s not clear to me what the ask is here. Is it specifically that shebangs can tell Node to execute a file as ESM, or that a loose extensionless file can be run as ESM? |
@GeoffreyBooth I believe per #32316 (comment) it is that a hashbang containing file can execute as ESM without any other context involved; no symlinks, no package.json, etc. |
Okay. That’s already possible via the solution in the gist: #!/usr/bin/env sh
J=S//;echo "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
import { version } from 'process';
console.log(`Running Node ${version} in ESM mode!`); Is there something about that solution that you find wanting? |
As author of that gist, I'd like to underline if the |
I do believe the user experience is sub optimal to have to use a non-portable / readable hashbang. I think a compromise might also be having the behavior of the default package be configurable at build time? Then people could build their own form of |
it's a workaround, hence not optimal indeed, but AFAIK there's no env incapable of running it, except Windows PowerShell, but server-side is rarely done on Windows, and most devs are on Linux, macOS, or WSL/Git/Ming shell, even Docker, where it works already, as long as
|
This was directed at @haraldrudell. Based on the initial comment:
It would seem that the gist satisfies both requirements, and it's also clever, so we get bonus points for satisfying the requirement of the thread title. So my question is, can we consider this issue closed or is there something about the gist's solution that is lacking?
I think what you mean here is that if you had an extensionless JavaScript file with the gist solution, if that file contained |
My |
Excuse me for budding in as a newbie to node dev but I think my different perspective as an experienced linux systems developer might be useful. You are at a junction where you can easily choose what you want the default standard used by extensionless bin commands to be. It would be crazy if you use that freedom to choose it to be the legacy cjs standard that is being superseded by esm. Shebang should do what is intuitive and inline with the future unless the burden is too great in breaking legacy stuff which it seems from this conversation that it clearly is not. JS is a general language now. node does not have to be relegated to just for server side web applications and their supporting scripts. I got here through Atom customization and now am considering electron apps for all sorts of utilities with a UI and writing more cli commands in JS instead of bash, python, or php. (I might even use it for server side web apps too:) I would include extensionless bin JS cli and GUI commands in my deb and rpm packages, but I am not going to prefer JS if it forces me to use an older standard in stand alone commands and not if If that ugly and cryptic shebang hack is the official way for me to do what I would expect to be the default! BTW, in linux we would distribute one 'node' executable that has a symlink called 'node-cjs'. The node executable would init the default mode to esm if its invoked as 'node' and cjs if its invoked as 'node-cjs'. Lots of programs do that. I am not sure if you can do that in Win and Mac but as already discussed, you could always build two executables. --BobG |
We can’t actually choose to change this without breaking countless shebang scripts that have been using |
I still think the |
Here’s how to create your own
Now you can save the following as a file like #!/usr/bin/env node-esm
import { version } from 'process';
console.log(version); And once it’s executable, it should work as you want: $ chmod +x ./my-script
$ ./my-script
v13.12.0 Credit to @jkrems for the first part 😄 |
i'd recommend doing |
I feel like there's now multiple solutions that don't require changes to node itself. I'd consider this working as intended for the time being. It only affects small scripts that are placed directly in the Should we close this issue? |
(edit) woops, did not see the other posts before I sent the msg below. I would just say, dont leave it to everyone to make their own node-esm and node-cjs in the long term -- ship them alongside node and document creating standalone shebang scripts.
The statement that the esm default was in place without complaint for months left me with the opposite impression but maybe testing with this version has not been widespread? In any case it does seem easy to support both. Instead of producing a whole separate executable you could ship two little scripts alongside the node executable.
In the meantime, people can create those scripts in an npm package and install them globally (I think either 'bin' property or installing with -g would work) You could encourage script command authors to be explicit in using node-esm or node-cjs and not rely on the default behavior of 'node' (which may change eventually) --BobG |
I just went to write a more robust node-esm script command. That script will be hard to get 100% correct and to maintain if you dont fix the --input-type option. node-esm should support the same syntax as node.
The problem is that you can not reliably identify the 'script.js' parameter in that syntax unless the script knows which node and V8 options expect parameters. The script would need to do similar command line processing as node itself. From the outside, it seems logical that --input-type should affect stdin content, -e string content and the contents of filename specified on the command line. They are all ways to provide the entry point script that node's going to run -- i.e. they all are ways to provide the 'input' that --input-type refers to. Its just confusing otherwise. That option should not affect imports inside the code or anything else -- just the input to node. If you make that change, any scripts people need to write to add options (not just the case) will be trivial, robust and not need maintenance.
--BobG |
The various shell script solutions listed on this thread are hard to get right and to maintain. Node supports a lot of environments, and I wouldn't want to take on the challenge of ensuring that this is compatible with all of them; and even if we explicitly said that this is only supported for some subset (and people noticed that and didn't report it as a bug for explicitly unsupported platforms), it would be hard to catch all the edge cases like additional arguments or The short answer is that this doesn't feel like a high priority use case for Node to support directly, and to implement this robustly and support it would take tremendous effort; probably more effort than is reasonable for the use case/subset of users wishing for this feature. Whereas the various solutions above don't feel terribly burdensome; users who want this functionality can implement whichever of the above solutions works best for their environment and/or scripts.
This was discussed extensively in the design process. See nodejs/modules#300. Basically, there are downsides to every alternative and I think I'm going to close this issue as answered, if others feel otherwise please reopen. |
@GeoffreyBooth, you've got to be kidding us? I principally refuse to believe that running from a file with any extension is such a difficult problem. Nobody is asking to support loading ALL modules in a program from files with arbitrary extensions - the loader would have to be able to read minds for that. But those issues are irrelevant when I GIVE node the unambiguously identified file and ask it to FOR HEAVEN'S SAKE JUST OBEY MY EXPLICIT IMPERIAL DECREE THAT THIS PARTICULAR ONE IS AN ES6 MODULE. |
@rulatir this places a #!/usr/bin/env sh
J="$(dirname $0)/package.json"//;if [ ! -f "${J:0:-2}" ];then echo '{"type":"module"}'>"${J:0:-2}";fi;echo -e "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
import { version } from 'process';
console.log(`Running Node ${version} in ESM mode!`); |
I was surprised to find out this is an issue. How the hell are expected to migrate to es modules if there is no easy upgrade path? |
The above command is working for me. You can see an explanation of the syntax at As mentioned in the post, I recommend adding a link to the blog post within the comment.
|
Chiming in with my DevOps hat on. Extensionless scripts should "just work" without wrappers and hacks. How about an env var?
|
Responsibility shouldn't be on the user, but on the author of a script. |
It would be nice if ECMAScript shebang files with no extension
a. worked :)
b. were assumed to be modules for Node.js current+
Especially for server people that have lots of command-line scripts
And I think we should plan for a future without require
exhibit:
This shebang no work either:
#!/usr/local/bin/node --experimental-modules
The solutions at present is to:
node -v && uname -a
v13.11.0
Darwin c87m1.local 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan 9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64
A related trouble is if you happen to have a native-code dependency. requires node_modules and such
The text was updated successfully, but these errors were encountered: