npm And Global Packages

Using a Node module that provides an CLI presents some challenges, especially to newcomers. Below is a discussion of some approaches and some pros/cons.

Global Only

Just install globally and don't worry about local installs. It will be easy to run the commands (no prefixes!), but you'll have to manually keep everybody on the team in sync with which packages and versions. This has caused problems in the greater Node ecosystem in the past.

[UPDATE : DailyJS is down, but the content is still available on the Wayback Machine. Thank you Daniel Ellard for pointing out the dead link.]

Local And Global Hybrid

Install the packages locally (with --save) and globally. Some CLI packages used to promote this pattern as the global command would run the local "executable" if it was present. I don't like this option because it requires global package installs (same issues as above just maybe less so) and packages have to be built specifically to check for this. Another issue is just how "impure" it feels to have to install the same package twice just for a single app.

Local Only

Install locally (with --save) but not globally. Now the executable will be available as ./node_modules/.bin/typings. This is a little cumbersome to use, but really not a problem if you don't need to run the command too often. The benefit is now you can properly document which packages you depend on without any global caveats. Also your team can get the same packages with a simple npm install. (Use a tool like npm shrinkwrap if you are going to be pedantic about version numbers.)

Local Plus npm run

Install locally (with --save) and add an entry in the scripts section of package.json ("scripts": { "tsc": "tsc" }). You still have to prefix your commands, but now it is just npm run tsc, which is a bit easier to type and shorter besides. Additional arguments can be passed in after --, as in npm run tsc -- --help.

Not every CLI package needs to be accessible with npm run. Consider a project that uses Sass and TypeScript with typings. I might add build:styles and build:scripts entries for stylesheets and typescript separately. I would certainly add a build script that packaged up both styles and scripts. However I probably wouldn't bother adding an entry for typings; I simply wouldn't run that command often enough to be worth it.


I have been quite happy with simply installing packages locally and not worrying about global installs. Even without npm run, I would prefer local installs. After all nothing stops me from adding short helper scripts to ./scripts, and in that case it doesn't really matter that the local ./node_modules/.bin isn't in my $PATH.

Extra credit:

See https://docs.npmjs.com/misc/scripts
See https://docs.npmjs.com/cli/run-script
See https://github.com/npm-scripts/scripts