Press "Enter" to skip to content

Why use gulp?

admin

rocketborder3rev

gulp is a build system as compared to a task runner, such as Grunt, although for all intents and purposes both tools can be used to accomplish the same result. gulp differentiates itself by focusing on optimized file operations with a limited Application Programming Interface (API) and strict set of plugin authoring guidelines.

gulp performs file operations where Grunt tasks may refer to any type of operation, including running shell commands. Although gulp does use the term task, it refers to a grouping of plugins to execute. In addition, gulp favors code over configuration as builds are configured using code, providing access to common coding techniques and libraries.

Common file operations are copying, moving, renaming, compiling, transpiling, concatenating, and minifying. These are all canonical gulp use cases.

A typical non-file related task, on the other hand, may be to commit source code to a source code repository. A gulp plugin written to do this would need to use the gulpfriendly keyword as it does not pass gulp’s plugin guidelines. Nevertheless this activity may be desired as part of the developer’s workflow and can be integrated into the build system.

Grunt was developed to handle all types of tasks, and it’s API suffers bloat as a consequence. In comparison, gulp chose to focus on a few key areas and limit the API, making it easy to grasp for newcomers.

Build Tool Comparison

PastedGraphic-1

Easy to learn

With just four methods, gulp’s minimal API provides for quick adoption and configuration. Plugin authors are also encouraged to adopt a minimal API with gulp’s development guidelines. Let’s take a look at the four methods along with their options.

gulp.task

gulp.task defines a task using Orchestrator. A task contains the operations you want to execute, where the operations are commonly provided by plugins.

gulp.task(name[, dependencies], function () {
  // perform operations
});
name
The name of the task. The task name will be accessible via the CLI, so make it something meaningful. For example, scripts or optimize or minify.
dependencies
An optional array of tasks to be completed prior to your task. Orchestrator ensures that dependent tasks are executed before this task. The dependencies are also executed in parallel to ensure maximum efficiency. This is another key differentiator over Grunt.
function
The function that performs the task’s operations. The function typically follows the pattern of collecting files from the file system, performing some operations provided by plugins, and writing the result back to the file system. We’ll take a closer look at this later.

The following example defines a task named template with a dependency on the minify task. The minify task will be executed and completed prior to the template task.

gulp.task('template', ['minify'], function () {
  // perform operations
});

gulp.src

gulp.src will often be the first operation to perform within the task function as it collects the files from the file system on which your task will operate. It takes a file matching pattern (glob), representing the file structure to read, and returns a readable stream.

gulp.src(glob[, options]);
glob
The node-glob file pattern to read. The glob simply refers to pattern matching based on wildcard characters.
options
An optional parameter to further define file criteria, such as the Current Working Directory (cwd) or whether your task needs the contents of the file read from the file system. Occasionally you may just need the file path and not the contents.

The following example collects all files with the coffee file extension located in the scripts directory or any of its subdirectories.

gulp.src('**/*.coffee', {cwd: 'scripts'});

gulp.dest

gulp.dest writes files to the specified path and is commonly the last step in the task function.

gulp.dest(path);
path
The output directory path.

The following example writes the output files to the dist directory.

gulp.dest('dist');

gulp.watch

gulp.watch performs one or more tasks when specified files change. To avoid manually rerunning the build tasks after the developer makes changes, the watch method can be configured to do this automatically by executing certain tasks when particular files are changed. The watch method plays a large part in overall developer productivity.

gulp.watch(glob[, options], tasks);
glob
The node-glob file pattern that indicates the files to monitor for changes.
options
An optional parameter to further define file criteria, such as the Current Working Directory (cwd).
tasks
An array of tasks to be executed when a file changes.

The following example runs the coffee task when any file with the coffee file extension located within the scripts directory or any of its subdirectories changes.

gulp.watch('**/*.coffee', {cwd: 'scripts'}, ['coffee']);

That’s it to the gulp API, just four methods. Define the task, get files on which to operate, write files back to the file system, and watch for file changes on which to perform more operations. Now that we know the API, let’s dig in a bit deeper and show how to use it.

Easy to use

Node.js has quickly become a widely used server-side runtime environment. Node.js runs on Windows, Linux, and Mac, and since Node.js applications are written in JavaScript, front-end developers are often up and running with relative ease.
New patterns and techniques have emerged with Node.js, such as CommonJS, to help alleviate the complexities of managing application dependencies. For example, the following code gets a handle to the coffee-script dependency using the CommonJS require syntax and executes the compile function on a snippet of code.

var coffeeScript = require('coffee-script');
var code = 'console.log "I am CoffeeScript code"';
var javaScript = coffeeScript.compile(code);

All that’s necessary is to use the require method to grab the dependency’s exposed functionality. Gulp saw no need in reinventing the wheel, but instead wanted to leverage the coding techniques of Node.js development and keep it simple. Grunt, on the other hand, introduced their own form of configuring tasks.

To demonstrate, the following build configurations will compile CoffeeScript files to JavaScript and minify the result. Let’s first take a look at the Grunt implementation.

Grunt example

module.exports = function (grunt) {
  grunt.initConfig({

    coffee: {
      compile: {
        files: [{
          cwd: 'src',
          src: '**/*.coffee',
          dest: 'temp',
          expand: true,
          ext: '.js'
        }]
      }
    },

    uglify: {
      minify: {
        files: [{
          cwd: 'temp',
          src: '**/*.js',
          dest: 'dist',
          expand: true
        }]
      }
    },
  });

  grunt.registerTask('default', [
    'coffee',
    'uglify'
  ]);

  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-uglify');
};

The coffee and uglify object properties passed into the grunt.initConfig method provide Grunt the details of how to execute these plugins. Notice the use of grunt.loadNpmTasks. This is the Grunt style of pulling in dependency functionality in lieu of CommonJS’s widely used require.

Also there is no readily-available ability to perform operations outside of a Grunt plugin or task. For instance, the developer may need to execute some functionality on the compiled CoffeeScript but before the uglify step kicks in.

Can you identify all of the tasks in the Grunt configuration? You can see the registerTask method and can assume default is one of them. But did you see the other two? Both coffee and uglify are tasks as well. With gulp, on the other hand, a task is a task.

Now let’s configure gulp to do the same thing as above.

gulp example

var gulp = require('gulp');
var coffee = require('gulp-coffee');
var uglify = require('gulp-uglify');

gulp.task('default', function () {
  return gulp.src('**/*.coffee', {cwd: 'src'})
    .pipe(coffee())
    .pipe(uglify())
    .pipe(gulp.dest('dist'));
});

The gulp configuration looks like any other Node.js file. In fact, gulp itself is included as a dependency using the CommonJS require method, as are CoffeeScript and Uglify. And since gulp configuration is code instead of a configuration object, like Grunt, all common development privileges are allowed.

Notice the dramatically reduced gulp configuration when compared to Grunt. The code tells the story.

  1. Get a handle to gulp
  2. Get a handle to CoffeeScript
  3. Get a handle to Uglify
  4. Define the task (default in this example)
  5. Collect all CoffeeScript files from the src directory or any of its subdirectories
  6. Execute coffee to compile CoffeeScript to JavaScript
  7. Execute uglify to minify the JavaScript
  8. Write the files to the dist directory

The story is not as evident when reading the Grunt configuration, however.

These two examples show a basic configuration using Grunt and gulp. Digging further we can see the differences aren’t only superficial. These builds can be expected to execute repeatedly and continuously, and gulp set out to make them fast.

flameborder_learningswift