Upcoming and OnDemand Webinars View full list

Journey into Gulp

Todd Gandee

Workflows for front-end software development are nothing new. Every developer has his or her preferred toolset and build process. Trying to keep up with the latest workflows and tools can be difficult, however. As legacy tools become bloated with syntax or configurations for the plethora of plugins, retooling needs to happen.

Our JavaScript developers have been using Grunt for the majority of our client-side applications. If you haven’t seen a Gruntfile , it is a JavaScript module with task definitions and configurations. These tasks will be called from a Node process named Grunt. As the Gruntfile acquires more processes and tasks, the configurations start to look cryptic to anyone other than the original author, requiring a detailed explanation of what each configuration does. When we were just minifying JavaScript and CSS files, Gruntfiles were probably easy to read. Over time, however, that’s no longer the case.

Enter Gulp. Gulp is a Node task runner for building next-generation applications or just accomplishing simple development tasks. Gulp promotes code over configuration: instead of writing a configuration file, you write and call functions that perform your tasks. Some developers find the syntax easier to read and learn. Another nice benefit is that tasks run more quickly, as Gulp can run tasks in parallel.

Gulp has five main methods, and leverages Node syntax and Node streams. src, dest, task, watch and pipe are all you need in order to work with Gulp.

Getting Started with Gulp

To install Gulp, you must have Node.js and npm already installed. If you need to, grab the installer from nodejs.org. Then, on the command line, type:

    npm install -g gulp

This will install Gulp globally. Next, create your project directory or navigator to the existing project. At this point, you will need to create a node package configuration, a package.json file, if one does not exist:

    npm init

This npm command will ask you a series of questions to help you create a package.json file that will manage your project’s node modules. Next, you will add your node dependencies, beginning with ‘gulp’:

    npm install --save-dev gulp

When you use the option --save-dev, npm will install the module locally in your ‘node_modules’ directory and save the reference to the module in your package.json file. If you open your package.json file, it should look like this:

package-json

Now that you have added Gulp, you can create a Gulp file named “gulpfile.js” and enter the following JavaScript code:

    var gulp = require('gulp');

    gulp.task('default', function() {
      // place code for your default task here
    });

Of course, this does not do much of anything. What you have written (or copied and pasted) is the basis for creating a Gulp build system. You have loaded the Gulp module by calling require('gulp') and saving a reference to the module in a variable. That module provides a method named task, and you are calling it to create a task. task accepts two arguments: a name for your task and a callback containing the code for that task. The name ‘default’ is the task that will run when you call gulp from the command line with no other arguments.

Let’s add a simple console.log() to the task, and run Gulp. The ‘gulpfile.js’ should look like this:

    var gulp = require('gulp');

    gulp.task('default', function() {
      // place code for your default task here
      console.log('[gulp]: default task');
    });

And now from the the command line:

    gulp

gulp default

Running the command gulp default should yield the same result. Gulp expects to execute either the ‘default’ task or a task whose name you specify as an argument to the ‘gulp [task]’ command.

Tasks can be a series of synchronous actions, or streams, using the pipe method. pipe will take the results of the previous function innvocation and send them to the function passed as the argument to ‘pipe’. Here is a common pattern when using pipe, and two of Gulp’s other methods, src and dest:

    gulp.src('application.js')
      .pipe(gulp.dest('build/js'));

In this example, the src function would retrieve the contents of the file application.js, and pass those contents to gulp.dest, along with its existing arguments or configuration (‘build/js’). This specific pattern of getting source, processing it and sending it to a destination is a way to create production builds of your app. Specifically, you write some code and then check it (linting), process it (minify) and copy it to a production directory. For each stage of processing, you just add another .pipe(someFunction()) call in between gulp.src() and .pipe(gulp.dest()).

Let’s talk about src and dest. In the last example, you used pipe to see how a single file can be copied to a production folder. Now, try to take an entire directory and move it to the ‘build’ directory.

    var gulp = require('gulp');
    var scripts = 'scripts/*.js';

    gulp.task('copy', function() {
      // place code for your default task here
      gulp.src(scripts)
          .pipe(gulp.dest('build/js'));
    });

gulp copy

src can take String or an Array of strings as an argument. In Node, it’s called the glob pattern. Those strings are patterns for the src method to match. In the above example, scripts/*.js is a pattern for finding all the .js files in the scripts directory. Another example of a common pattern is scripts/**/*.js, which would find all the .js files in all the sub-directories of scripts. This type of recursion is computationally expensive, so use the /**/*.* pattern sparingly. When using an array of string patterns, you can list files to include and files to exclude in the same array.

    var gulp = require('gulp');
    var scripts = ['scripts/**/.js', '!scripts/someDirectory'];

    gulp.task('copy', function() {
      // place code for your default task here
      gulp.src(scripts)
          .pipe(gulp.dest('build/js'));
    });

Using ! is a signal in the pattern to make sure you do not retrieve those files. You may have numerous arrays of string patterns for varying steps in your build process. Some files might need to be sourced from the development code and returned to that same directory, while others need to be piped to the production directory without processing.

From all the previous examples, you probably have a good idea of what gulp.dest() does. It takes a path argument as a string, and will pipe the contents from previous operations to the destination path directory. It will create the directory if it does not exist.

The last Gulp function is watch. It will keep a node server running and watch a glob (a string pattern or array of string patterns) of files and process tasks as files change. watch will take a glob, an optional array of tasks to be run when the server starts, and a callback for when a change events occurs.

    gulp.watch('scripts/*.js', ['default'], function(event) {
      console.log('File '+event.path+' was '+event.type+', running tasks...')
      gulp.start('copy');
    });

This simple invocation of watch will run the ‘default’ task and watch for any changes to .js files in the ‘scripts’ directory. When a change occurs, the console will log the path of the file that triggered the change event and event type. Then, it will start the ‘copy’ task.

gulp watch

In the screenshot, the console reads out the actions that took place when watch was started. The ‘default’ task was run, a change occured, and then the ‘copy’ task was run. At this point, the watch server is still running and watching for changes.

Fine-Grained Control with Gulp

In the course of this post, you have installed Gulp, run the ‘default’ task, written a task to move files from the development enviroment to the production environment, and spun up a watch server to watch of changes while you development your project. The five methods of Gulp allow you fine-grained control over every aspect of your build process with clear language and simple, familiar JavaScript syntax. Your tasks are written as plain JavaScript functions, making it straightforward to modularize and organize your task code.

Now that you know how Gulp works, start looking for ‘Gulp-*’ plugins (npm modules) to add to your process. It is easy to see the power of pipe when chaining together multiple steps in processing your files.

Want to learn more about frontend web development? Sign up for our HTML5 Apps with jQuery bootcamp. Todd will be teaching June 9-13.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project