Top 10 things I like about Gulp

2013, Feb 10

Gulp has been my favourite tool for almost a year now. It’s great in so many ways and for so many purposes. I'll list in this article the Top Ten Things I like about Gulp.

New to Gulp? Read this Getting started with Gulp first! It is a task runner built on NodeJS that can enhance your productivity over time dramatically! Who wouldn't like that?!

1: Plugin ecosystem

plugins-everywhere.png

Gulp use pluggable architecture. It has hundreds of Plugins are really simple and easy to use.

My top favourite plugins are:

  1. gulp-load-plugins
  2. browser-sync
  3. gulp-autoprefixer

However, that's not all! there are other plugins for linting, validation, static site generation, deploying over FTP to AWS S3, documentation generators, and many more.

2: Simple API but unlimited use-cases

Out of the box, Gulp exposes just four simple methods:

  • gulp.task
  • gulp.watch
  • gulp.src
  • gulp.dest

With these four you can have a very flexible build process piped together. Many combinations if you include plugins to the mix.

3: Don't stop at `gulp.dest

// gulpfile.js
var gulp = require('gulp')
var rename = require('gulp-rename')

gulp.task('md-files', function () {
  gulp
    .src('*.md')
    .pipe(gulp.dest('./output'))
    .pipe(gulp.dest('./another'))
    .pipe(
      rename({
        extname: '.txt',
      })
    )
    .pipe(gulp.dest('./yet-another'))
})

This is a simple but noteworthy one as it's not immediately obvious to everyone. You can save files to disk as many times as you'd like in a pipeline.

4: Load Plugins at once

gulp-load-plugins is my top favourite Gulp plugin. Usually, gulpfile tend to get crowded with decent requires on top. Basically, gulp-load-plugins aggregates gulp-plugins from package.json as method into a single object.

{
  "devDependencies": {
    "gulp": "",
    "gulp-load-plugins": "",
    "gulp-if": "",
    "gulp-replace": "",
    "gulp-debug": ""
  }
}
// gulpfile.js
var gulp = require('gulp'),
  loadPlugins = require('gulp-load-plugins'),
  plugins = loadPlugins()

gulp.task('rewrite', function () {
  gulp
    .src('./input/*')
    .pipe(plugins.if(false, plugins.replace('src', 'target')))
    .pipe(gulp.dest('./'))
    .pipe(plugins.debug())
})

5: Gulp stream is pretty flexible

You'll inevitably need a bit of flexibility in your tasks. For example, apply manipulations exclusively to a subset of files within a stream;

var gulp = require('gulp'),
  loadPlugins = require('gulp-load-plugins'),
  plugins = loadPlugins()

gulp.task('default', function () {
  var pngFilter = plugins.filter('*.png')

  gulp
    .src('./images/*')
    .pipe(gulp.dest('./output/images'))
    .pipe(pngFilter)
    // ...
    .pipe(gulp.dest('./output/raster'))
    .pipe(pngFilter.restore())
    .pipe(
      plugins.filter(function (file) {
        return file.contents.toString('utf8').length > 500
      })
    )
    // ...
    .pipe(gulp.dest('./output/other'))
})

gulp-filter accepts a single glob, an array of globs or a function returning Boolean. It's possible to use multiple filters per pipeline, you can restore or undo the filter at any point.

gulp-if accepts a "condition" in the form of a Boolean, or a function which returns one (like gulp-filter).

var gulp = require('gulp'),
  loadPlugins = require('gulp-load-plugins'),
  plugins = loadPlugins()

gulp.task('default', function () {
  var isLong = function (file) {
    return file.contents.toString('utf8').length > 500
  }

  gulp
    .src('./*')
    .pipe(plugins.if(isLong, gulp.dest('./output'), gulp.dest('./output2')))
})

6: Concurrency, done right!

Gulp uses a Scheduler underneath, which sequence, execute tasks and dependencies in maximum concurrency. Dependencies run in parallel, however, it depends. As long as you've a flattened dependency graph, as much as possible, no need to worry. Question is, how can we achieve that?

Consider tasks in the following example:

gulp.task('build', ['frontend']);

gulp.task('frontend', ['styles', 'scripts', 'assets']);

gulp.task('styles', ['css', 'vendor-styles']);
gulp.task('css', function(){ ... });
gulp.task('vendor-styles', function(){ ... });

gulp.task('scripts', ['app-scripts', 'vendor-scripts']);
gulp.task('app-scripts', function(){ ... });
gulp.task('vendor-scripts', function(){ ... });

gulp.task('assets', function(){ ... });

First look it looks nice, but with a closer look, you'll notice that no need to have the frontend as a decorator here. It's an additional node in gulp's dependency graph. Simplify things and remove it!

gulp.task('build', ['styles', 'scripts', 'assets']);

gulp.task('styles', ['css', 'vendor-styles']);
gulp.task('css', function(){ ... });
gulp.task('vendor-styles', function(){ ... });

gulp.task('scripts', ['app-scripts', 'vendor-scripts']);
gulp.task('app-scripts', function(){ ... });
gulp.task('vendor-scripts', function(){ ... });

gulp.task('assets', function(){ ... });

So always take another look on your gulp file, removing any complex task dependencies to flatten the dependency graph as much as possible.

7: Neat debugging experience

It's possible to pipe gulp-debug anywhere.

debugging.png

For example, to see which files passed down the stream now and then. By checking the logs you'll find varying levels of details about the files depending on the plugin options passed.

var gulp = require('gulp'),
  loadPlugins = require('gulp-load-plugins')
plugins = loadPlugins()

gulp.task('build', function () {
  gulp
    .src('./abc/**/*.png')
    .pipe(
      plugins.debug({
        title: 'Before filter',
      })
    )
    .pipe(plugins.filter('*@3x.png'))
    .pipe(
      plugins.debug({
        title: 'After filter',
      })
    )
})

8: Yeoman is history

Yeoman used scaffolding projects from a community created "generator".

With Gulp plugins you can copy files, prompt the user same as Yeomen. The streaming scaffolding system (SLUSH) is using Gulp as Yeoman replacement. A Slush generator is just a globally installed module with a slushfile which can be a simple gulpfile.

var gulp = require('gulp')

gulp.task('default', function () {
  gulp.src(_dirname + '/**/*').pipe(gulp.dest('./'))
})

9: Flexibility means developer experience

From all the above points its clear that you can do a lot with gulp. Creating tenth and hundreds of gulp pipeline possibilities. It's customizable according your team and project requirements.

For example, a common task is watching for sass changes, compile with css-preprocessor, and sync with the browser using browsersync.

var gulp = require('gulp')

gulp.task('watch', ['browserSync', 'sass'], function () {
  // watchers
})

10: All in a single file

Gulp file, long or short, is a single source of truth contains all your pipeline. That's powerful when you're new to a project and want to understand available pipelines for you. so you can hit the ground running; especially big projects.

Of course the file can be split since it's just JavaScript at the end. However, I find it more convenient to use a single file and don't branch into other file. When you need to do that, ask yourself; Would this logic or task be a good candidate for a custom gulp plugin?

Conclusion

That was my Top Ten Things I like about Gulp. Big and expanding plugin ecosystem and powerful pluggable architecture. Sky is the limit when you use gulp to speedup things in your workflow.

Thank you for reading! Give Gulp a try if you haven't already and shout out to me with how it goes.