Grunt: The Javascript Task Manager

When you play the Web Developer role, sometimes you may have to endure some repetitive tasks like minification, unit testing, compilation, linting, beautify or unpack Javascript code and so on. To solve this problems, and in the meantime, try to keep your mental health in a good shape, you desperately need to find a way to automate this tasks. Grunt offers you an easy way to accomplish this kind of automation.

In this article I’ll try to explain how to automate some tasks with Grunt, but I recommend that you should take some time to read Grunt’s documentation and enjoy the experience by yourself.

So, in the following sections I’ll try to show you how to accomplish this tasks:

  • Concatenate and create a minified version of your CSS, JavaScript and HTML files.
  • Automatic generation of the documentation for JavaScript with JSDoc.
  • Linting your JavaScript code.
  • Reformat and reindent (prettify) your JavaScript code.

You can install Grunt via npm (Node Package Manager), so, to install Grunt you need to install Node.js first.

Now that you have Node.js and npm installed is a good time to install globally the Grunt CLI (Command Line Interface) package.

$ sudo npm install -g grunt-cli

Once grunt-cli is installed you need to go to the root directory of your project and create a package.json file, to accomplish this you can do the following:

$ cd example_project
$ npm init

The previous command will ask you a series of questions in order to create the package.json file, package.json basically store metadata for projects published as npm modules. It’s important to remember to add this file to your source code versioning tool to ease the installation process of the development dependencies among your partners via npm install command.

At this point we can install Grunt and their respective plugins in the existing package.json with:

$ npm install grunt --save-dev

And the plugins that you need can be installed as follows:

$ npm install <grunt-plugin-name> --save-dev

Please note that the --save-dev parameter will change your devDependencies section in your package.json. So, be sure to commit the updated package.json file with your project whenever you consider appropriate.

Code documentation

If you document your code following the syntax rules defined on JSDoc 3, e.g.:

 /**
  * A callback function returning array defining where the ticks
  * are laid out on the axis.
  *
  * @function tickPositioner
  * @see [email protected] http://api.highcharts.com/highstock#yAxis.tickPositioner}
  * @param {String} xOrY - Is it X or Y Axis?
  * @param {Number} min - Minimum value
  * @param {Number} max - Maximum value
  * @returns {Array} - Where the ticks are laid out on the axis.
  */
 function tickPositioner(xOrY, min, max) {

    // do something

    return tickPositions;
 }

If you need more information about JSDoc, read their documentation, it’s easy to catch up.

The next step to automate the generation of the code documentation is to install first the grunt-jsdoc plugin as follows:

$ npm install grunt-jsdoc --save-dev

Once grunt-jsdoc is installed you must create your Gruntfile.js in the root directory of your project and then add the jsdoc entry to the options of the initConfig method.

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      jsdoc : {
          dist : {
              src: ['src/*.js', 'test/*.js'], 
              dest: 'doc'
          }
      }
  });

};  

Then, you need to load the plugin after the initConfig method in the Gruntfile.js:

// Load the plugin that provides the 'jsdoc' task.
grunt.loadNpmtasks('grunt-jsdoc');

The resulting Gruntfile.js until now is:

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      jsdoc : {
          dist : {
              src: ['src/*.js', 'test/*.js'], 
              dest: 'doc'
          }
      }
  });

  // Load the plugin that provides the 'jsdoc' task.
  grunt.loadNpmtasks('grunt-jsdoc');

};  

To generate the documentation, you need to call the jsdoc task as follows:

$ grunt jsdoc

Immediately you can see, inside the doc directory, the available documentation in HTML format with some beautiful styles by default.

JSDoc

Linting your JavaScript code

In order to find suspicious, non-portable or potential problems in JavaScript code or simply to enforce your team’s coding convention, whatever may be the reason, I recommend that you should include a static code analysis tool in your toolset.

The first step is to define your set of rules. I prefer to specify the set of rules in an independent file called .jshintrc located at the root directory of the project, let’s see an example:

// file: .jshintrc
{
    "globals": {
        "Highcharts": true, // This is only necessary if you use Highcharts
        "module": true // Gruntfile.js
    },
    "bitwise": true,
    "browser": true,
    "camelcase": true,
    "curly": true,
    "eqeqeq": true,
    "forin": true,
    "freeze": true,
    "immed": true,
    "indent": 4,
    "jquery": true,
    "latedef": true,
    "newcap": true,
    "noempty": true,
    "nonew": true,
    "quotmark": true,
    "trailing": true,
    "undef": true,
    "unused": true
}

If you need more details about the checks that offer every rule mentioned above, please, read the page that contains a list of all options supported by JSHint

To install the grunt-contrib-jshint plugin, , please do as follows:

$ npm install grunt-contrib-jshint --save-dev

Next, proceed to add the jshint entry to the options of the initConfig method in the Gruntfile.js as follows:

jshint: {
 options: {
  jshintrc: '.jshintrc'
 },
 all: ['Gruntfile.js', 'src/*.js', 'test/*.js']
}

Then, as it’s done in the previous section, you need to load the plugin after the initConfig method in the Grunfile.js

// Load the plugin that provides the 'jshint' task.
grunt.loadNpmTasks('grunt-contrib-jshint');

To validate your JavaScript code against the previous set of rules you need to call the jshint task:

$ grunt jshint

Note that if you need more information or explanations about the errors that you may receive after the previous command I suggest that you should visit JSLint Error Explanations site.

Code Style

As I mentioned in the previous section, I suggest that you should maintain an independent file where you define the set of rules about your coding styles:

// file: .jsbeautifyrc
{
  "indent_size": 4,
  "indent_char": " ",
  "indent_level": 0,
  "indent_with_tabs": false,
  "preserve_newlines": true,
  "max_preserve_newlines": 2,
  "jslint_happy": true,
  "brace_style": "collapse",
  "keep_array_indentation": false,
  "keep_function_indentation": false,
  "space_before_conditional": true,
  "break_chained_methods": false,
  "eval_code": false,
  "unescape_strings": false,
  "wrap_line_length": 0
}

Next, proceed to add the jsbeautifier entry to the options of the initConfig method in the Gruntfile.js as follows:

jsbeautifier: {
  modify: {
      src: 'index.js',
      options: {
          config: '.jsbeautifyrc'
   }
  },
  verify: {
   src: ['index.js'],
   options: {
   mode: 'VERIFY_ONLY',
   config: '.jsbeautifyrc'
  }
 }
}

The next step it to load the plugin after the initConfig method:

// Load the plugin that provides the 'jsbeautifier' task.
grunt.loadNpmTasks('grunt-jsbeautifier');

To adjust your JS files according to the previous set of rules you need to call the jsbeautifier task:

$ grunt jsbeautifier:modify

Concat and minified CSS, HTML, JS

To reduce the size of your CSS, HTML and JS files do as follows:

$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-htmlmin --save-dev
$ npm install grunt-contrib-cssmin --save-dev

Next, add the htmlmin, cssmin and uglify entries to the options of the initConfig method in the Gruntfile.js as follows:

htmlmin: {
  dist: {
    options: {
      removeComments: true,
      collapseWhitespace: true
    },
    files: {
      'dist/index.html': 'src/index.html',     // 'destination': 'source'
      'dist/contact.html': 'src/contact.html'
    }
  }
},
cssmin: {
  add_banner: {
    options: {
      banner: '/* My minified css file */'
    },
    files: {
      'path/to/output.css': ['path/to/**/*.css']
    }
  }
},
uglify: {
 options: {
   banner: '/* <%= grunt.template.today("yyyy-mm-dd") %> */\n',
   separator: ',',
   compress: true,
 },
 chart: {
   src: ['src/js/*.js'],
   dest: 'dist/js/example.min.js'
 }
}

The next step it to load the plugins after the initConfig method:

// Load the plugin that provides the 'uglify' task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Load the plugin that provides the 'htmlmin' task.
grunt.loadNpmTasks('grunt-contrib-htmlmin');
// Load the plugin that provides the 'cssmin' task.
grunt.loadNpmTasks('grunt-contrib-cssmin');

To adjust your files according to the previous set of rules you need to call the htmlmin, cssmin or uglify tasks:

$ grunt htmlmin
$ grunt cssmin
$ grunt uglify

After loading all of your Grunt tasks you can create some aliases.

If you want to create an alias that runs the three previous tasks in one step do as follows:

// Wrapper around htmlmin, cssmin and uglify tasks.
grunt.registerTask('minified', [
        'htmlmin',
        'cssmin',
        'uglify'
    ]);

So, to execute the three tasks in one step you can do the following:

$ grunt minified

The previous command is a wrapper around the htmlmin, cssmin and uglify tasks defined before.

Wrapping all together we get the following [Grunfile.js][]

Other plugins

Grunt offers you a very large amount of plugins, they have a dedicated section to list them!

I recommend that you should take some time to check out the following plugins:

Conclusion

Certainly, Grunt do a great job when we talk about task automation, also, automating this repetitive tasks is fun and reduce the errors associated with tasks that in the past we used to run manually over and over again. That been said, I definitely recommend that you should try Grunt, you will get in exchange an improvement in your development workflow, also with it you can forget to endure repetitive tasks that we hate to do, as a result you will get more free time to focus on getting things done.

Last but not least, another option for Grunt is Gulp, I recently read about it, if you look at their documentation you notice that their syntax is more clean than Grunt, also is very concise and allows chaining calls, following the concept of streams that pipes offer in *nix like systems, so, I’ll give it a try in my next project.