RequireJS Optimization with Play 2.2, WebJars, and CDNs

The Story So Far

This post is just a short follow-up to my previous post about using RequireJS with Play 2.1 and WebJars. In that post I laid out a general approach on how to modularize your Play app using RequireJS, and how to make it production-ready by dividing your RequireJS configuration into several files (one for development, one for production, one for building). This is obviously flawed and can be an easy source of errors when the versions of your WebJars assets don’t match up with CDN versions. It also violates the DRY principle. At the time however it was the only way to use these technologies together.

Meanwhile, In Another Galaxy

Direct RequireJS support

In the meantime, James Ward has been actively developing WebJars and its accompanying Play plugin with two notable enhancements.

First off, WebJars now supports RequireJS directly (previously, it was only limited to the Play plugin). This means it is no longer required to write require(['webjars!angular.js'], function(angular) {...}). Instead require(['angular.js'], function(angular) {...}) is sufficient. To differentiate WebJars libraries from local JavaScript files, the local ones should be prefixed with ./.

For example:

require(['angular', './common'], function(angular) {
  angular.module('myModule', ['common']);
});

Check out James’ post to learn how it works in detail.

So, this solves the problem of the RequireJS-optimizer not recognizing the WebJars loader plugin. We can’t get rid of a seperate build.js file just yet, but the file itself is simpler than before.

Direct CDN support

Secondly, James struck a deal with the jsDelivr Content Delivery Network1. All WebJars are now hosted on jsDelivr, and can easily be retrieved by adding //cdn.jsdelivr.net as a prefix to a WebJars path. An example URL would be http://cdn.jsdelivr.net/webjars/angularjs/1.2.14/angular.min.js.

This means we no longer have to override our RequireJS dependencies in the seperate production main file. Instead they will be resolved at runtime by the updated WebJars Assets controller and locator that are looking for the config parameter contentUrl.

Making Use Of It

The following changes were made to play-angular-require-seed to incorporate the new capabilities.

JavaScript

  • Reduce mainDev.js and mainProd.js to a single main.js file
  • Remove WebJars dependencies from the RequireJS shim
  • Replace any webjars! loader call with only the the dependency’s name
  • Prefix local files with ./, except for packages

Configuration

  • Add WebJars locator to sbt dependencies
  • Configure the CDN url in a prod.conf file, to be loaded in production mode

Scala + Templates

  • Extend WebJars’ Assets controller to load a WebJar from CDN if configured
  • Use RequireJS.getSetupJavaScript helper and the new WebJarsAssets to load main.js and other static content (e.g. CSS files)

The Future Is Bright

This is already not bad, but there are still of couple of things missing2. The introduction of sbt-web, due in Play 2.3, will hopefully allow us to control our asset pipeline the same way as Grunt or Gulp do, but with all the Scala and WebJars goodies on top.


  1. www.jamesward.com/2014/03/20/webjars-now-on-the-jsdelivr-cdn

  2. For example, we currently do not have a single minified and hashed script file, and the locator doesn’t switch to the minified version on the CDN.

Posted in Scala & Playframwork
6 comments on “RequireJS Optimization with Play 2.2, WebJars, and CDNs
  1. Assaf says:

    Hi,
    I’m constantly follows blog & and updating my app according your great play-angular-require-seed.
    Couple of days ago I faced a problem I can’t resolve, and thought maybe a word from you might do the trick.
    I updated my project to use the new WebJars that now supports RequireJS directly, got rid of my two main(mainDev & mainProd) and added the CdnWebjarAssets.
    since then, my project does not being concatenated to single main file on production mode, any idea why?

    • marius says:

      Make sure that in production mode you are referring to main.js in the “javascript-min” folder – that’s the minified, complete file.

      • Assaf says:

        Thanks for the quick response, that is exactly my problem.
        my “main.js” inside “javascript-min” is minified but not concatenated (doesn’t hold all sources).

        • marius says:

          I just checked again with play-angular-require-seed and it works fine. Make sure your main.js actually requires other modules so that Play/r.js can parse the dependency tree and concatenate the files.

  2. Gilles Meyer says:

    Hi,

    Thanks for the great play-angular-require-seed! I like it very much.

    I was wondering how to do unit testing with the seed’s project structure and webjars. In particular, I would like to use karma and jasmine. Would you have any guidelines or good references ?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.