They’re the latest craze, there’s not doubt about it. Everyone’s using them and chances are, if you’re a web developer, you know what they’re on about. Or do you?

Actually, maybe you’re not too sure. Or maybe you’ve been wondering how or why you should use them?

Before I delve into it let me wear my philosopher’s hat for a moment and share a thought with you: What is the real motor for progress? In my opinion it’s our ability to simplify complex tasks and free up time that can be spent building upon previous advances – or simply letting our ingenuity run wild.

I’ll let you digest that for a second or two before I ask you, have you ever wished days were longer so that you could get more stuff done? Maybe you keep finding yourself having to use some redundant process and wishing that it were better streamlined so that it became less cumbersome or altogether hidden?

Enter Grunt and gulp [thunderclap].

When you’re developing an application, running a deployment, optimising images… aren’t there a million things you’d rather be doing than copy/pasting files or repeating the same actions over and over? Task managers allow us not just to streamline these processes but to do a lot more, as we’re about to see.

I’m sure you already know this, but, in case you’ve been hiding behind a rock for the last few years, large scale (and not so large) applications nowadays are increasingly built on top of Node.js and you want to have it installed in your machine to use Grunt or gulp. And there is also an ever-increasing number of tasks to run even for really small changes to your code base; so it was really only a matter of time for someone to come up with some sort of task manager, and that’s just what Grunt and gulp are.

There’s a heated debate about which one of the two is better, but as we’re about to see, they both have their advantages and limitations, and, ultimately, it’s up to us developers to choose the one that suits our project or coding style best. In this post I am going to focus mostly on Grunt but I will also touch upon gulp.

Grunt

One of the best selling points for Grunt.js is the huge community behind it and the plethora of plugins available. As of 3rd of October 2014 there are 3,642 plugins that you can use; and these are just the ones that are officially tagged as Grunt plugins. There may be many, many more.

Numbers this big can become a bit meaningless because they can make you lose perspective. When will you ever need to use even a tenth of that? In any case, this goes to show how vast Grunt is and how many things it’s capable of doing. It is pretty safe to say that if you can think of it, Grunt can do it. And if there isn’t a plugin already there that does what you want then you can write your own and contribute it to the community 😉

Let’s see now how to use Grunt:

The first thing you’re going to want to do is install Grunt in your machine. We’ll use npm (the package manager for Node) to do that and also to add all of the different tasks we’ll be using in our projects.

First things first, type in your terminal:

npm install grunt-cli -g

This will install Grunt’s CLI globally in your system (that’s what the ‐g is for) so that it can be called from any location.

The two files that any project using Grunt must have in the root folder are package.json and Gruntfile.js. The first one is the file that tells npm what dependencies your project has, and the latter is where the magic happens.

In order to create the first, run in a terminal npm init and follow the steps which will generate the package.json file. (You may choose to give the information it asks now or fill it in after you create it with your preferred text editor.)

As this file is basically a list of settings and dependencies that your project has, we need to include Grunt and every other module that we want to install. To do that, run the command:

npm install grunt --save-dev

Tip: the --save-dev is what tells Node to add the package as a dependency for the project.

If you get to work in a project that already has a package.json all you need to do to ensure you have the same dependencies with the correct versions as everyone else is run the commandnpm install. Node will take care of the rest. This command must also be run by all your team whenever there is a change to the package.json file.

Creating a Gruntfile is actually quite simple. All you need to do is write a wrapper function as follows:

  module.exports = function(grunt) {
    grunt.initConfig({
      pkg: grunt.file.readJSON(’package.json’),
      
      // Tasks and subtasks are defined from here on
    });
  };

Once we have our package.json and our Gruntfile.js, let’s start installing modules and registering tasks. Installing tasks is as easy as running, as we saw before:

  npm install <name of the task> -—save-dev

When you start looking for tasks, it might be useful for you to know that tasks prefixed with grunt-contrib- have been developed by the grunt team, so you can be pretty sure that they are well maintained and ‘reliable by default’. This doesn’t mean that the ones that don’t have it are not trustworthy by any means. Much as this prefix is a quality-assurance stamp you should definitely check other ones out too.

Each and every Grunt task is documented in its own page or git repo where you will find the necessary documentation on how to make them do what you want.

A very powerful feature of Grunt is that you can split your tasks into subtasks, allowing you to run a particular task on different files or group of files. Let’s have a look at a quick example using the grunt-contrib-concat task. Here’s our Gruntfile:

  module.exports = function(grunt) { 

    // Project configuration. 
    grunt.initConfig({ 
      pkg: grunt.file.readJSON('package.json'), 
      concat: { 
        js: { 
          options: { 
            banner: 'Combined JS file *n’ // which you can make more dynamic (using <%= … %> syntax) 
          } 
          files: [ 
            {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'}, 
            {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}, 
          ], 
        }, 
        css: { 
          options: { 
            banner: 'Combined CSS file *n’ 
          } 
          files: [ 
            {src: ['src/bb.css', 'src/bbb.css'], dest: 'dest/b.css'}, 
            {src: ['src/bb1.css', 'src/bbb1.css'], dest: 'dest/b1.css'}, 
          ], 
        }, 
      }, 
    }); 
  }; 

If we were to run the ’js’ substask it would only concatenate the Javascript files. If you run the ’css’ one it would have the same result with the CSS files, but if we were to run just ‘concat’, it would run both subtasks.

So how do we actually run a Grunt task?

Before the above task can be run, it needs to be ‘loaded’ within the Gruntfile and default tasks need to be defined. Let’s see the code:

  module.exports = function(grunt) { 

    // Project configuration. 
    grunt.initConfig({ 
      pkg: grunt.file.readJSON('package.json'), 
      concat: { 
        js: { 
          // we’re going to add a comment (banner) at the top of the destination file to specify what the file is 
          options: { 
          banner: 'Combined JS file *n’ // you can make the banner more dynamic using ruby-like syntax (<%= blablabla %>) 
          } 
          files: [ 
            {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'}, 
            {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}, 
          ], 
        }, 
        css: { 
          options: { 
            banner: 'Combined CSS file *n’ 
          } 
          files: [ 
            {src: ['src/bb.css', 'src/bbb.css'], dest: 'dest/b.css'}, 
            {src: ['src/bb1.css', 'src/bbb1.css'], dest: 'dest/b1.css'}, 
          ], 
        }, 
      }, 
    }); 

    // Registered tasks. 
    grunt.loadNpmTasks('grunt-contrib-concat'); 

    // Default tasks. 
    grunt.registerTask('default', ['concat']); 
    grunt.registerTask(‘css’,[‘concat’:’css’]); 
  }; 

Now we have the task configured, loaded and registered, and we have defined what calling our tasks will do. Typing grunt or grunt default in our terminal would run the task ‘concat’; but if we run grunt css we would only run the ‘css’ subtask from the ‘concat’ main task. You’re more than likely to end up with a number of tasks – each one potentially having its own set of subtasks – so it’s totally up to you to define which tasks are run; there is no limit to the amount of tasks that you can add to the array.

One thing to bear in mind is that the tasks will be run in the same order you specify them in the array, so if there are any interdependencies you need to make sure they are listed in the right order.

Pretty simple, isn’t it? That’s the magic of Grunt: you specify a particular configuration for your files, set the tasks to be run, and you can forget about it.

Here are some of my recommended tasks:

  • grunt-rev: Busts your caches. It makes sure your assets are dumped from the cache by prefixing them with a hash that changes every time you run the task, forcing the browser to reload them.
  • grunt-contrib-concat: As we have seen, it looks at some files and concatenates them.
  • grunt-contrib-copy: Copies files and folders from A to B.
  • grunt-contrib-cssmin: Minifies your CSS files.
  • grunt-contrib-sass: Compiles your Sass files into CSS. Perfect in conjunction with ‘grunt-contrib-watch (below). It also allows you to minify the output, which is pretty neat.
  • grunt-scss-lint: Lints your SCSS files and flags up mistakes and warnings.
  • grunt-contrib-jshint: Validates Javascript files.
  • grunt-contrib-uglify: Minifies Javascript files.
  • grunt-githooks: Allows you to run specific tasks triggered by git commits or other hooks; very useful to prevent bad code to be committed.
  • grunt-contrib-watch: Watches for changes on specific files and runs tasks accordingly. This task is a bit special in the sense that it sits in the background and keeps watch until you tell it to stop; even if you have tasks that stop the process when an error is thrown (e.g. linting tasks). Having ‘watch’ still running means that I can go to my problem file, correct the errors, save them and it will pick up the change and re-run the tasks automatically. Nifty!
  • grunt-svg2png: Rasterizes SVGs to PNGs using PhantomJS
  • grunt-remove-logging: Removes all the instances of console.log() from your .js files.

And remember, you can always write plain Javascript in your Gruntfile if you need to!

gulp

And then we have gulp, a task manager that came about slightly later but certainly an admirable contender that is picking up users real quick.

Much in the same fashion as Grunt, gulp can be installed using npm:

  npm install -g gulp

This will install gulp globally in your system giving you access to its CLI. We then need to install it into our project as a dependency using exactly the same command you’ve seen a few times now:

  npm install gulp –-save-dev

The way gulp runs is very similar to Grunt in many ways: you need a gulpfile.js (notice the lowercase) in your root folder where you define your tasks, concatenate tasks in various ways, etc. For many a developer the more method-ical approach that gulp takes which we’re about to see is more intuitive. In gulp we define a chain of actions piping the outcome of an operation on to the next one. It’s that simple. Let’s have a look at a very simplified example using the ’sass’ task:

  gulp.task('sass', function() {
    return gulp.src('src/styles/main.scss')
      .pipe(sass({ style: 'compressed' }))
      .pipe(gulp.dest('dist/assets/css'))
  });

As you can see we are just applying the pipe() method to whatever is output from the previous step, commencing in this case by grabbing the file that we want ’sassyfied’ and finishing defining the destination file.

This example may not make it too obvious, but by streaming and piping tasks in this fashion we get to save resources because we don’t create temporary files. This is actually what happens behind the scenes with Grunt so we can potentially shave off some milliseconds by using gulp. Also, another good point is that it looks clean; Grunt’s configuration could become a bit cumbersome if you need to define many tasks and options.

As with Grunt, we can make use of a huge number of plugins that the community maintains (about 780 at the moment) and to which you can also contribute. These will most likely cover all your necessities for the foreseeable future.

I also find the way we load these plugins to be slightly simpler when compared with Grunt: define them as variables and you’re good to go:

  var gulp = require('gulp'),
      sass = require('gulp-ruby-sass'),
      minifycss = require('gulp-minify-css'),
      jshint = require('gulp-jshint'),
      uglify = require('gulp-uglify'),
      imagemin = require('gulp-imagemin'),
      concat = require('gulp-concat'),
      livereload = require('gulp-livereload');

If this seems like an awful lot of code for you (are you serious?!) you might be interested on gulp-load-plugins.

In terms of downsides, I believe that its coding style, which at times could require some experience with Node.js, may put off some less JS-savvy people. Some people may find the more limited number of plugins a downside, but if you are one of them I challenge you to find a project where you’d need a plugin that cannot be found amongst the 780. (If you do, please let me know, I’m genuinely interested.)

A really cool online tool for trying out your gulp skills or to easily build a gulpfile.js that you can then use in your project is gulpFiction, a visual editor for gulp.

There isn’t much more about it, really. You can find all the intricacies of configuring the different tasks in the tasks’ respective git repos (or npm pages). As both Grunt and gulp are built with the same principles in mind, much of what we’ve said about Grunt applies to gulp too.

Conclusion

In this post we have been looking mostly at how Grunt and gulp work and commented on a few similarities and differences between them. Hopefully I’ve managed to grab your interest enough that you’ll want to try one (or both) out.

A word of warning, though: use with care! Task managers are highly addictive. Once you start unveiling their potential you might even start using them to streamline processes outside of your development projects; managing files in your computer, filtering emails, generating document templates… Their potential is vast, so I’ll leave it to you to judge when you might be overdoing it 😉

Author: Yago de la Torre.