Using Browserify without a node_modules directory

Posted on Sat 19 July 2014 in JavaScript

NPM and Bower are pretty awesome; they provides both NodeJS and web developers a way to share CommonJS modules (aka packages) amongst code-bases without relying on copy/pasting concatenated .js files. Browserify fills in the missing link for deploying an application to the web; it gathers up disparate CommonJS modules which NPM/Bower resolved and bundles them up into a single .js file ready to be dropped into a <script> tag.

When writing CommonJS code, you can import files by two methods, either relativley, (typically used internally inside your app / module), ie:

var MakePizzaCommand = require("./commands/MakePizzaCommand");

Or you can import entire libraries, eg: those which were resolved by Bower / NPM

var reqwest = require("reqwest");

In the first example, Browserify will locate the MakePizzaCommand object by reading the file relative from the host file making the require call; ie:

├── app.js <-- makes the require call
└── commands
    └── MakePizzaCommand.js  <-- gets included

In the second example, Browserify identifies that you are not referencing a local file (as the require argument does not look like a path), so instead it starts looking up the directory tree for a node_modules folder which contains a subfolder for the module the host file making the require call is trying to import.

pizzajs
├── node_modules <-- 3. found it! now look in each subfolder
│   │                  for a package.json file with the name of
│   │                  the module we are trying to import.
│   └── reqwest
│       └── package.json
│
├── src <-- 2. no `node_modules` dir here either, so look in the
│   │               parent dir (pizzajs)
│   │
│   ├── app.js <-- 1. makes the require call, no `node_modules` dir
│   │               here so look in parent dir (src)
│   └── commands
│       └── MakePizzaCommand.js
└── test

But what if you don't want to use NPM / Bower to resolve your dependencies? AFAIK you have two options avaiable to you:

  1. Create a node_modules folder in your project and copy all your CommonJS libraries into it during the build process; note that the node_modules folder must sit either adjacent to your project's sources (src) or in a ancestor folder.

  2. Make use of the NODE_PATH environment variable, this provides another absolute URL for browserify to use when looking for CommonJS modules. This allows you to pull off the following folder structure (which may suit none-javascript-based build systems better):

pizzajs
├── lib
│   └── reqwest
│       └── package.json
└── src
    └── app.js

If you're using Grunt, you can easily set the NODE_PATH environment variable with each build by adding the following to the top of your Gruntfile.js:

// Set NODE_PATH env-var to the cwd.
process.env["NODE_PATH"] = __dirname;

module.exports = function(grunt) {
  grunt.initConfig({ .. });
});