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:
- Builds frontend files, which involves:
- Compiling LESS files
- Combining and minifying CSS files
- Combining and minifying JS files
- Installing frontend dependencies (such as Bootstrap and JQuery)
- Moving font files into the correct locations
- Sets up Git Hooks when the application is initially setup<
- Run tests on the codebase before a commit, and after a merge
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:
Anyway, that’s it for now, I’ll probably update this at some point, so feel free to check back.// 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']);
};