npm / rfcs

Public change requests/proposals & ideation
Other
730 stars 240 forks source link

[RRFC] Reintroduce global-only bin command #657

Open molisani opened 2 years ago

molisani commented 2 years ago

Motivation ("The Why")

With npm v9, the npm bin command was removed, which also removed the npm bin -g command. It displayed the path for globally-installed packages, and also checked to make sure that that path was included in the $PATH environment variable. For unix/mac, this is generally not an issue as global packages are installed to the same location as the node/npm binaries.

However, with Windows it's different as the global packages are installed in a completely separate directory from the node installation. Being able to run node does not necessarily mean that the user is also able to run the globally installed packages. Taken from the Windows installer, updating the $PATH variable is split into two different "features".

* Add to PATH
  * Node.js and npm - Add Node.js and npm (if installed) to the PATH environment variable
  * npm modules - Add modules that are installed globally by npm to the PATH environment variable. This option works for the current user only.

With a clean install of node on Windows with these two features selected, it will add two separate locations to the $PATH:

C:\Program Files\nodejs
C:\Users\<username>\AppData\Roaming\npm

This is because of the value of prefix decided by the builtin config, which is set to %APPDATA%\npm by default:

$ npm config ls
; "builtin" config from C:\Program Files\nodejs\node_modules\npm\npmrc

prefix = "C:\\Users\\<username>\\AppData\\Roaming\\npm"

If I then install a package (ex: typescript) with npm i -g typescript, this installs tsc to C:\Users\<username>\AppData\Roaming\npm\tsc which I'm only able to invoke successfully because of the second new entry in my PATH variable. This logic is entirely custom to npm, as it's derived from npm's prefix config value.

This all works seamlessly in the ideal case, but speaking as someone that handles troubleshooting for a large population of developers that are not always primarily doing Node/npm/JS development, incomplete or inaccurate $PATH variables have been an uncommon yet consistent issue. It was useful for us to have npm bin -g as that could prompt the user to add the global package location to their $PATH if it wasn't already set (as it would warn with (not in PATH env variable)).

Given the nature of Windows' two-location installation, this does seem like a Windows-only problem. As such, I'm not sure what the cleanest fix would be. For this use case we do not need any of the "non-global" functionality of npm bin without -g that allowed for the aforementioned bad behavior, so I'm not suggesting that this command be added back as it was. If the functionality to check the global install location (and also if that was in the $PATH variable) could be added to an existing command or even a new command, that would work well for us. I'd be happy to contribute such a feature (whatever it may look like) if that sounds like a good idea to all involved.

Example

This RFC would be for a new command, subcommand, or flag that reproduces the behavior of npm bin -g. Specifically, printing the location of globally installed executable packages and checking whether that location is available in the $PATH environment variable.

Potential suggestions:

How

Current Behaviour

No platform-independent way to calculate location of globally installed packages.

Desired Behaviour

$ npm prefix bin -g
$ npm path
$ npm path bin
C:\Users\<username>\AppData\Roaming\npm
(not in PATH env variable)

References

wraithgar commented 2 years ago

I like the idea of coming at this from solving a specific problem, i.e. "Telling windows users the extra directory they need to add to their path" This keeps us on task and hopefully steers us away from displaying local bin info.

There has also been discussion about supplementing npm exec with a which functionality. I think there is some overlap between these two ideas, and this RFC gives some very good info on what would help folks troubleshoot windows issues.

npm doctor has also long been in need of some iteration, specifically breaking up its functionality into discrete checks, and a check for Is the npm global bin in your PATH? could be a very helpful addition. Putting it under the context of npm doctor seems to make sense from a troubleshooting standpoint.

wraithgar commented 2 years ago

@molisani if npm doctor were to have this check, what would the "Recommendation/Notes" be?

I'm working on a long-wished-for iteration of npm doctor that allows for discrete checks, and adding this as an "environment" check is on the list of things to do.

wraithgar commented 2 years ago

Screen Shot 2022-11-22 at 10 08 13 AM

wraithgar commented 2 years ago

Screen Shot 2022-11-22 at 10 31 20 AM

Screen Shot 2022-11-22 at 10 31 51 AM

molisani commented 2 years ago

Having this live in npm doctor would be great for helping users to diagnose and fix this issue; those examples look great.

However, I was also looking through our code for any other dependencies on npm bin -g and we have some programmatic dependencies on it as well. For situations where we want to execute a globally installed script but don't have access to $PATH (such as execFile or spawn) we used npm bin -g to reconstruct the full path. It's been a while since I've revisited this logic, so it's possible that there's a cleaner alternative, but it would be useful to have a command that output that path otherwise.

wraithgar commented 2 years ago

If you want to execute a globally installed script you can use npx. What's the use case where that is not available?

wraithgar commented 2 years ago

Manually having folks fix their path to include the global bin dir feels like fixing a symptom not a problem. The problem being: why isn't this a part of their node installation? How do we fix THAT problem?

As far as I can tell, either you want to invoke a globally installed bin and let the $PATH pick it up, or you want to be sure it's the one npm installed and then you use npx.

ljharb commented 2 years ago

Isn’t this npm root -g already?

wraithgar commented 2 years ago

No that is the node_modules folder.

molisani commented 2 years ago

Manually having folks fix their path to include the global bin dir feels like fixing a symptom not a problem. The problem being: why isn't this a part of their node installation? How do we fix THAT problem?

I haven't personally seen this in a while, possibly because we introduced the npm bin -g check to our setup instructions, and it's taking care of itself now. My best guess is that this is caused by users coming from much earlier versions of Node they installed years ago, or maybe the $PATH variable was edited incorrectly at some point. To my knowledge, I don't think anyone who has installed a modern version of node has had this issue (I've never seen it after a fresh install). I understand the desire to diagnose a larger issue rather than treat a symptom, but that's for us to do with our user base since it appears to be a solved problem in the installer today. I'm asking for help to distribute the cure here. 😁

molisani commented 2 years ago

As far as I can tell, either you want to invoke a globally installed bin and let the $PATH pick it up, or you want to be sure it's the one npm installed and then you use npx.

As for the programmatic use case I mentioned above, it had been a while since I looked into it (I searched everything we had for npm bin -g). I think the issue with not having $PATH should be solvable through other means.