Using Grunt with Grunt plugins

 
 
 

This is a basic overview of using Grunt with Grunt Plugins. Grunt is a task runner which in a nutshell runs repetitive tasks like compiling, minifying and concatenating stuff for you, it has a great library of plugins and most of the common tasks have already been written. If you are still trying to get your head around what Grunt is then there is some great information over on the Grunt website.

Getting Started

Firstly you will need to install Node and npm (node package manager), I won't go into how to do that here as there is plenty of information on the Node.js website. I recommend using Homebrew if you are running a Mac as that will do most of the install for you and manage your updates.

Install your Node modules

Create a package.json file in the root of your project, in this file we are going to specify all the node modules that we need to install, my base package.json usually starts with:


{
    "name": "Your project name",
    "version": "0.0.1",
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-watch": "~0.5.3",
        "grunt-contrib-compass": "~0.8.0",
        "grunt-contrib-uglify": "~0.4.0"
    }
}

Grunt is the only dependency you really need in the package.json file as that is the actually task runner, you don’t need to start with the other 'contrib' modules (also known as Grunt plugins) if you don’t need them, I install these to start with because I know I will be using SASS with Compass as my SASS compiler, Ugilfy to concatenate and minify my JS files and Watch to look for changes and compile as I develop. By the way I will be using Uglify later in this article so if you are following along make sure you at least install that one.

Next open terminal and run npm install from your project directory:


$ cd /my/project/directory
$ npm install

Let npm install all your modules for you.

Create your Grunt tasks file

Next we need to create a file called Gruntfile.js and place that in the root of your project. In this file we are going to add our tasks. As a minimum you'll need the below wrapper:


module.exports = function(grunt) {
    grunt.initConfig({

        //Your task and options here

    });

    //Trigger your tasks via the command line $ grunt
    grunt.registerTask('default', ['your_task_name']);
};

This includes a 'Config' section where we can add our tasks and it's options. The line:


grunt.registerTask('default', ['your_task_name']);

Is the command line task that we can run from the terminal, this one is named 'default' which makes it the default task that will run if no named task is given, i.e. if you run:


$ grunt

At the moment this will do nothing as we have not specified any tasks for that command to run. So lets add a task and use one of the Grunt plugins we installed earlier:


module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        //Uglify - minify and concat JS files
        uglify: {
            js: {
            options: {
                sourceMap: true,
                sourceMapIncludeSources: true,
            },
            files: {
                '/js/app.min.js': [
                        'js/app/app.js',
                        'js/app/another_file.js'
                    ]
                }
            }
        },

    });

    //node modules
    grunt.loadNpmTasks('grunt-contrib-uglify');

    //Trigger your tasks via the command line $ grunt
    grunt.registerTask('default', ['uglify']);

}; 

Here we have created an Uglify task which will concatenate and minify the two js files and compile it into a new app.min.js file. This task is triggered when we run grunt from the command line as our ‘default’ task now indicates that we want to run the Uglify task, we have also told grunt about the node module (grunt plugin) with the lines:


//This tells grunt more about the installed packages
pkg: grunt.file.readJSON('package.json'),

//This tells grunt about the install plugin that we want to use
grunt.loadNpmTasks('grunt-contrib-uglify’);

Its the node module (grunt plugin) which contains the guts of the Uglify task, what we have specified is the options for that task. For more info on the grunt plugin options, checkout the plugins page on the grunt site.

Paths

I like to include a paths section at the top of my file which will indicate where all my assets are, I can then use the paths in the rest of your grunt file so that the file is a lot easier to manage if anything changes, here is the same Uglify with more manageable paths:


module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Document paths
        paths: {
            httpdocs : 'httpdocs/foundation',
            bower : '<%= paths.httpdocs %>/bower_components',
            js : '<%= paths.httpdocs %>/js',
            css : '<%= paths.httpdocs %>/stylesheets',
            scss : '<%= paths.httpdocs %>/scss',
            img : '<%= paths.httpdocs %>/img',
            fonts : '<%= paths.httpdocs %>/fonts'
        },


        uglify: {
            js: {
                options: {
                    sourceMap: true,
                    sourceMapIncludeSources: true,
                },
                files: {
                    '<%= paths.js %>/app.min.js': [
                        '<%= paths.js %>/app/app.js',
                        '<%= paths.js %>/app/another_file.js',
                    ]
                }
            }
        },

    });

    //node modules
    grunt.loadNpmTasks('grunt-contrib-uglify');

    //Trigger your tasks via the command line $ grunt
    grunt.registerTask('default', ['uglify']);

}; 

Wrap up

So thats about it, now just add all the tasks that you want to run and be sure to check the grunt plugins page for more plugins and details on the options to configure them. Heres my finished boilerplate using Compass, Uglify and Watch which I start most projects with:


module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

            //------------------------------------------------>
            // Document paths
            //------------------------------------------------>
         
            paths: {
                httpdocs : 'httpdocs/foundation',
                bower : '<%= paths.httpdocs %>/bower_components',
                js : '<%= paths.httpdocs %>/js',
                css : '<%= paths.httpdocs %>/stylesheets',
                scss : '<%= paths.httpdocs %>/scss',
                img : '<%= paths.httpdocs %>/img',
                fonts : '<%= paths.httpdocs %>/fonts'
            },

            //------------------------------------------------>
            // Compass - compass config stuff
            //------------------------------------------------>
         
            compass: {
                dist: {
                    options: {
                        sassDir: '<%= paths.scss %>',
                        cssDir: '<%= paths.css %>',
                        imagesDir: '<%= paths.img %>',
                        javascriptsDir: '<%= paths.js %>',
                        fontsDir: '<%= paths.fonts %>',
                        outputStyle: 'compressed',
                        environment: 'production',
                    }
                }
            },

            //------------------------------------------------>
            // Uglify - minify and concat JS files
            //------------------------------------------------>

            uglify: {
                js: {
                    options: {
                        sourceMap: true,
                        sourceMapIncludeSources: true,
                    },
                    files: {
                        '<%= paths.js %>/app.min.js': [
                            '<%= paths.js %>/app/app.js'
                        ]
                    }
                }
            },

            //------------------------------------------------>
            // Watch - Watch for changes
            //------------------------------------------------>

            watch: {
                grunt: { 
                    files: ['Gruntfile.js'] 
                },

                compass: {
                    files: '<%= paths.scss %>/*.scss',
                    tasks: ['compass']
                },

                js: {
                    files: ['<%= paths.js %>/**/*.js'],
                    tasks: ['uglify']
                }
            }

    });

    //node modules
    grunt.loadNpmTasks('grunt-contrib-compass');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-uglify');

    //------------------------------------------------>
    // Grunt tasks
    //------------------------------------------------>

    // $ grunt build 
    // Just runs compass and uglify as a one off
    grunt.registerTask('build', ['compass', 'uglify']); 

    // $ grunt 
    // Runs the build task above and then runs watch which will look for changes
    grunt.registerTask('default', ['build', 'watch']); 

};