Saturday 7 November 2015

A Few Tips on Gulp

I have been using Gulp a lot these days and have encountered a few cases that are a bit tricky to solve if you are new to Gulp. These scenarios get even trickier if you used Grunt before and just started using Gulp later, like myself. This is because, Grunt is synchronous and the execution of a Grunt task that combines a set of tasks is predictable. On the other hand, Gulp is asynchronous and we need to carefully examine the way a combined task runs in order to get maximum benefits out of it.

The asynchronous behavior of Gulp tasks makes it a bit difficult to perform things like chaining multiple tasks, handling tasks not returning a stream work seamlessly and performing multiple operations in a single task. In this post, we will see how to handle these tasks.

Chaining Multiple Tasks
It is common to define multiple tasks and combining them to create a master task. The master task alone is called whenever there is a need to perform the set of tasks together. Some of these tasks may be dependent on each other and others may be independent. In general, a set of Gulp tasks are combined using the following syntax:

gulp.task('build', ['copy-files', 'concat-files', 'run-tests', 'copy-coverage-results']);
Out of these tasks, the task concat-files is dependent on the task copy-files. The above setup starts executing these tasks at the same time. Execution of the task concat-files is not paused until execution of the task copy-files is completed. It can be solved in two ways. One way is to declare the task copy-files as a dependent task on the task concat-files and have the concat-files task alone as dependency on the build task.

gulp.task('concat-files', ['copy-files'], function(){
  //definition of the task
});


The second way is to run these tasks in sequence using the run-sequence package. It is shown below:

var runSequence = require('run-sequence');
gulp.task('copy-and-concat', function(){
  return runSequence('copy-files','concat-files');
});


We have to use one of these ways to solve this case based on the scenario in hand.

Task not returning a Stream
Sometimes we have to deal with tasks that don’t return streams. Such tasks are difficult to chain, as gulp doesn’t understand about their completion. One of such tasks is gulp-requirejs. We need to wrap execution of such tasks inside gulp pipes to make them return streams. Following snippet wraps the gulp-requirejs task inside a pipe:


gulp.src('undefined.js')
  .pipe(requires(/*configuration of the task goes here*/))
  .pipe(gulp.dest('path-of-destiation'));


Task handling Multiple Operations
A task may handle multiple asynchronous operations. In such case, the task has to return a stream representing the combined execution. Sometimes, we can return the stream returned by the last operation alone, but this approach doesn’t guarantee that all tasks are completed by the time the stream is completed. So, it is a good practice to combine such tasks using the event-stream package. Following snippet combines a number of copy operations into a single task using this package:

gulp.task('copy-files', function () {
  return es.merge(
    gulp.src('app/index.html')
      .pipe(gulp.dest('build')),
    gulp.src('app/JSON/*.json')
      .pipe(gulp.dest('build/JSON')),
    gulp.src('app/requirejs-config.js')
      .pipe(gulp.dest('build/temp'))
    );
});


I have encountered these cases quite a few times and the solutions discussed here worked well for me. These may not be the only possible ways to tackle these cases, but these are just my views. If you got any other ways to solve these problems, feel free to add a comment.

Happy coding!