IT IS HERE! Get Smashing Node.JS on Amazon Kindle today!
Show Posts
← Back to homepage

I decided to put together a little list of tips and tricks related to NPM you might not know about, that come from my experience working with it daily, in both production and development.

Dev dependencies

Make sure packages that only need to be installed while developing your module, like test frameworks, are included as devDependencies:

"devDependencies": {
   "module": "0.1.0"
}

Introspecting package.json

Starting with Node 0.6, since require() can read and parse JSON files automatically, you can leverage it to easily introspect package.json:

// considering the module lives in lib/module.js:
exports.version = require('../package').version;

If you want to access package.json for other modules, you can do it like this:

require('my-module/package').name

Linking

Sometimes you’re developing two or more modules at once, and at least one of them depends on the other. And often times, in order to be able to publish the module to NPM, you need to ensure the modules that depend on it work OK prior to publishing.

You can leverage npm link to generate a global reference to a module, and then run npm link <package>
to install it in other modules. Consider the following example, in which moduleB depends on the version of moduleA you’re currently developing, and moduleB specifies "moduleA" as a dependency in its package.json

$ cd moduleA/
$ npm link
$ cd ../moduleB

# if moduleB package.json is pointing to a yet-unpublished
# version of moduleA, npm install will fail:
$ npm install

# this will install your local version of moduleA
$ npm link moduleA

# since moduleA is now installed, npm install will ignore it:
$ npm install

Production flags

When deploying, you want npm install to be as fast as possible. To make sure NPM doesn’t waste time installing devDependencies, use the --production option:

$ npm install --production

If you’re logging its output, you also want it to only output the absolutely necessary.

$ npm install --loglevel warn

Git dependencies

If you haven’t published a certain package to the NPM registry, or you can’t because it’s a private module, you can point to a git:// URI instead of a version number in your package.json:

"dependencies": {
    "public": "git://github.com/user/repo.git#ref"
  , "private": "git+ssh://git@github.com:user/repo.git#ref"
}

The #ref portion is optional, and it can be a branch (like master), tag (like 0.0.1) or a partial or full commit id (thanks @Roman for pointing this out). I recommend you use git tags as references to ensure npm install always keeps the module up to date.

Local binaries

Sometimes you need to reference a bin from a module you depend on. It’s not uncommon to have Makefile tasks that leverage a certain node program to perform a compilation or run tests.

Make sure you avoid referencing globally installed binaries like this:

test:
  mocha mytest.js

Instead, point it to the local node_modules, which installs the binaries in a hidden .bin directory:

test:
  node_modules/.bin/mocha mytest.js

Make sure the module (in this case "mocha") is in your package.json under devDependencies, so that the binary is placed there when you run npm install.

If you don’t care for Makefiles, you can also leverage npm run-script by defining the scripts namespace in your package.json:

"scripts": {
    "test": "mocha mytest.js"
  , "build": "uglify mycode.js
}

You can run them like this:

$ npm run-script test
$ npm run-script build
$ npm test # shortcut for `run-script test`

Finally, you can also leverage $PATH and add the relative path ./node_modules/.bin/ to your profile. Then, every time you cd to a module you can type in the scripts directly!

Private repositories

If you never intend to publish a certain module, and you want to avoid accidental publication, make sure to set private like this in your package.json:

"private": true

If you have many private repositories, you might want to consider setting up your own registry, in which case the configuration would look something like this:

"publishConfig": { "registry": "https://yourregistry:1337/" }

Shrinkwrapping

NPM has great support for semantic versioning through the usage of wildcards and expressions in dependencies definitions:

"dependencies": {
    "some-module": "1.0.x"
  , "some-other-module": "<=2.0.0"
}

This, however, can lead to unexpected failure when running npm install over time. At one point your app might work fine, and the next time you deploy, errors can be introduced without changing anything in your code base, due to the introduction of a new version of a dependency with errors. To better understand this problem refer to this blog post.

The shinkwrap NPM command solves this situation rather elegantly. Running

$ npm shrinkwrap

will generate a snapshot of the dependency graph suitable for deployments. For more information about the inner workings of this command, read this article by Joyent.

12 Comments

Roman said

I would also note that the #ref for git dependencies can be the sha1 of the commit id. Even better it can be just the first few (I have found 6 to work well) characters as that is generally unique enough. I use this all the time with my deployments for packages I have forked and patched.

Ruben Verborgh said

Interesting hint for the local binaries!
However, the scripts in `node_modules/.bin` might not always work as expected due to path differences. For example, the following fails with “Cannot find module”:

node_modules/.bin/jshint

Therefore, it is safer to use a module’s scripts directly from the its subfolder:

node_modules/jshint/bin/hint

Guillermo Rauch said

@Ruben
That’s really interesting, I wonder what causes it!

Aaron Powell said

Nice post, I didn’t know about the devDependencies or the git dependencies, gonna keep that in mind for future reference

Jakub Nešetřil said

Great article! Just a quick note – the link to Felixes blog is broken, the correct URL is rather unwieldy: http://debuggable.com/posts/npm-an-intervention:4f44dd25-a114-4361-ada1-6cefcbdd56cb

Guillermo Rauch said

@Jakub
Fixed, thanks!

Anand George said

Is there a way to link all packages in a package.json to a app folder. So the app folder would have the following

app.js
package.json.

Then run npm link. It should link all packages in the package.json, so that they can be required in app.js. Is such an option available.

Tim Oxley said

Just for reference, there’s now two more handy options for npm install: npm install –save-dev and –save-optional which will record the dependencies in your package.json as devDependencies and optionalDependencies respectively.

Briefly blogged about it here: http://blog.timoxley.com/post/22371787222/handy-new-options-for-npm-install

Devin Rhode said

There’s also –save, which will just save the package into dependencies, instead of devDependencies or optionalDependencies

Your thoughts?

About Guillermo Rauch:

CTO and co-founder of LearnBoost / Cloudup (acquired by Automattic in 2013). Argentine living in SF.