There are a lot of tools in the npm ecosystem that you need to run in your project. For example, firebase-tools or heroku.

If you read the documentation, those tools require you to install them globally:

npm install -g firebase-tools

Now you can execute the binary from everywhere, for example: firebase init.

But it’s recommended to install packages locally instead of globally 1.

Updating a global package would make all your projects use the new release, and as you can imagine this might cause nightmares in terms of maintenance, as some packages might break compatibility with further dependencies, and so on.

All projects have their own local version of a package, even if this might appear like a waste of resources, it’s minimal compared to the possible negative consequences.

But how can I run a command-line program now with locally installed npm packages?

How to Run Local NPM Binaries

First, install the required tool locally, either as a normal dependency or as a dev dependency.
Inside your project folder:

npm install --save-dev firebase-tools

Where can we find the package? Try npm bin. It will find the closest executables.

Alternatively, you can use qnm to query your node modules for the package:

npx qnm firebase-tools

Now, you can use npm bin to run the packages locally. In Bash/Zsh shell:

$(npm bin)/<binary-name> [args]

e.g.

$(npm bin)/firebase-tools init

You can create a Bash function, too:

function npm-bin { (PATH=$(npm bin):$PATH; eval $@;) }

Fish shell doesn’t allow command substitution, so you’ll have to use a workaround:

# First set a variable
set NPMBIN (npm bin)

# Then you can use it like so:
command $NPMBIN/firebase init

What about npx?

The current version of npm ships with npx, a command runner that either runs from the local node_module or from a central cache.

By default, npx will check whether <command> exists in $PATH, or in the local project binaries, and execute that. If <command> is not found, it will be installed prior to execution.

For programs like firebase-tools, npx will always default to installation. Now every time you run npx firebase-tools firebase init, npx will first download the required package although it’s already installed locally.

tl;dr

Instead of cluttering your global installation with tools you run as one-off commands, use npm bin to execute binaries from the local npm packages.

Further Reading


  1. via Flavio Copes ↩︎