Porting from Make to Grunt

Aug 16, 2014 gruntJavaScriptMake

This post documents my experience porting a Makefile to a Gruntfile in my Rimu Markup project.

NOTE: Since first writing this post I have switched the same project from Grunt to Jake.

Why Grunt

My reasons for porting were to:

  1. Learn about Grunt (it seems to be the de facto nodejs/JavaScript build tool).
  2. Make the build process cross-platform.
  3. End up with a build script that is more widely understood.
  4. End up with a build script that is easier to write, maintain and test.

What differentiates Grunt from Make

Use ShellJS instead of Grunt plugins

My first porting attempt was based on the de facto Grunt scripting model i.e. use Grunt Plugins to run development tools. I soon became frustrated, for every build tool I wanted to run I had to:

  1. Find a corresponding Grunt plugin (often having to choose from competing plugins).
  2. Install the plugin and add the devDependencies to the package.json file.
  3. Learn the plugin’s JSON configuration syntax and mentally map it onto the tool’s command-line arguments.
  4. Load the plugin in the Gruntfile.
  5. Cross my fingers and hope that the plugin developers keep the plugin bug free and in sync with both new releases of the build tool and Grunt.

For example, my Makefile’s lint target (task) runs a jshint command:

        jshint test/spans.js test/blocks.js bin/rimuc.js

Aside from installing jshint that’s it, it’s that simple – plus I know exactly what it does because I already know how to use the jshint command.

Now consider doing it with Grunt using the grunt-contrib-jshint plugin:

  jshint: {
    files: ['test/spans.js', 'test/blocks.js', 'bin/rimuc.js'],
    options: {
      jshintrc: true

All this crazy extra work and stuff to learn for nothing! I was about to pack it in when I discovered ShellJS, a great library that gives you cross-platform UNIX-like shell capabilities. The above plugin code can now be replaced by:

grunt.registerTask('lint', 'Lint Javascript files', function() {
  shelljs.exec('jshint test/spans.js test/blocks.js bin/rimuc.js');

No plugin, nothing new to learn, easy to read, easy to verify from command-line – what’s not to like!

See the full Gruntfile.js for more examples of the use of ShellJS functions.

Lessons Learnt

  1. Do not use Grunt plugins that are just wrappers for existing command-line tools (I’m not the only one who thinks this way, see here and here.
  2. Be very selective of the plugins you do use – weigh carefully plugins verses your own JavaScript code.
  3. Use the ShellJS library to leverage your existing knowledge and to make your Gruntfiles easily readable by users that are not Grunt experts.
« Previous Next »