nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
106.58k stars 29.06k forks source link

Cannot run npm without node being in PATH #54183

Open jcbhmr opened 1 month ago

jcbhmr commented 1 month ago

Version

v22.5.1

Platform

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.
$ 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?

$ 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
  4. ~/.local/bin/node-18-npm symlinks to ~/node-18/bin/npm
  5. ~/.local/bin/node-22 symlinks to ~/node-22/bin/node
  6. ~/.local/bin/node-22-npm symlinks to ~/node-22/bin/npm

And they wouldn't accidentally start using each other's node binaries!

RedYetiDev commented 1 month ago

@nodejs/npm