Keeping it local with npm

In transitioning to the Node ecosystem, I quickly recognized many strengths npm brings to the table as a package manager. As someone who has used rvm/bundler to routinely manage gemsets, an immediate win I noticed was how every install of a project is localized within the project by default.

This is not only true about code level library dependencies, but also command line tooling that the project may rely on for automation. These tools are linked into ./node_modules/.bin/*

For example, after an npm install of a project that declares gulp (a popular task runner) as a devDependency, you would get your task runner installed at ./node_modules/.bin/gulp. This means you don't actually have to install gulp globally as long as you're ok with running gulp with

./node_modules/.bin/gulp <task>

Because this is a bit verbose, most projects also recommend you install a global gulp runner that enables you to shorten this up with a familiar

gulp <task>

But are these global installs necessary to reap the more succinct benifit?

This is where npm's script feature comes in.

npm scripts

Another feature of npm is the ability to define both lifecycle tasks for your package and custom package scripts, giving it some limited ability as a task runner itself.

In your package.json you can define these scripts like so.

{
  "name": "my_package",
  "version": "0.0.1",
  "description": "My Awesome Project.",
  "scripts": {
    "<task>": "gulp <task>"
  }
}

Then you can

npm run <task>

Which achieves our succinct interface while divorcing the global install requirement.

This is possible because npm run <...> runs with ./node_modules/.bin included in its path.

consitency

Another benifit of this approach is having a consistent task interface across multiple projects that might have differing task running needs. One project might particularly benifit from a stream based task runner like gulp, while another might not need an additional task runner at all.

Both projects in this case would use the same npm scripts interface and would be immediately familiar for team members that might not be familiar with one or both projects.

Jonathan D. Johnson

I love my wife, my sons, and my work.
Senior Software Engineer @ Codeship.
Ruby/JavaScript. I like to be outside.