It is widespread to go on a quick start page of some tool and utility and find instructions similar to this one:
npm install - global cool-package
cool start
This command is likely to work, but the --global
should make your eyebrow rise. It is barely needed or recommended to install packages globally.
Well, Why Not?
There are a few reasons for you to avoid installing packages globally:
But it works for me…
Who of us, developers, have never heard this? Your colleague is trying to run something you just told them to, and… fails. But it works for me, you tell them.
Global packages can be it. If you have some package they do not have, or if there is a version mismatch between your machines, that might be a problem here.
CI is failing
That problem is similar to the machine mismatch described above. You are setting a CI that is trying to run a command, but it cannot find it. The reason is that the command was part of a package installed locally on your machine.
That version does not work
One of the worst problems when relying on global packages is version compatibility. If you are working on multiple projects, you are likely to have different versions of packages. Version 3 for project A and version 4 for project B. Sadly, only a single version can be installed globally. You need to work this out.
Oh, a new node version!
Are you using nvm to manage your node versions? (and if you do not - have a look at it. It is awesome!). If you switch to a new version of a node, even a minor change, your global packages are gone. If you are a very careful maintainer, you will be using the option --reinstall-packages-from=default
. But if you are like me, you will find sometime later that a specific command does not work, and realize you did not migrate your packages.
Minimizing the number of packages can surely save some precious time.
OK, I am convinced. I will try to avoid using global packages. What is the recommended way to work?
Keep Them Local
In most cases, you should keep your packages local to your projects. Save all the packages needed for a project locally with the compatible version or versions range.
npm install --save cool-package
or
yarn add cool-package
But this raises an issue: when you install a package globally, you can easily run it by typing its executable name:
cool start
If you try to do that in a local package, you will get an error that the command was not found. NPM installs the executable under the node_modules/.bin
folder. When running a command, the shell does not search on this path. The NPM global path, on the other hand, is added to the shell path (run echo $PATH to view it).
There are a few ways to solve this:
Run the command via npm script.
Define the command in the npm script:
{
"name": "my-package",
"scripts": {
"build": "cool build",
"test": "cool test"
}
}
Now you can run the command by running:
npm run build
Pro Tip: If your command requires config arguments that start with double dashes, you need to specify it twice when running via npm script. So if you want to run cool build --watch
You need to run: npm run build -- --watch
(with two sets of dashes). Otherwise, the command will not be recognized.
Run with npm bin
However, there are scripts that you want to run only occasionally, and it does not make sense to create a script for each one of them. In this case, you can run it directly by specifying:
node_modules/.bin/cool rare-command.
A shorter and friendlier way to do it is to use the npm and yarn bin command that returns the path to the executable path, and you can run:
$(npm bin)/cool rare-command
Have a command that you use often, but you do not want to put it in a script? set an alias for it, such as:
alias cl=$(npm bin)/cool
This command runs the cool script that is local to the project you run it.
Use NPX for Local Scripts
Starting NPM 5.2, NPM has a new package called NPX. NPX is extremely powerful, and too often, its powers are overlooked.
Use NPX to run local scripts: By merely typing npx cool
inside a folder where cool-package is installed, NPX finds the local installation and run the script. If you need to pass any arguments, just send them, without any changes.
Use NPX for uninstalled Packages
When starting a new project using a CLI, such as Angular CLI, React Create App, or Vue CLI, the project does not yet exist. Therefore, you cannot install the generator package inside the project.
NPX is a lifesaver here, as you can run:
npx create-react-app my-app
NPX downloads the package to a temp folder and executes the command from there.
In the create-react-app
, the name of the script is the same as the name of the package. If the package name is different from the command, you can specify the package name to be installed.
npx -package @angular/cli ng new my-app
Use Yarn
When installing Yarn, the bin command is automatically mapped to the yarn command, so you can run it as:
yarn cool
(Credit to @bnaya for this excellent comment!)
When To Use A Global Package?
After that said, there are cases where global packages are acceptable. The rule of thumb should be to use it when:
- You run the commands mostly outside the context of specific projects.
- You run the command relatively often, and you do not wait for NPX cache every time.
- You are not extremely sensitive to the package's version, or versions do not change often.
For me, an example of such a package is http-server
. I need it sometimes when I want to run a simple, well, HTTP server from a local folder for development and testing options. In this case, I would install the package globally.
Ah, and do not forget to update the version occasionally. Use npm outdated -g
to see what packages need an update.
Conclusion
Run npm ls -g --depth=0
to see your currently installed global packages. Review them carefully, and always be conscious about the packages you install globally.
More often than not, you can avoid global packages and save yourself some precious debugging time.
Top comments (6)
Oh no... what did I do all these time...
Nice article, just a small typo:
npx -package @angular/cli ng new my-app
should benpx --package @angular/cli ng new my-app
ornpx -p ...
for shortVery true, ever since it being able to reference the binary, I've been going through and cleaning these up in all the projects I come across. 😄
Also, nice to see a fellow DX'er!
Say goodbay to ‘npm install’, no need ‘npm install’, throw away ‘npm install’.
You are absolutely right. This was the original intent, but lost in editing (I copied the article from a draft elsewhere). Thanks for the comment.