Grunt While You Work

The Problem

The tools for producing web designs are improving every day. We’ve got incredible third party services to handle everything from hosting our content, to social coding. Production tools are also improving. Instead of CSS, we can use Sass to cut down on repetitious selectors. Instead of writing Javascript, which is a little clunky and hard to understand, we can write CoffeeScript. We can use tools to minify images, concatenate our code, and compress our text files, etc. All of these improvements are amazingly useful, but they come at the price of ease of entry. To really get rolling in a production environment, these days you need to know a lot and you need to be able to accomplish these tasks efficiently and effectively.

You might choose to do everything one step at a time, but that can quickly become time consuming, even for the fasted of us. Grunt.js is designed to alleviate some of that production pressure by taking all of those tasks and automating them.

Grunt.js

Grunt.js is a javascript task runner. It’s a bit of javascript code that runs other bits of javascript code. Those other bits handle all of the tasks that we might have to do by hand otherwise, e.g. compressing images, pushing code to the server, staging code and concatenating files, etc. It is a command line tool, but is very simple and easy to set up. I used to avoid the command line at all costs, so those of you reading this thinking, “No frikin’ way”, don’t worry. If I can do it, so can you.

Getting Setup for Grunt Install

To get started you’ll need two things Node.js and NPM. Luckily, NPM comes with Node these days, so you really just have one install. Head over to the Node.js site and click the install button. You’ll download a .pkg file. Run through the installation process and you should be good to go. You can check to make sure you’re install worked properly by opening up up your favorite terminal emulator ( Macs come with Terminal.app, but I prefer iTerm2 ) and typing in ‘which node’. If it spits out a file path, you’ve got a clean install. For me, node is installed at ‘/usr/local/bin/node’.

This isn’t an article about installing Node or NPM, so if this is too vague, check out this article, because you need might need to install some other dependencies to get everything to play nice.

Installing Grunt

Once you’ve got Node and NPM installed, pull up your command-line tool of choice and type in this little bit of code:

    npm install -g grunt-cli

NPM will install the grunt-cli globally so that you can use it anywhere on your computer. Very handy indeed. And that’s it for installing Grunt. Easy-peasy.

Okay, Now What???

To get started with Grunt, you’ll need to know just a couple of things:

1. Grunt uses libraries to run tasks. These libraries are managed and installed via NPM. You’ll use Grunt to coordinate these tasks and run them. These files are configured in npm’s package.json file. More on that in a moment.

2. Grunt uses a file to build tasks and to set up the commands to run them. Appropriately enough, this file is called the Gruntfile.js. You will need to create the Gruntfile in the root of your project.

3. Grunt does magic, so it pretty much happens in the background. Don’t overthink it. Be Zen and go with it for a while.

Alright, to get started with Grunt you first need to identify what you want it to do. Lets say that we want it to concatenate our .js files and to minify our images. It can do a whole lot more, but we’ll start there.

Setting Up a Project to Use Grunt

First, download this .zip file and unpack it to your desktop. In your favorite command line editor ( hereafter referred to as ‘terminal’ ), cd to the folder you just unpacked from the zip called GruntSite.

Next, you’ll need to initialize npm. In terminal, type the following code and press ‘return’:

    npm init

 

npm will ask you a series of questions about information related to the project. Tab through all of the options and leave them at the defaults. That way we’ll be using the same defaults. Once you’ve tabbed through everything, npm will spit out some JSON into the terminal. The JSON is the contents of the package.json file. It should look like this:

}
    {
        "name": "GruntSite",
        "version": "0.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "BSD-2-Clause"
}

 

Now, if you look in finder, you’ll find the package.json file in the root of the GruntSite folder. npm just created that for you. Yeah!

Next, we’ll need to load up the Grunt libraries that we’ll use to concatenate our javascript and to compress our images. Again, we use npm to manage their installation. There are dozens of Grunt libraries for every task imaginable, but we’re going to focus on two of the standard ones found in the ‘grunt-contrib’ suite: grunt-contrib-concat & grunt-contrib-imagemin.

To use these libraries, we load them up through the command line via NPM. Make sure you are still in the root of the GruntSite folder and run these two commands:

    npm install grunt-contrib-imagemin --save-dev

 

    npm install grunt-contrib-concat --save-dev

The commands are fairly self explanatory, but take note that ‘–save-dev’ will write to the package.json file and record the two libraries just installed as dependencies of the project. Your package.json file should look like this now:

{
    "name": "GruntSite",
    "version": "0.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "BSD-2-Clause",
    "devDependencies": {
        "grunt": "~0.4.2",
        "grunt-contrib-imagemin": "~0.4.0",
        "grunt-contrib-concat": "~0.3.0"
    }
}

Note that the versions of your dependencies, e.g. “~0.4.0” or “~0.3.0” might be different from those listed here. It’s not a problem; npm is going to install the latest version of the library, not the specific version that is in this article.

You should also take note of two other things. npm has created a folder in the root of GruntSite called ‘node_modules’. This is where npm stores the libraries that we just installed. You’ll also notice that npm went ahead and installed a third library called ‘grunt’. Automagical stuff indeed. npm manages it’s local installations really nicely; so nicely, in fact, that if you were to drop the package.json file into another directory somewhere else on your computer, cd to that directory in terminal and then run ‘npm init’, npm would recreate the node_modules folder in the new directory. Pretty nifty.

We now have everything in place to start using Grunt! We can now write our Gruntfile and start making magic!

The Gruntfile

Lets start writing some JSON. In the root of your GruntSite folder, create a file called Gruntfile.js and open it in your favorite text editor.

Grunt is fairly straightforward to configure:

1. Loading the libraries so that Grunt has access to them.
2. Configuring each library so that it does what you want it to.
3. Register and name tasks so that they can be called from the command line.

To keep this as simple as possible, I’m going to go ahead and show you the contents of the Gruntfile, after which we’ll walk through it point-by-point.

    module.exports = function(grunt) {

        //2. Project configuration.
        grunt.initConfig({
            //define what to do with the imagemin process
            imagemin : {
                movetodist : {
                    files : [{
                        expand : true,
                        cwd : 'img/',
                        src : [ '**/*.{png,jpg,gif}' ],
                        dest : 'dist/img/'
                    }]
                }
            },

            //define what to do with the concat process
            concat : {
                concattodist : {
                    src : [
                        'js/one.js',
                        'js/two.js'
                    ],
                    dest : 'dist/js/main.js'
                }
            }
        });

        //1. load task libraries for use
        grunt.loadNpmTasks( 'grunt-contrib-imagemin' );
        grunt.loadNpmTasks( 'grunt-contrib-concat' );

        //3. register a named task ( 'process' ) that runs Grunt processes ( 'imagemin', 'concat' )
        grunt.registerTask( 'process', [ 'imagemin', 'concat' ] );
    };

Every Gruntfile has some boilerplate code that makes the code accessible. The contents of the Gruntfile will need to be wrapped with the following code.

    module.exports = function(grunt) {

       //gruntfile code goes here

    };

The rest of the code goes inside the function definition.

1. Load Task Libraries

    ...
    //1. load task libraries for use
    grunt.loadNpmTasks( 'grunt-contrib-imagemin' );
    grunt.loadNpmTasks( 'grunt-contrib-concat' );
    ...

grunt.loadNpmTasks() simply makes the libraries accessible to the Gruntfile. I know it looks out of order, but you need to create the configuration JSON for the libraries to use first.

2. Load task Libraries

Imagemin

Configuring your tasks is really simple. To do so, pass a JSON object containing all of the configuration information to grunt.initConfig(). Each property of the JSON object will correspond to one of the tasks. Our configurations are designed to process images and js and to place them into a new folder called ‘dist’ while maintaining their file structure. We’ll walk through the configuration for imagemin as a for-instance.

    ...
    //define what to do with the imagemin process
    imagemin : {
        movetodist : {
            files : [{
                expand : true,
                cwd : 'img/',
                src : [ '**/*.{png,jpg,gif}' ],
                dest : 'dist/img/'
            }]
        }
    },
    ...

The imagemin task is simple to configure and is representative of most Grunt tasks. First, create a property that matches the task. In this case, the property is ‘imagemin’. This property defines a JSON object that is a list of named variations of the imagemin task. In this case, we’ve created a named tasked called ‘movetodist’. If we run ‘imagemin’, the command will run all of the named tasks, but a specific task can be run with a slightly different command. In this case, ‘imagemin:movetodist’.

The ‘src’ property determines what places imagemin will look to find images to minify and move. The ‘dest’ property tells imagemin where to put the new images. ‘cwd’ just changes the current working directory.

Concat

    ...
    //define what to do with the concat process
    concat : {
        concattodist : {
            src : [
                'js/one.js',
                'js/two.js'
            ],
            dest : 'dist/js/main.js'
        }
    }
    ...

The concat task is much of the same. ‘src’ tells concat what files to concatenate. ‘dest’ defines where to put the concatenated file. Thats it. Blammo.

3. Register tasks

    ...
    grunt.registerTask( 'process', [ 'imagemin', 'concat' ] );
    ...

 

The last thing to do is to tell Grunt how to run your tasks. We want to create a task called ‘process’ that runs ‘imagemin’ and ‘concat’ in turn. Once the task is registered, we can call it from terminal with ‘grunt process’.

Run with Grunt

Now that we have everything set up, we can go ahead and concat our js files and compress our images. In terminal, make sure you are still in the root of GruntSite. Then run ‘grunt process’. Grunt should spit out some info into the console that lets you know what it has accomplished. In this case, it’s compressed two images (saved 59.40 kB for the win) and moved those files to the ‘dist’ folder. Grunt has also run concat and moved the new file to the ‘dist’ folder.

    Running "imagemin:movetodist" (imagemin) task
    ✔ img/astro.jpg (saved 33.51 kB)
    ✔ img/swirl.jpg (saved 25.89 kB)
    Minified 2 images (saved 59.40 kB)

    Running "concat:concattodist" (concat) task
    File "dist/js/main.js" created.

In Conclusion

And there you have it. Grunt can really help speed up your production workflow by automating much of the, pardon the pun, ‘grunt work’ that would otherwise occupy your time. For this tutorial, we just focused on concatenating js files and minifying images, but you can use Grunt to automate almost anything. Processing Sass and CoffeeScript are easy as pie with Grunt. A live reload server can be set up with just a couple lines of code. You can hint your code or set up a watch process to run tasks whenever you save files. It’s kind of amazing.

I encourage you to take some time to review all of the libraries that Grunt.js has available. Go forth and automate all the things!

Author

Giovanni DiFeterici

Giovanni has a BFA in Fine Art & a BS in Graphic Design. He is an accomplished illustrator & designer. He's the go to guy when it comes to developing creative solutions to complex visual problems.