Speeding up Require.js optimization in Maven

Sannie Kwakman

In the last couple of web applications we've been working on, we used the Require.js library to modularize our Javascript code. This allows developers to separate large amounts of Javascript code into smaller modules which makes the code a lot easier to read and maintain.

Require.js also provides an optimizer script which compiles and compresses these modules into one (or a few) files for efficient delivery to the end-user. Sadly, when running the optimizer as part of a Maven build this process can take several minutes to complete. In this article I’ll explain how to speed things up a bit.

The Rhino in the room

We use Require.js' optimizer as part of our application package- and release cycle which currently is powered by Maven. To run the optimizer with Maven we use the requirejs-maven-plugin. As the optimizer is written in Javascript and Maven runs Java, this plugin runs the optimizer script using the JRE’s built-in Rhino Javascript engine. This allows us to create both server- and client-side packages in one go. There’s only one problem: the Rhino Javascript engine is horribly slow.

I can’t really blame the engine itself though as it’s really old. It predates the so-called ‘Engine Race’ during which Javascript engines are being aggressively optimized to deliver speeds we are used to in modern browsers. But still it’s no fun having to wait several minutes for an optimizer running on ancient technology while you know there’s something way faster out there.

As Require.js’ own documentation already states: the preferred method to run the optimizer is by using node.js which is powered by the much faster V8 Javascript engine from Google. Up until recently however, there was no easy way to incorporate node.js into a Maven build. Sure it’s possible to assume node.js is installed on the build environment and execute some commands using maven-ant-plugin, but this will make your build a lot less portable.

Using node.js in Maven

So after getting annoyed by this problem long enough for it to become a pet peeve, I decided to finally fix this. For me the ultimate solution consists of two steps:

  • requirejs-maven-plugin should support running the optimizer using node.js
  • node.js binary should be supplied to the build environment without using an external installation process

To implement step 1 I worked together with requirejs-maven-plugin’s creator Matthew Cheely to add node.js support. In the latest version it’s possible to supply a path to a node.js binary after which the plugin executes the optimizer using node.js.

In order to supply the node.js binary to the build environment I created a new plugin called nodejs-maven-plugin. This plugin extracts a platform-specific nodejs binary to a directory of your choosing. Currently supported platforms are Windows, Mac and Linux, all 32 and 64 bit versions.

Configuring these two plugins should look something like this:


<plugin>
 <groupId>com.github.skwakman.nodejs-maven-plugin</groupId>
 <artifactId>nodejs-maven-plugin</artifactId>
 <version>1.0.2</version>
 <executions>
  <execution>
   <goals>
    <goal>extract</goal>
   </goals>
  </execution>
 </executions>
 <configuration>
  <targetDirectory>
   ${project.build.directory}/nodejs/
  </targetDirectory>
 </configuration>
</plugin>
<plugin>
 <groupId>com.github.mcheely</groupId>
 <artifactId>requirejs-maven-plugin</artifactId>
 <version>2.0.0</version>
 <executions>
  <execution>
   <goals>
    <goal>optimize</goal>
   </goals>
   <phase>prepare-package</phase>
   <configuration>
    <filterConfig>true</filterConfig>
    <nodeExecutable>${project.build.directory}/nodejs/node</nodeExecutable>
   </configuration>
  </execution>
 </executions>
</plugin>

This configuration instructs nodejs-maven-plugin to extract a node.js binary inside the target/nodejs directory of the Maven project. Requirejs-maven-plugin will then run Require.js’ optimizer script using the newly extracted node.js binary. This setup enables you to use node.js on on all your build environments having to install it manually.

Using this new configuration our Javascript optimization step went from 1.5 minutes to a mere 12 seconds. Not bad for a ~10 year jump into the future.

Comments (3)

  1. Fabrice CROISEAUX - Reply

    June 6, 2013 at 6:38 am

    Why not using the optimizer with Grunt ?

    • Sannie Kwakman - Reply

      June 6, 2013 at 9:44 am

      I like the idea of having one build process in which all web application components (in our case: Java .ears and html/javascript/css) are built, tagged, packaged and published to a repository. This simplifies the build process a lot as creating a new build simply means running one command eg. 'mvn clean install'. For us, Maven suits this purpose very well.

      However Maven is very Java centric and is easily trumped by tools like Grunt when it comes to Javascript. There's an interesting discussion on StackOverflow regarding the choice between build-everything-in-maven and build-web-stuff-in-grunt (http://stackoverflow.com/questions/15406977/javascript-web-app-and-java-server-build-all-in-maven-or-use-grunt-for-web-app). For our project, we chose the former.

      However, each approach (eventually) requires node.js on the build environment. But now there's a Maven plugin for that as well.

  2. Seth Williams - Reply

    July 8, 2013 at 1:50 pm

    Thanks for the tutorial it was very easy to understand. I'm definitely going to use it.

Add a Comment