If you aren’t sure what npm packages are it’s probably worth reading up on them before continuing with this post, https://docs.npmjs.com.
There are two different ways to install them, locally and globally.
Local packages are all of the packages that a project depends on and are defined
in the package.json
file.
Global packages are all of the packages that are installed at the global scope
and are installed with the -g
(or --global
) flag.
Before I get to the problem, I want to identify the different types of global packages. Most globally installed packages are CLIs and there are two different types:
Non-project dependencies - These are non-project dependencies, some examples of these are npm-check-updates and create-react-app.
Project dependencies - These are packages that a project a project depends on, some common ones here are bower, gulp, grunt-cli.
Non-project dependencies don’t have one, so carry on using them exactly the same!
Project dependencies on the other hand do, which are:
They are an extra install step, as you have to run npm install -g PACKAGE
as well as npm install
.
They aren’t listed as a dependency in your package.json
.
The first issue is a minor one, as you need to keep your documentation up to date with what global packages need to be installed and you create an (unnecessary) install step.
The second issue is a real problem, it easily becomes a problem because the
version is not specified, so when you run npm install -g PACKAGE
it will
install the very latest version of the package, which might be one or more major
/ (minor) semver versions from the version that the
project was initially built with. This could cause lots of issues as the package
might have drastically (or minorly) changed its API, which means things might
break!
It’s quite difficult to find the original version that was used, as it’s not tracked anywhere.
We can fix this by treating it similarly to other dependencies, first by
installing it as a dependency npm install --save(-dev) PACKAGE
, when
installing npm packages locally anything that is designed to be run on the
command line will be installed in ./node_modules/.bin
.
This now means that when someone runs npm install
it will install everything
that is required so that extra install step is no longer required!
For example, if you install gulp with npm install --save-dev gulp
you can run
it by typing ./node_modules/.bin/gulp
in your console.
Problem solved!
Well not quite, it’s really annoying to have to type that every time you want to run gulp. Luckily there is a solution for that too!
When running npm scripts, npm will first look in the ./node_modules/.bin
to
see if there is an executable in there before checking your PATH
.
This means if you add something like this to your package.json
.
{
...
"scripts": {
"gulp": "gulp"
}
}
You can now run gulp with npm run gulp
. If you need to pass additional
parameters you can do so with --
so gulp build
becomes npm run gulp -- build
.
Hopefully having gone through this you see the benefits of moving global package dependencies into your control and how this is achieved.
One thing I see quite often with a front end project is an additional build step
for bower, beyond the global install, you have to then additionally do a bower install
.
This can be improved by using the above solution and a postinstall
script (see
the docs).
npm install --save-dev bower
.{
...
"scripts": {
"postinstall": "bower install"
}
}
This means that running npm install
installs all of the project dependencies!