Executables in npm scripts

Let's say your project uses a build tool called rutabaga, but you don't want contributors to have to globally install it to build the project. If you're just starting to use npm scripts to help simplify this, you might have gone through a process like this:

npm install rutabaga --save-dev.

package.json:

// Don't actually do this
scripts: {  
  start: './node_modules/rutabaga/bin/rutabaga.js build'
}

That seems great, but has a few issues:

  • it will break if the authors of rutabaga decide to change the structure of the package;
  • it will break on systems that use a path seperator other than / (Windows)

It turns out that there's an easier way to do this. Even if you don't have rutabaga installed globally, this will still work:

scripts: {  
  start: 'rutabaga build'
}

But why?

If you depend on modules that define executable scripts, like test suites, then those executables will be added to the PATH for executing the [npm] scripts

- https://docs.npmjs.com/misc/scripts

What that means

If you aren't used to working in a UNIX-based environment, PATH is basically a special environment variable that indicates where to find executables (i.e. programs you can execute) in response to a given command.

In the case of rutabaga and other such tools, executables can be defined in package.json like so:

"bin": {
    "rutabaga": "./bin/rutabaga.js"
}

Npm will look for those files and copy/symlink them into a special directory (node_modules/.bin/). In our example, the file rutabaga/bin/rutabaga.js will be copied/symlinked to npm_modules/.bin/rutabaga. That directory is added to the PATH, and we are now able to call rutabaga in our npm scripts. Hurray!