Repository: node
Issue: Cannot run npm without node being in PATH
Labels: npm

### Version

v22.5.1

### Platform

```text
Linux PIG2016 5.15.153.1-microsoft-standard-WSL2 #1 SMP Fri Mar 29 23:14:13 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
```


### Subsystem

npm

### What steps will reproduce the bug?

1. Download the latest Node.js tarball from the website `wget https://nodejs.org/dist/v22.5.1/node-v22.5.1-linux-x64.tar.xz`
2. Extract the Node.js tarball to your testing folder `tar xf node-v22.5.1-linux-x64.tar.xz -C ~/testing123`
3. Make sure you REMOVE any previous Node.js installation from your PATH `export PATH=$(echo "$PATH" | tr ":" "\n" | grep -v node | tr "\n" ":")` replace `grep -v node` with `grep -v /your/node/bin`
4. Confirm that `command -v node` returns nothing
5. Make sure `~/testing123/bin/node --version` works
6. Try to run `~/testing/bin/npm --version`. It fails.

```sh
$ command -V node
bash: command: node: not found

$ ~/testing123/bin/npm --version
/usr/bin/env: ‘node’: No such file or directory
```

### How often does it reproduce? Is there a required condition?

Must NOT have a (compatible) Node.js binary available on PATH.

This problem does not happen on Windows.

### What is the expected behavior? Why is that the expected behavior?

It should show me the version of npm, same as if I directly invoke `~/testing123/bin/node ~/testing123/lib/node_modules/npm/bin/npm-cli.js --version`

### What do you see instead?

```sh
$ command -V node
bash: command: node: not found

$ ~/testing123/bin/npm --version
/usr/bin/env: ‘node’: No such file or directory
```

### Additional information

I think this is because:

1. `~/testing123/bin/npm` is a symlink to `../lib/node_modules/npm/bin/npm-cli.js` which is `~/testing123/lib/node_modules/npm/bin/npm-cli.js`
2. ...so `~/testing123/lib/node_modules/npm/bin/npm-cli.js` is _directly executed_ when you run `~/testing123/bin/npm`
3. `~/testing123/lib/node_modules/npm/bin/npm-cli.js` uses `#!/usr/bin/env node`
4. It performs a PATH lookup to find `node`
5. It fails.

This problem does not happen on Windows because the `npm.cmd` wrapper is NOT just a simple symlink on Windows; instead it directly executes `${the_node_exe} ${the_cwd}/node_modules/npm/bin/npm-cli.js`.

I think a good solution for this would be to change `./bin/npm` in the release tarball for Linux (and macOS too, I assume?) to be a POSIX shell script similar to `npm.cmd` in the Windows release. It's already a (symlinked) shebang script `#!/usr/bin/env node` so a change to `#!/bin/sh` and `exec ${the_cwd}/node ${the_cwd}/../lib/node_modules/npm/bin/npm-cli.js` wouldn't be that much of a change.

OR to somehow get the npm-cli.js to check for a ../../../../bin/node executable and use that before performing a PATH lookup

Another scenario where this could cause an issue (conceivably; I'm just fishing at this point lol)

1. User is using ancient Ubuntu with Node.js v12
2. Download the Node.js v22 tarball to see if some project works with upgrading to Node.js v22 & npm v10 (don't add it to PATH)
3. Runs `./test-node22/bin/npm install` and it fails because npm's JavaScript uses features that Node.js v12 doesn't have (because it ran using `#!/usr/bin/env node`, not the local `./test-node22/bin/node`)

It'd be pretty trippy if `npm` in your local test Node.js v22 used an older v12 `node` version from your `PATH` to run itself or even run dependencies like `prettier` 😵

Admittedly all of these issues could be solved by saying "well just add it to your path and be done with it!". And that's true -- `export PATH="$HOME/testing123/bin:$PATH" && ~/testing123/bin/npm` would solve the problem.

However I contend that since this was solved on Windows it should be within the scope of Node.js to also solve the same problem on Linux (and macOS?) systems.

This would also make it easier to do something like:
1. Node.js v18 installed to ~/node-18; not in PATH
2. Node.js v22 installed to ~/node-22; not in PATH
3. `~/.local/bin/node-18` symlinks to `~/node-18/bin/node`
5. `~/.local/bin/node-18-npm` symlinks to `~/node-18/bin/npm`
6. `~/.local/bin/node-22` symlinks to `~/node-22/bin/node`
7. `~/.local/bin/node-22-npm` symlinks to `~/node-22/bin/npm`

And they wouldn't accidentally start using each other's `node` binaries!