Gulp is better than Grunt — Like, objectively

Let me ask you a question. Would you like to use functional Javascript to build your Javascript projects? Or would you like to use some weird, ambiguous JSON-formatted configuration file to have some mysterious, underlying system build your project with processes that may or may not be efficent (who knows? you don’t!)? Answer: Functional Javascript to build Javascript is better.

So, this isn’t going to be another one of those articles comparing and contrasting the benefits of each tool and letting you decide which is best for your particular situation. No, this is going to be an article about how to use Gulp (the better tool) the right way (my way)! My grandfather used to tell me “If you want something done right, do it yourself.” So don’t blame me for being opinionated. It’s my grandfather’s fault.

TL;DR: A Working Example

This is a fairly lengthy post, so if you get bored and/or have ADHD like me… feel free to skip ahead and look at the working demo on gitub.

A Simple Gulp Task

Let’s start off with a bare minimum gulp task example. This is the most basic view of a gulp task that I can think of, so understand this and you can easily build on the concept as we go.

1
2
3
4
5
6
7
8
9
10
11
12
// my-awesome-task.js

'use strict';

var gulp = require('gulp');

gulp.task('my-awesome-task', function() {

  // Do stuff

  return true;
});

So as you can see in the above example, we require gulp (presumably after running npm i --save-dev gulp) and we define a gulp task called “my-awesome-task”. Where the “Do stuff” comment is located, you would write Javascript and logic and stuff and make cool things happen.

Make It Do Something

Since we’re going to make our gulp task do something, let’s make it do something that you should actually be doing in all of your projects already anyway. Let’s check for security vulnerabilities in all the npm modules in your project. Thankfully, the fine folks at the Node Security Project created a really cool gulp module that will do this for you automagically. gulp-nsp can be installed by running npm i --save-dev gulp-nsp in your console in your project root. The gulp task to run the check would be as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// security-check.js

'use strict';

var gulp            = require('gulp');
var securityCheck   = require('gulp-nsp');

gulp.task('security-check', function(callback) {

  // Define callback function if null/undefined
  callback = callback || function() {};


  // Provide the path to your package.json file
  return securityCheck('./package.json', callback);
});

So here’s what we did:

  1. We required the modules that the task will use (gulp and gulp-nsp)
  2. gulp-nsp requires a callback to be passed as an argument, so we added a quick safety net in case it doesn’t exist for some reason (line 11)
  3. We gave the NSP module the path to the package.json file (line 15)

Yeah, it’s that easy. Pretty rad, eh?

So once that task is created, you can run gulp security-check in your console in the root of your project and gulp will do its thing… and gulp-nsp will do its thing… and you will be certain that your third-party app code is safe. You’re still on your own with your non-npm-module security though, so don’t screw the pooch! — well, not entirely true… you can use the same project (just not the gulp version) to run a check on your own code.

Default Gulp Task

Next let’s look at the default gulp task and what we can do with it. Basically, the idea is that if you have multiple gulp tasks, you wouldn’t want to have to type out each task individually like gulp my-task-1; gulp my-task-2; gulp my-task-3 etc. The default gulp task allows us to just type gulp and it will kick off a default task that will kick off multiple tasks. You would set that up as follows:

1
2
3
4
5
6
7
// default.js

'use strict';

var gulp = require('gulp');

gulp.task('default', ['my-task-1', 'my-task-2', 'my-task-3']);

So we named our task “default” and then we gave it an array of tasks to run. Now in the root of your project you can type gulp in the console and it will run the default gulp task whose job is to run 3 other gulp tasks. Now by default, this will kick off all 3 gulp tasks at the same time and run them in parallel. Sometimes that is good, but usually you need more control over task sequences. Enter “run-sequence” for gulp.

Gulp Run Sequence

As of the time of this article, gulp is in version 3.9.0. Gulp version 4.0 is spec’d to provide configuration for running tasks in series or parallel… but until then, run-sequence is required to achieve this functionality.

So let’s just say we have 6 tasks that we want to run each time we run our default gulp task.

  1. Security check
  2. Code linting
  3. Stylus build
  4. CoffeeScript build
  5. Jade build
  6. Development server start

If we ran the regular gulp default task, it would kick off all 6 tasks at the same time and most certainly cause things to blow up as the server would probably start before the previous 5 tasks were complete. So let’s use run-sequence to solve that problem. As always, we need to install the module, so run npm i --save-dev run-sequence in the console in the root of your project. And here is what our gulp task should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// default.js

'use strict';

var gulp            = require('gulp');
var runSequence     = require('run-sequence');

gulp.task('default', function(callback){

    // just a little insurance to keep things moving along
    callback = callback || function(){}

    // series tasks are passed as strings
    // parallel tasks are passed as an array of strings
    runSequence('security-check', 'lint', ['stylus-build', 'coffee-build', 'jade-build'], 'start-server', callback);

});

Looking at line 15 above, there are arguments passed as strings as well as an argument passed as an array of strings. The single string arguments will run in series and the array of strings will run in parallel. The above example dictates that the security check task will run first. When that is done, the code linting task will run. When that is done, the stylus build, coffeescript build and jade build tasks will all run at the same time. When all 3 of those are done, the server will start. You can have any number of series or parallel tasks in any order. So, for example, if you wanted to run parallel, series, parallel, parallel, series, series, series, parallel, parallel, parallel, etc. — no problem.

A Note About Efficiency

At the beginning of this article, I made fun of grunt and mentioned having more control over your build system by using gulp. Let’s talk about a good example and combine a helpful tip for writing some common gulp tasks.

React has taken the Javascript world by force with the unorthodox embedding of markup into Javascript. Distracting developers away from the fact that they require an unorthodox approach, Reacters point to the speed and efficiency gains of React over other view renderers (<– is that a word?). Without going into the nerdy details of Virtual DOM diffing and such (which I don’t understand anyway), react achieves this speed by only updating the portion of the DOM that has changed since the last change happened. So if only 1 character in the DOM has changed, that’s the only thing that gets updated.

Contrast this process with something like AngularJS which updates the entire DOM regardless of the scale of the change, and its easy to see why React is so much faster.

Or is it? Unrelated interesting read about React evangelists possibly not being completely thorough in their speed examples.

Ok, back to Gulp. I wrote all that to write this… We can apply the same basic idea of only updating the changes that have happened instead of all of the things, all of the time. We achieve this by using 1 of 2 really helpful gulp modules. They are gulp-newer and gulp-changed. You can read this nice little Stack Overflow post outlining the differences between the 2 modules, but they are basically the same (not exactly, obviously, but close enough for the purposes of this section of an article on gulp efficiency).

Let’s look at a quick example of what it looks like to use gulp-changed in a jade building task.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// jade-build.js

'use strict';

var gulp            = require('gulp');
var jade            = require('jade');
var changed         = require('gulp-changed');

gulp.task('jade-build', function(){

    return gulp.src('src/**/*.jade')
        .pipe(changed('app', {extension: '.html'}))
        .pipe(jade())
        .pipe(gulp.dest('app'))

});

This example was pretty much copied from the gulp-changed repo, but let me explain what’s happening. As always, we require the modules we need at the top. We pass the source directory (the unbuilt files of our project) to gulp.src as an argument — as seen on line 11 (basically, anything inside the src directory with a file extension of “.jade”). Then we pipe in changed (line 12) and tell it to look at the html files in the app directory as the comparison (since jade compiles to html). We then run the actual jade build step on line 13, giving it only the files that need to be updated (because we defined that before the jade build ran). Lastly, we use gulp.dest to output the results to the ‘app’ directory. That’s basically it. For larger projects, this will end up saving you lots of time during your build — especially during active dev sprints… do people still use the word “sprint”? That feels very 2008 to me.

One other note: make sure to take a look at the hasChanged flag for gulp-changed as that is one of the bigger differentiating factors between gulp-changed and gulp-newer. It basically switches from doing file time-stamp comparisons to using SHA1 hashes to determine if a file has changed.

Organize Your Gulp Tasks

Finally, let’s look at the best way (my way) to organize your gulp tasks in a real-life project. Hopefully you are writing small javascript modules in your projects instead of a single monolithic JS file with dozens (hundreds?) of functions. If not, I’m just going to assume you are reading this article because you know that’s the wrong way to do it and you want to do it the right way. So just like that’s the wrong way to organize your app, its also the wrong way to organize your gulp tasks. Instead of multiple gulp tasks inside a single gulpfile.js file, I’m going to show you how I break things up and still get everything to build like a boss.

The directory structure

You can see a larger example of my project structure in this article, but here are the basics:

# in the project root

gulpfile.js

|-- gulp

    index.js

    |-- tasks

        default.js
        dev-server.js
        jade-build.js
        linter.js
        security-check.js
        stylus-build.js
        unit-test.js

    |-- util

        javascriptFilter.js

In the example above you can see there is a gulpfile.js file and a gulp directory in the project root. Let’s look at gulpfile.js first since its the simplest.

1
2
3
4
5
// gulpfile.js

'use strict';

require('./gulp');

Ha! Yeah, that’s it. Gulp looks at the project root for gulpfile.js, so all we’re doing here is putting all the contents of our gulp directory into this file so gulp thinks we were bad developers and put everything in that one file. Moving right along…

The gulp directory

Now that our gulpfile knows about the gulp directory, let’s move one level down the tree and look at ./gulp/index.js which is really all our gulpfile.js knows about (it has no concept of sub directories).

1
2
3
4
5
6
7
8
9
10
11
// ./gulp/index.js

'use strict';

var fs          = require('fs');
var onlyJS      = require('./util/javascriptFilter');
var tasks       = fs.readdirSync('./gulp/tasks/').filter(onlyJS);

tasks.forEach(function(task) {
    return require("./tasks/" + task);
});

I’m not going to go into details about fs and fs.readdirSync and all that because that’s not what this article is about. But let’s just say the point of this file is to load all the .js files from the tasks directory (which is then loaded into the gulpfile.js one level up).

Let’s take a quick look at ./util/javascriptFilter.js since we can’t just ignore the fact that it is there.

1
2
3
4
5
6
7
8
9
// ./gulp/util/javascriptFilter.js

'use strict';

var path = require('path');

module.exports = function(name) {
  return /(\.(js)$)/i.test(path.extname(name));
};

Again, without going into unrelated details, the purpose of this file is to guarantee that only files with a .js extension are loaded. This protects against things like .DS_Store files (common to OS X) and other files that could blow up the task builder.

So now we can include any one-off gulp tasks in the ./gulp/tasks directory and it will get loaded into the project. So, like we looked at earlier, running gulp in your project root will now just work.

Conclusion

That might have been a longer article than it needed to be, but I wanted to document exactly how I organize and write my build scripts. Remember, Gulp is objectively better than Grunt, so put that in your pipe and smoke it!

~ Peace!