A boilerplate module setup using Browserify, Babel, Mocha, and Chai

Over the past few months I’ve been taking a look at a few of the ES2015 features that have landed in JavaScript language. There some excellent additions that will help JS developers write more succinct code. My personal favorites at the moment are Template literals and Default parameters. Being able to specify a default parameter without a load of boilerplate code at the top of a function is a real win, and no more concatenating strings when you need to include a variable.

So while experimenting with the new features, I figured I’d also bring a few other technologies together to build a very simple module boilerplate, that I could then use on future projects.

Purpose of the boilerplate

The initial purpose was to experiment with the ES2015 import statement, but this then branched out to include Babel, and Browserify. Once those were implemented, I figured I would also roll in Mocha, Chai, and jQuery.

There was a big question in my head on how these could all work together to create a simple workflow, which could then be built upon in the future. My checklist of what I wanted to achieve with the experiment was:

  • Ability to use ES2015+ features
  • Split the code into separate modules, for easier maintenance and testability
  • Be able to bundle the JavaScript in an efficient manor
  • Work directly with the DOM using the querySelector methods, or even jQuery since I sometimes require old IE support
  • Be testable using a popular testing framework

Technologies

A list of the technologies used and their purpose is listed below:

ES2015 (the language formally known as ES6, formally known as Harmony)

ES2015 is the latest iteration of the JavaScript language. The year is now being used in the name, so it is easy to distinguish when the changes in the language were added (in theory!). So as you would expect there’s also an ES2016, and ES2017 in the pipeline. These latest iterations add a whole host of useful language features and improvements to make it easier to write JavaScript. The full ES2015 language specification can be found here

Babel

The problem with introducing new language features on the web platform is you still need to support older browsers. As the saying goes, you can’t break the web. Because of this, nobody wants to write some lovely ES2015 code, and then have to write another version just for older browsers. Older browsers won’t know how to interpret these new features, and the page will break.

This is where Babel comes to the rescue. Babel is a ES2015 compiler, that will transform the latest features back into ES5 compatible code so older browsers will still work. It even has the ability to polyfill methods that older browsers don’t support; so there’s no reason not to start learning and using ES2015 today.

Mocha & Chai

Mocha is a very popular JavaScript testing framework that runs on Node.js. It allows a developer to write and run a set of tests across their JavaScript code, and accurately report the results. Any tests that fail can then be debugged and fixed.

Chai is a BDD / TDD assertion library that can be used with any testing framework, but is often used in conjunction with Mocha. It comes with a whole set of API’s that allow you to write your tests in whatever way you prefer (Should, Expect, Assert).

Using both libraries together can be a real life-saver when it comes to refactoring code. It is very quick and easy to see when new code changes have broken functionality (assuming you have written your tests correctly!).

jQuery

I don’t think I really need to explain this one do I? Everyone knows jQuery, I’m sure it has saved most of us from cross-browser hell in the past! I have included it here because I often have projects that still require IE8 support. In my opinion it is much easier to include jQuery, than pick out a host of micro-libs to do the same job, with all their custom API’s and documentation.

Browserify

Browserify is the “glue” that brings all of these technologies together. Browserify allows you to write code like you would in Node.js (using the require() method). This then gets bundled up into a single JavaScript file that the browser can execute. Browserify deals with all the code dependencies for you, and in the case of this demo also compiles ES2015 back to ES5 using Babel mentioned above.

Simple module example

The code below shows a very simple example on how modules are imported into the main index.js file, then Browserify does the rest for you. The init() method kicks off the DOM interaction in module 1, and a sample unique array is being imported in from module 2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// pull in everything from the sample module using a path
import * as moduleName from './modules/sample-module';
// pull in single variable from a module using browserify alias (browserify.js)
import { uniqueArray } from 'sample-module-2';

// start the module, pass any options we need
moduleName.init({exampleSetting: 'String Value'});

// use a specific method in the module
const addResult = moduleName.add( 5, 6 );
const minusResult = moduleName.minus( 10, 10 );

// results from module 1
console.log( addResult, minusResult );
// results from module 2
console.log( uniqueArray );

The functionality of each module is hidden away in separate files, allowing you to easily modularise you code. Using this setup, it is easy to create private variables and methods inside the modules. A developer then only need expose what is required to the wider application using the export keyword.

I’ve created a very simple sample page you can view using the grunt command. Grunt compiles everything and then launches a small BrowserSync http server that can be used to view the page. For more information on how to run the code see the Github readme. Note: If you prefer to use Gulp instead of Grunt, this is fully possible. I simply used Grunt in the demo as I already had most of the code written from previous projects. This allowed me to focus on the JavaScript, rather than workflow tooling.

Issues

There was a fair bit of trial and error with the initial setup to get it working as I wanted. My main issue occurred when it came to implementing Mocha, Chai, and jQuery. Testing jQuery in a Node environment became a real pain because of two errors I ran into: ReferenceError: $ is not defined and Error: jQuery requires a window with a document.

The first, $ no defined, was easily solved. In the browser environment I had jQuery loaded as a global object via its own script. You then tell Browserify this is the case using "jquery": "global:jQuery" in the package.json file. This of course didn’t work in Node since it isn’t a browser environment, so the script wasn’t loaded. Adding import $ from 'jquery'; inside the module fixed that.

The second, jQuery requires a window with a document, took a good few hours to fix. I tried everything I could find on Stack Overflow including jsdom and mocha-jsdom; But no matter what setup I tried, the error still occurred. In the end it was installing jsdom-global that fixed it. Install it, create yourself a mocha.opts file for your the custom settings, and you are ready to go.

1
2
3
4
5
6
7
8
9
describe( 'Example test description', function () {
    let $;

    before( function () {
        $ = require( 'jquery' );
    } );

    // your tests go here
} );

Then you can either run Mocha from the command line using npm run test with the following settings in your package.json:

1
2
3
"scripts": {
    "test": "./node_modules/mocha/bin/mocha ./src/js/modules/test --reporter list --opts ./src/js/modules/test/mocha.opts"
  },

Or what I prefer to do is run the tests directly in my IDE. JetBrains WebStorm is my editor of choice, just remember you may need to point your IDE towards the mocha.opts file in the test settings.

Conclusion

This is my first iteration of a module system I plan to use on new projects going forwards, and it will continue to evolve to fit particular project requirements as they are discovered. You can find all the code, along with the very simple example on GitHub. Feel free to use it if you like, or even suggest improvements. I’m always open to constructive criticism so raise an issue or get in contact with me via the contact page.