Flite Culture

Why We Use Node.js and Grunt to Build JavaScript

Engineers write a lot of JavaScript at Flite. At last count, we had over 103,000 lines of JavaScript in one of our repositories. It powers our interactive web applications, real-time metrics server, and advertising runtime. As our JavaScript has grown in complexity, maintenance has become more challenging.

In order to wrangle our constantly growing source, we’ve completely changed our JavaScript development workflow to bring it up to modern front-end development standards. We ultimately chose Node.js and grunt over rake, ant, or a Makefile to manage this process. Here’s why.

Rapidly maturing JavaScript build tools

As both Paul Irish and Rebecca Murphey have proclaimed, there has been an explosion of JavaScript tools that help streamline developer workflows in the front-end. As a community, front-end engineers have moved way beyond using browser developer tool plugins and strategically-placed console.log() statements to debug, test, and build code.

In our case, all the critical tools we were using for testing and building javascript — jasmine, uglifyjs, and jshint — had actively maintained Node.js projects.

Ease of writing and maintaining build scripts

Engineers at Flite, even our I’d-rather-not-touch-JavaScript-even-with-a-very-long-stick backend developers, speak some JavaScript. It was important that everyone could have a basic understanding of how the front-end build actually works by examining the source. Compare these three implementations of minifying files:

Ant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<target name="js-compile-all" description="Compile JavaScript files with Closure" unless="skip-js-compile">
    <echo>Compiling JS files in ${input.scripts.dir} in closure...</echo>
    <apply executable="java" dest="${output.scripts.dir}">
        <arg value="-jar"/>
        <arg path="${jar.lib.dir}/closure-compiler.jar"/>
        <arg line="--js"/>
        <srcfile/>
        <arg line="--js_output_file"/>
        <targetfile/>
        <fileset dir="${output.scripts.dir}" includes="**/*-main.debug.js" />
        <mapper type="glob" from="*-main.debug.js" to="*-main.min.js"/>
    </apply>
    <echo>Finished compiling JS files</echo>
</target>

Rake

1
2
3
4
5
6
7
8
9
10
11
def uglify(script_file)
  output = `uglifyjs #{script_file}`

  #Did it work?
  if !$?.success?
    puts "Uglify Failed"
    Process.abort
  end

  output
end

Node.js (using grunt)

1
2
3
4
5
6
7
8
grunt.initConfig({
  min: {
    dist: {
      src: ['dist/built.js'],
      dest: 'dist/built.min.js'
    }
  }
});

Of all the above implementations, grunt wins for readability – it’s just a JSON object with an array of source files and a single destination file. The rake task is also straightforward, but it just wraps the command line and it requires UglifyJS to be installed globally before running it. As for the ant task… the XML speaks for itself.

The awesomeness of grunt + npm

We originally wrote our Node.js build script using jake, which is an npm package similar to Ruby’s rake. It worked… except we ended up with a 1000+ line build file to concatenate, minify, test, and deploy to Amazon S3.

In grunt, however, concatenation and minification tasks are built-in. It’s easy to load other grunt tasks via npm so you can develop custom grunt tasks that can be shared across projects (and the world!):

1
2
// Load some grunt tasks we’ve developed from the npm package grunt-barkeep.
grunt.loadNpmTasks('grunt-barkeep');

There’s a growing library of npm modules that define custom grunt tasks that will plug in to another grunt file. A search for “grunt” on search.npmjs.org returned 15+ modules that do everything from compile CSS to build CoffeeScript. There’s even an Amazon S3 module.

Grunt is straightforward to configure. If someone needs to make a build change, like adding a new build target, it’s as simple as modifying a centralized JSON configuration object. The grunt API also is well documented with many examples.

Last Thoughts

The Node.js tools used to build javascript are evolving rapidly. By the time we put the finishing touches on our giant Jakefile, the grunt project was just picking up speed.

Testing, building, and deploying javascript on Node.js is getting much easier. By leveraging npm, it seems likely that the future of javascript build scripts could be as easy as à la carte loading of npm packages and a few small changes to a JSON configuration object. Grunt makes this process easy, and that’s why we use JavaScript to build JavaScript.

Follow the grunt-barkeep project on github if you’re interested to see (and contribute) to the grunt plugins Flite has developed for our JavaScript build scripts.

 

Flite Open Source Javascript Libraries:

Projects Referenced in This Article: