Getting Started with CommonJS Modules and Browserify

Monday, January 5th, 2015

Modules allow us to organize code and prevent polluting the global namespace. Until ES6, JavaScript didn’t have a native module system. There were however popular module systems created by the community. In the browser, the most popular module system was the Asynchronous Module Definition (AMD). In order to use AMD modules, you could use a library like Require.js. The Node.js environment took a different approach to modules and created a module system called CommonJS. If you’d like to use CommonJS style modules in the browser, there is a tool called Browserify which has been picking up steam. Which module system you choose is entirely up to your team’s preference and application’s needs.

Before we can see how to use CommonJS modules in the browser with Browserify, let’s look at how CommonJS modules work.

How CommonJS Modules Work

To create a CommonJS module, simply create a new JavaScript file. Anything in that file won’t be seen by other modules. In order to give your module a public API, you can attach properties and methods to the module.exports property that is accessible in every module.

Custom Modules - Example 1

Let’s say we have a Validation constructor function stored in validation.js and a User constructor function stored in the models/user.js.

Example 1 folder structure

validation.js

function Validation() { /* implementation here */ }
Validation.prototype.passes = function() { /* implementation here */ };
module.exports = Validation;

Here I’ve defined a constructor function Validation with a method passes(). This function is made public by assigning it to module.exports which is accessible from every module.

And similarly for models/user.js:

function User() { /* implementation here */ }
module.exports = User;

If we want to access a module from main.js, we need to use the require() function and specify a relative path to the file wihout the .js extension.

var Validation = require('./validation');
var User = require('./models/user');

console.log(Validation);
console.log(User);

What require() returns is whatever is set to module.exports in the module being required. For the validation module, main.js can’t see anything else but the Validation constructor function. Any variables declared inside validation.js are inaccessible from main.js.

So in other words, if we had the following in validation.js:

var emailPattern = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

function Validation() { /* implementation here */ }
Validation.prototype.passes = function() { /* implementation here */ };
module.exports = Validation;

The variable emailPattern would only be accessible from within validation.js and could not be seen from main.js.

One other thing to mention is that in every module, there is another variable availabled called exports which references module.exports.

databaseConfig.js

exports.host = 'localhost';
exports.username = 'root';
exports.password = 'root';
exports.database = 'mydb';

main.js

var db = require('./databaseConfig.js');
console.log(db); // { host: 'localhost', username: 'root', password: 'root', database: 'mydb' }

Custom Modules - Example 2

We can also specify a file named index.js in a folder. If we require that folder, it will automatically use the index.js file as the module. This is particularly useful if you have a bunch of related modules that you want to group under a single object.

Example 2 folder structure

Here I have created two different types of formatter modules in the formatters directory.

formatters/currency.js

module.exports = function currency() {
  console.log('format currency');
};

formatters/date.js

module.exports = function date() {
  console.log('format date');
};

To require all formatters from main.js, simply create an index module that requires each formatter. We can set module.exports equal to an object containing each formatter.

formatters/index.js

module.exports = {
  date: require('./date'),
  currency: require('./currency')
};

Then, from main.js, we can simply require the formatters directory which will automatically require formatters/index.js.

main.js

var formatters = require('./formatters');
formatters.currency(); // format currency
formatters.date(); // format date

Working With Third Party Modules

We can also work with third party modules from NPM. Let’s install one I created called validatorjs, which was inspired by the Validator class in the Laravel framework.

npm install validatorjs

This will create a folder called node_modules containing all installed third party modules.

Example 3 folder structure

From our main.js file, we can require it by simply specifying the name of the module.

main.js

var Validator = require('validatorjs');
console.log(Validator);

How to create a reusable third party module is an entire post for itself so I won’t cover that here.

Those are the basics of creating and using CommonJS modules. Let’s now see how we can use Browserify to leverage CommonJS modules for our JavaScript in the browser.

Browserify

To install browserify, simply install it globally using npm.

sudo npm install -g browserify

Once you have installed browserify, you can build all of your JavaScript into a single file:

browserify main.js -o bundle.js

Browserify will look at main.js and recursively bundle up all of the required modules into a single output file (hence the -o option) bundle.js.

Having to run this command every time you modify a JS file can be tedious. Instead, we can leverage another global module called watchify.

sudo npm install -g watchify

As the module documentation states, watchify is “watch mode for browserify builds”. We can use it just like the browserify command. watchify will watch any files in your dependency graph and rebuild the output file as soon as any file changes.

watchify main.js -o bundle.js

One thing to note, when using watchify, you will see full system paths to your modules in the output file. For obvious security reasons, you don’t want this for your production build. watchify is not intended to be used for deployment. In your build system, use the browserify command instead and you won’t see these full system paths.

Conclusion

Browserify is a command line tool that allows us to use CommonJS modules in the browser. Let me know what your thoughts are on using CommonJS modules in the browser through Browserify.

Disclaimer: Any viewpoints and opinions expressed in this article are those of David Tang and do not reflect those of my employer or any of my colleagues.

comments powered by Disqus