RequireJS and Juicer

Austin King

3

As JavaScript codebases become larger and more complex, we start to miss the niceties we take for granted in general purpose programming languages. It would be really great to have a JavaScript module system. The CommonJS community has created a standard that Jetpack and Node.js use.
PhotonQ-Beauty on the Horizon of Complexity
Let’s start a conversation around JavaScript modules and toolkits.

A module or package system does a couple things, but the main benefits come from:

  • Loading code
  • Providing a way to organize source code
  • Makes unit testing easier

I’ve been test driving RequireJS and Juicer; it’s a good combo providing the following benefits:

  • Framework agnostic (as happy in Django as it is in Node.js, RoR, or Kohana)
  • Asset packaging for CSS and JavaScript
  • CommonJS compatible
  • Integrates with JSLint and YUI Compressor

RequireJS

RequireJS is a JavaScript library which provides a CommonJS modules implementation and the ability to load individual JavaScript files. It’s optimized for in-browser use, unlike many of it’s CommonJS siblings.

A common performance tip is to concatenate scripts and minify them to reduce bandwidth and total number of HTTP requests. As applications mature, it’s easy to repeat yourself or include too much code in your minified file. Breaking your JS up into modules allows you to do dependency management and only include relevant code.


Editorial Note: It’s important to know which code is in which file when explaining this stuff… so I’ve included the filename in the code snippet. It isn’t actually important to how the RequireJS or Juicer systems work.

Here is a hypothetical example of JavaScript organized into RequireJS modules:

//file: search/form.js
require.def('search/form', [], function () {
    return {
        initialize: function () {
            $('input[name=q]').focus();
        }        
    };
});

// file: anonymous_visitor.js
require.def('anonymous_visitors', ['search/form'], function (search) {
        search.initialize();
});

So the top level script anonymous_visitor.js uses a function declared in the search module. Perhaps this website has a minimal amount of JavaScript for anonymous visitors, but loads a different, more heavy weight script, for recognized visitors. By breaking up the code, the search module could be used a second time from another top level script.

There are multiple ways to use require. If a module doesn’t have any dependencies, then you can use this format:

//file: search/form.js
require.def('search/form',  {
    initialize; function () {
           $.('input[name=q]').focus();
    }
});

So far, I’ve found it best to use the “module, dependencies array, function” format even if a module doesn’t depend on any other modules. This allows a module to grow without having to change the last parameter to def from an object literal to a function. It increases consistency in the code and reduces the amount of boilerplate you have to memorize.

There is one variation. In top level JavaScript files, instead of an object literal, we return a function. This function runs once, right after the dependencies have been loaded. Really this code should be thin and just kicking things off. Think of this top level file as the “application” script and the others as reusable components.

By default, RequireJS can load code over the network. It’s got some advanced features here, but I’m not using them for by default. Imagine we’ve taken a 3,000 line JavaScript file and break it into 30 components. The overhead of 30 separate HTTP requests for JavaScript files kill our performance?

Not if we take our top level application scripts like anonymouse_visitor.js and create a minified script with all of their dependencies included. RequireJS will only try to load modules over the network, if it has not seen their definition. If we make a single big old JavaScript file out of all the relevant bits, then no HTTP requests for .js files are needed.
Carrot Apple Ginger Juice

Juicer

This is where Juicer comes in. It is a command line application for packaging up JavaScript and CSS. Django, Ruby on Rails and other systems often ship with a similar asset packager.

For JavaScript, Juicer will:

  • Take one JavaScript file and figure out all of it’s dependencies
  • Only include relevant code
  • Verify the code quality with Jslint
  • Minify with jsmin or YUI Compressor

For CSS it will:

  • Take one CSS file and figure out all of it’s dependencies
  • Only include relevant files
  • Embed images as data uri

And many other handy features to improve website performance. These steps happen once at build time.

To Juicify our earlier example code, we do the following:

//file: anonymous_visitor.js
/**
* @depend  search/form.js
*/
require.def('anonymous_visitors', ['search/form'], function (search) {
        search.initialize();
});

The only change is the @depend and the full filename relative to the current JavaScript file.

Then running the command juicer merge anonymous_visitor.js will produce anonymous_visitor.min.js, after running JSLint against each dependency and top level script.

As a module depends on more modules you just add them to the 2nd argument and add parameters to the function (which is the 3rd argument).

During development, you probably don’t want the code minified, so you can pass juicer –min '' and it will just concatenate your files. JSLint is optional, so if you’ve had your fill of curmudgeonry for the day, then skip it with glee.

CSS is similar and doesn’t invent any new syntax. You just use an empty top level CSS file filed with @import statements. Example:

/* file anonymous_visitor.css */
@import url(search.css);
@import url(general.css);
@import url(registration.css);

Juicer currently has no verify step for CSS. Read the docs on the merge command for both JS and CSS as there are lots of handy flags.

Feature Request

The only major flaw with this setup is that Juicer uses it’s own convention for capturing dependencies with the @depend doc tag. You may have noticed that once we added Juicer to the mix, we started violating the DRY principal. With @depend search/form.js and require.def('appname', ['search/form'], ... we’re saying the same thing twice. if we rename form.js, we’ll have to update the code in two places.

Juicer should be enhanced to understand the RequireJS standard and load files from it. It should use an optional -d or --document-root, just like it already does for CSS. Then @depend. would be obsolete.

I should note that the RequireJS project does have a Juicer-like tool called Optimize. I liked Juicer (and some other asset packagers I tested) more than RequireJS’s Optimize, YMMV. Perhaps it’s because Optimize uses Google Closure. Perhaps it’s because I like small focused tools. If you choose Optimize instead of Juicer, then you don’t have to repeat yourself and can skip using @depend. tags.

She's Come Undone

How Should I Organize My Code?

RequireJS and Juicer give you the building blocks for a better codebase. However, they don’t give you any guidance on how to structure your CSS and JavaScript. You’ll still need to do performance testing or guesstimating to know weather you should have 2 top level scripts across your 17 different screens, or 5 top level scripts with 11 lazy loaded components… Performance tuning and architecture aint easy!

jQuery

I’m treating jQuery (and other third party libraries) as part of the platform. I don’t worry about wrapping them in RequireJS style modules nor verifying them (as JSLint hates jQuery). If you want to be consistent, some of the work has already been done for you in making jQuery compatible with RequireJS. Instead I just use an @depend to track these 3rd party dependencies.

What’s Your Take

I’ve been using this setup for about a week and I really like how it improves the code without tying me to one web development framework.

  • What are you using?
  • What do you think?

3 responses

  1. skierpage wrote on :

    You’ve got missing text after “already does for CSS. Then @depend” , then an unclosed code tag that puts the rest of the post in monospace.

  2. dherman wrote on :

    I totally agree with your sentiment– JS is missing some basic facilities for modularity and packaging that other languages have. It’s great to see people building tools to make up for this lack. Thanks for bringing attention to them!

    I also think the JavaScript language deserves to move forward, and some of the aspects of module systems — particularly, managing imports and exports — would work more smoothly with language support. I don’t foresee an end to the need for external tools for minification, analysis, optimization, repository management, etc. But at the same time, the global object and the various “module patterns” people have to use make it hard to build common ground and avoid stepping on each other’s toes.

    On the ECMA standards committee, we’ve been working on a new module system for the next version of JavaScript. Some of our notes on the system are here:

    http://wiki.ecmascript.org/doku.php?id=strawman:simple_modules

    and we are always happy to discuss them on the es-discuss mailing list:

    https://mail.mozilla.org/listinfo/es-discuss

    I believe the next version of JavaScript could make it significantly easier for people to build, share, and combine libraries in JavaScript. This isn’t meant to take the place of tools like RequireJS or Juicer, but it can alleviate some of the burden from them and build modularity support directly into JavaScript, no batteries required.

  3. Austin King wrote on ::

    @skierpage thanks! Fixed.