Skip to content

Handling Tasks With Grunt

Posted on:March 26, 2015 at 12:00 PM

So, what’s this Grunt? Basically, it’s a Javascript based task runner, which can be used for a huge range of, well, tasks!

In my use case Grunt performs a few tasks:

Getting Started

First, a very quick walkthrough on how to get Grunt up and running on an Ubuntu system.

This isn’t so much a tutorial, but more a walk through of my setup, for a proper introduction check out [http://gruntjs.com/getting-started] and [http://www.sitepoint.com/writing-awesome-build-script-grunt/]

First, install NodeJS:

sudo apt-get install nodejs nodejs-dev npm

Then, install GruntJS:

sudo npm install -g grunt-cli

At this point, you have Grunt, but to use it you need to add it, and modules to your project, so, in your project directory:

npm install grunt --save-dev

The next step is to find the plugins for the tasks you need, here: [http://gruntjs.com/plugins]

Frontend Build

So, to add the required modules, you can either add them to your package.json file, and run npm install, and it will fetch and install your packages, or you can run commands like:

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

This will install the module, and save the dependency to your package.json file, which is used to made Node.js modules.

This is my package.json

{
"engines": {
"node": ">= 0.10.0"
},
"devDependencies": {
"grunt": "~0.4.5",
"grunt-bower-task": "~0.4.0",
"grunt-composer": "^0.4.4",
"grunt-concat-css": "~0.3.1",
"grunt-contrib-concat": "~0.4.0",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-less": "~0.11.3",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-githooks": "~0.3.1",
"grunt-parallel-behat": "^0.3.6",
"grunt-phpunit": "~0.3.3",
"grunt-shell": "^1.1.1"
},
"scripts": {
"postinstall": "grunt githooks"
}
}

To use it, just create the package.json file, and run npm install, and all of the listed modules will be installed!

Configuring Grunt

Grunt takes its own configuration from Gruntfile.js, which I’ll go through in blocks, starting here, at the top, I’ve skipped over a few bits, but my full Gruntfile.js is here:

At the start, we load our tasks using grunt.loadNpmTasks():

/*global module:false*/
module.exports = function(grunt) {

grunt.loadNpmTasks('grunt-composer');
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-concat-css');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-bower-task');
grunt.loadNpmTasks('grunt-githooks');
grunt.loadNpmTasks('grunt-parallel-behat');

Then we begin our configuration using grunt.initConfig(), which is passed an object containing our configuration:

    // Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>
' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */
',

Next comes the configuration for our individual modules, here are a few examples:

        // Task configuration.
less: {
development: {
options: {
compress: false, // we don't minify the result, we do this later.
},
files: {
//"../app/assets/stylesheets/styles.css": "../app/assets/less/styles.less",
"../app/assets/stylesheets/bootstrap.css": "../app/assets/less/bootstrap.less",
}
}
},
concat_css: {
options: {
// Task-specific options go here.
},
styles: {
src: [
'../app/assets/stylesheets/bootstrap.css',
'./assets/jquery-ui/themes/smoothness/jquery-ui.css'
],
dest: '../public/assets/stylesheets/styles.css'
}
},

The 'watch' task is perhaps the most useful, and interesting, as it can be used while developing to monitor changes in specified files, and trigger a task when changes are detected:

        watch: {
js_frontend: {
files: [
//watched files
'./lib/jquery/dist/jquery.js',
'./lib/jquery-ui/jquery-ui.js',
'./lib/matchHeight/jquery.matchHeight-min.js',
'./lib/bootstrap/dist/js/bootstrap.js',
'../assets/js/scripts.js'
],
tasks: ['concat', 'uglify'], //tasks to run
options: {
livereload: true //reloads the browser
}
},
styles: {
files: ['./lib/bootstrap/less/*.less',
'../assets/less/*.less'
], //watched files
tasks: ['less', 'concat_css'], //tasks to run
options: {
livereload: true //reloads the browser
}
},
fonts: {
files: ['./lib/bootstrap/fonts/*'],
tasks: ['copy'],
options: {
livereload: true
}
}
}
});

Next come the tasks which bind together other tasks:

    // Default task.
grunt.registerTask('default', ['concat', 'uglify', 'less', 'concat_css', 'copy']);
grunt.registerTask('pre-commit', ['composer:install', 'behat', 'phpspec']);
grunt.registerTask('merge', ['concat', 'uglify', 'less', 'concat_css', 'copy', 'behat']);
grunt.registerTask('build', ['bower:install', 'concat', 'uglify', 'less', 'concat_css', 'copy']);

};

Anyway, that’s it for now, I’ll probably update this at some point, so feel free to check back.