Gulp All the Things

I've thoroughly enjoyed using Grunt as a base layer to my web development process over the last year. Applying tasks to smaller, more menial things like linting JavaScript and compiling Scss, as well as using it as the main build process for a massive e-commerce web app I've worked on. It's worked great for both.

However, over the last couple months a new build system has emerged - Gulp. There have been quite-a-many to sing it's praises in the "code over config" chorus. While i'm not as powerful as user as some, there are a few simple, obvious differences baked into Gulp that make it much more usable right off the bat.

Setup

The first major difference is setup. Grunt tries to follow Nodes module.exports object definition. To many, this is the main problem with Grunt. It reinforces a separation of concerns, but ultimately becomes cumbersome and tedious to maintain. Here's an example gruntfile.js -

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    exampleTask0: {
      options: {
        foo: 'bar'
      },
      dist: {
        foo: 'bar'
      }
    },
    exampleTask1: {
      options: {
        foo: 'bar'
      }
    }
  });

  // load the tasks
  grunt.loadNpmTasks('exampleTask0')
  grunt.loadNpmTasks('exampleTask1')
};

Indeed, it's not incredibly tedious, but the idea is that Grunt is moving through each task in a separated manner. Say your minifying, linting, exporting. All that happens in separated tasks, instead of one more focused task.

Enter the Streams - Gulp takes a different approach and is built around Node's definition of Streaming. Here's an example gulpfile.js -

var gulp = require('gulp')
var task = require('taskName')

gulp.task('task', function() {
  gulp.src('/look/for/file/here')
    .pipe(task({options: 'beSimple'}))
    .pipe(gulp.dest('/put/file/here'));
});

gulp.task('default' function () {
  gulp.run('task');
});

Overall, the ideas behind streaming help tasks become clearly separated yet mutually interconnected as the .pipe method works its magic. Simply by looking at the two config examples, it's easy to tell that Gulp requires less work to setup. A big reason it's incredibly helpful for projects big and small.

Watch Baked In

There could be long list about the primary differences between these Grunt and Gulp, I'd to to dig deeper into just one more. Watch.

Gulp takes a big divergence in watching by baking in a method to call on any task. The .watch function does much in making gulp require less config and easier to work on any task. Setting up a file or glob watch in Grunt required the installation of a separate task, typically grunt-config-watch, Gulp removes this by making it part of the system. For example, setting up a watch for Scss compilation is mega easy, here's the task i'm using on this site to compile and watch sass.

'use strict'

// Define Tasks
var gulp = require('gulp');
var sass = require('gulp-sass');

// Compile Scss
gulp.task('sass', function () {
  gulp.src('./public/scss/main.scss')
    .pipe(sass({includePaths : ['./public/scss/']}))
    .pipe(gulp.dest('./public/css'));
});

// Default Task
gulp.task('default', function () {
  gulp.run('sass');

  // Watch
  gulp.watch('./public/scss/**/*.scss', function () {
    gulp.run('sass');
  });
});

As a designer, being comfortable with a build/task system is really helpful. It makes your toolset a bit more handy and gives you lots of flexibility when working on various projects. I'd definitely recommend getting into Gulp. Here's a great screencast giving a more in depth look at getting started.

If you're looking for a deeper look into the differences and bigger philosophy behind Gulp, here's a great read.

Posted January 2014