89

I have a few "global" dependencies (jshint, csslint, buster, etc..) that I'd like to have automatically installed and executable via the command line when my package is installed via npm install. Is this possible?

Currently, I'm doing the following manually:

  1. npm install -g <package_name>
  2. from within my project: npm link <package_name>

Update: Just came across this feature request for npm. It seems like the scripts config within package.json is the way to go?

Update Again: Or, after reading the npm docs, maybe I'm supposed to use a .gyp file? I'm confused.

gxc
  • 4,946
  • 6
  • 37
  • 55

3 Answers3

61

It's not possible to specify dependencies as "global" from a package.json. And, this is by design as Isaac states in that feature request you referenced:

Yeah, we're never going to do this.

But, "binaries" can still be used when a package is installed locally. They'll be in .../node_modules/.bin/. And, you should be able to queue them up with a preinstall script.

Though, if the series of commands is rather lengthy (as "jshint, csslint, buster, etc.." would suggest), you may want to look into using a build tool such as grunt to perform the various tasks:

{
    // ...,

    "scripts": {
        "preinstall": "grunt"
    }
}
Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
  • Thanks @Jonathan. I think you're right. I currently have a grunt task called `install` that handles building modernizr, jquery, etc.. and copying random files over to my lib folder. This means developers will need to install global packages themselves but oh well, that shouldn't be the concern of my app anyway. – gxc Feb 02 '13 at 02:24
  • 1
    Your app should document its dependencies and, in my opinion, ensure that they exist in some way. – EndangeredMassa Feb 03 '13 at 03:51
  • 3
    https://www.npmjs.org/doc/misc/npm-scripts.html says that usage of install scripts is an anti-pattern. – Fdr Jun 09 '14 at 09:54
  • 4
    you can add npm install -g to the preinstall scripts if you need. – Eduardo Russo Apr 10 '16 at 13:56
35

I really like the pattern where you install local dependencies, then use a bash script that sets your PATH to ./node_modules/.bin.

File: env.sh

# Add your local node_modules bin to the path for this command
export PATH="./node_modules/.bin:$PATH"

# execute the rest of the command
exec "$@"

Then, you can use this script before any bash command. If you pair that with a Makefile or npm script:

File: Makefile

lint :
    ./env.sh csslint my_styles

File: package.json

"scripts": {
  "lint": "./env.sh csslint my_styles"
}

This tasks in these files look like they reference csslint in some global location, but they actually use the version in your node_modules bin.

The really awesome benefit of this is that these dependencies can be versioned easily, just like your other node modules. If you stick with a global install solution, you could be clobbering some specific version on the user's system that is required for one of their other projects.

EndangeredMassa
  • 17,208
  • 8
  • 55
  • 79
  • 1
    Can you expand upon why you're using a script to set the path every time you want to call a `./node_modules/.bin/` command? In your example, every time you run `lint`, you will prepend `./node_modules/.bin` to your path, even if it is already there. Why not just set the path once in your profile? If the command isn't there, the shell will continue looking in the rest of `$PATH`. – nshew13 Mar 28 '14 at 16:24
  • 1
    I want this to be usable by anyone. I could set this in my profile and it would work for me, but then everyone else who wants to work on this module will have to do the same. Note that the `./env.sh`isn't required in package.json `scripts` anymore. – EndangeredMassa Mar 29 '14 at 06:36
  • 2
    I still don't understand why you're setting the `PATH`. If all `./node_modules/.bin/` commands are routed through `env.sh`, why futz with the `PATH` at all? Isn't `exec ./node_modules/.bin/$@` sufficient? – nshew13 Apr 01 '14 at 14:15
  • @N13, that's not the same, considering you could have some binaries local, some global. So while your approach would suffice if *all* bins were local, the PATH approach allows you to find binaries local-first. That being said, I do agree that using an "env.sh" script seems needless, just add the PATH export to bash profile/rc script. – Jon L. Oct 14 '14 at 21:30
  • 1
    This is incredibly helpful for dotfiles repos where you can easily define all the global modules you expect on your system and commit it. Thanks for sharing the idea! – Matt Smith Sep 11 '15 at 03:12
8

You should try this: https://github.com/lastboy/package-script

I've been using it to install global npm packages straight from the package.json. It works well for clients who aren't technically literate.

It even checks if the packages are already installed, if not install them!

CMCDragonkai
  • 6,222
  • 12
  • 56
  • 98