Simple automation for web developers, using Grunt

Grunt, the JavaScript task runner, is an excellent tool to automate boring, repetitive stuff. With a plethora of plugins, and a thriving community around it, you’ll find it’s a great weapon for your webdev arsenal. I’d go as far as saying that it’s invaluable for any moderately complex web project. Once you spend a bit of time on it, you’ll be able to compile your LESS, concatenate, compress and optimize your CSS, minify and combine your JavaScript files, keep separate builds for production and development, deploy your kids to school after they have breakfast and even more!

Let’s begin by installing some needed tools.

Install node.js

$ cd ~
$ curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
$ sudo apt-get install -y nodejs build-essential
$ npm --version

Install Grunt

$ sudo npm install -g grunt-cli
$ sudo npm install grunt

Now, in order to do anything useful we must install NodeJS modules for the tasks we envision to use Grunt for. For instance, if we are using LESS, a good starting point would be a module for automatic compilation of LESS stylesheets to plain CSS every time the .less file is saved.

These NodeJS modules (essentially Grunt plugins) will be catalogued after their installation by npm, the NodeJS package manager (comes with NodeJS by default these days), in a file named package.json inside the root of our project. Let’s move to that directory and create this file, with the following contents:

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5"
  }
}

Perusing the popular Node plugins, we need to locate something to monitor our .less files for changes and then automatically compile them to .css. The following seem up to the task:

Intuitively, we also need to install the actual LESS compiler, lessc. Let’s start with that:

$ sudo npm install -g less
$ lessc --version

Next, installation of the two Grunt plugins we located earlier:

$ npm install grunt-contrib-less --save-dev
$ npm install grunt-contrib-watch --save-dev

The --save-dev parameter captures our dependency on this plugin in package.json. Here is what it looks like after installing the plugins:

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-less": "~1.1.0",
    "grunt-contrib-watch": "~0.6.1"
  }
}

Commit package.json in git along with your other files. When starting afresh, say on a new development VM, installing all your plugins is only one npm install away, executed inside your project’s root directory.

Now let’s move on to Gruntfile.js, the actual configuration of Grunt. First, create it:

module.exports = function(grunt) {
    grunt.initConfig({
       // named tasks will go here
    });

    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // the default task can be run just by typing "grunt" on the command line
    grunt.registerTask('default', ['watch']);
};

Okay, now we’ll configure our two tasks, watch and less. Remember, these will be placed in Gruntfile.js at the appropriate section as indicated by my comment. First, less:

less: {
  options: {
    compress: true,
    yuicompress: true,
    optimization: 2
  },
  files: {
    "assets/public/result.css": "assets/private/css/source.less"
  }
}

This is a very simple task, which when run will simply compile source.less into result.css.

We’ll use the second task, watch, to invoke less when the any .css or .less files change within the specified monitored directory:

watch: {
    styles: {
        options: {
    		spawn: false
        },
        files: [ "assets/public/css/*.css", "assets/public/css/*.less"],
        tasks: [ "less" ]
    }
}

It’s quite simple, isn’t it? Here is the completed Gruntfile.js in all its glory:

module.exports = function(grunt) {
  grunt.initConfig({
        less: {
            options: {
                compress: true,
                yuicompress: true,
                optimization: 2
            },
            files: {
                "assets/public/result.css": "assets/private/css/source.less"
            }
        },
        watch: {
            styles: {
               options: {
                    spawn: false,
                    event: ["added", "deleted", "changed"]
                },
                files: [ "assets/public/css/*.css", "assets/public/css/*.less"],
                tasks: [ "less" ]
            }
        }
    });

    grunt.loadNpmTasks("grunt-contrib-less");
    grunt.loadNpmTasks("grunt-contrib-watch");

    // the default task can be run just by typing "grunt" on the command line
    grunt.registerTask("default", ["watch"]);
};

Expanding on this very basic example should not be too difficult. Start slowly, focus on one task at a time and progressively automate away all your grunt work.