Awhile back I wrote a tutorial covering the basics of Grunt. Now that a new frontend build tool has entered the scene, I thought I'd take a look and see if I could write a simple guide for Gulp at the same level. This guide will take you from installation and getting set up through examples of real world build processes while laying out all of the tools out there to help you on the way.
This article aims to be thorough, but you'll also be referring to a few other resources while working with Gulp.
The Gulp Github repo is the main place to go for help. The README has a nice sample gulpfile, and the repo also contains Gulp's documentation. The getting started doc shows you how to install and create a blank gulpfile.
Why a Frontend Build Tool?
Sure you can just upload your raw development files to a web server and call it a day, but there are a lot of tools out there that can make a developer's life a lot easier, from CoffeeScript to jshint to Jasmine testing. If you're going to utilize even a very simple build process, a tool like Gulp can make things much more automated.
Gulp and Grunt
Besides that, you've got two pretty similar tools. Both have the goal of automating your frontend build process. Both are written mainly in Node and set up with a variety of plugins using npm. And both have a friendly root level file to tell them what to do (Gruntfile.js and gulpfile.js).
The documentation contained in the Gulp Github repo for getting up and running is actually quite effective, so check out the official getting started guide referenced above. You'll just be installing Gulp globally and then locally to your project using npm, so make sure you have Node installed. Just two simple commands here:
npm install -g gulp npm install --save-dev gulp
You can continue with the getting started guide to create a minimal gulpfile. Here's what my idea of a super basic one might look like:
You could now jump back to the command line and run this with the command
gulp. As you can probably tell, the only thing this does is output 'Hello world. '
gulp, then defining a task.
Tasks are the same concept from Grunt: you can define arbitrary tasks and run them from the command line with
default is the task called when no parameter is given. In Gulp though, tasks are just functions, and you can place whatever code you want inside of them.
A Useful Gulpfile - Copying Files
Let's look at that
** is a wildcard meaning this and any subdirectory, and
* is a typical wildcard for the filename. Prepending a path with an exclamation mark tells Gulp to exclude that directory.
In Gulp, you define the instructions for each task in a function passed as an argument to the task.
gulp.src takes an array of source paths.
gulp.dest copies results to a given directory. And
pipe chains tasks together.
Note how I'm concatenating the two paths arrays we defined in
paths. Again, you might be used to being stuck in the confines of Grunt's configuration specifics, but here we're just passing a parameter to a function.
gulp.src accepts an array of paths, so we can pass that however we want.
Try this gulpfile out if you want, and you'll see the specified .html and .js files in the app/ directory will be copied into the new dist/ directory.
A Real Gulpfile
Let's take a look at one more example, this time something you might find in a real app. The main idea for this gulpfile is similar to our above example; we're still building development files from an app/ folder into a production dist/ folder, we're just doing a little bit of processing in between.
We're using five new plugins this time around: gulp-clean, gulp-jshint, gulp-concat, gulp-uglify, and gulp-imagemin. You can see the full list of plugins in the plugin directory on the Gulp site. Just like in Grunt, when you use a new plugin in Gulp, you'll have to install it with npm. So to install these plugins and write them to your package.json, use something like this:
npm install --save-dev gulp-clean gulp-jshint gulp-concat gulp-uglify gulp-imagemin
Notice the default task definition down towards the bottom of this gulpfile. You can define a task as a sequence of other tasks by passing an array of task names instead of a function. Here, the default task runs
copy, so let's take a close look at each of these.
This plugin will simply delete files for you, allowing you to get rid of old or transitional files. The first thing this gulpfile does is run gulp-clean on the production dist/ directory, just to make sure that nothing created in a previous build and later removed in app/ could be lingering around.
jshint is a great tool for catching errors and bad habits in your code, and this plugin just runs it on the given files. Note that we also need to use a reporter here if we want to see jshint's output on the command line.
jshint.reporter('default') is fine if you're not picky about what it shows you, but go ahead and look at the Github repo if you want to get more specific. And don 't forget that this plugin will be looking in the root of your project for a
.jshintrc file to get your configuration preferences.
This plugin will minify your code by removing extra whitespace and other stuff that only matter to a human reader, not the browser. Pipe in the files you want it to operate on like normal.
This will concatenate all given files into one file to reduce the number of HTTP requests required to get your code. Just pass in the filename that you want it output as. Note that you should still add a
gulp.dest to the end of this to actually output the file into a given directory like normal.
This plugin reduces the weight of your images. Here it's used with
gulp.dest like normal to output the final files to the production folder.
Multiple Instructions in One Task
The copy task is very basic, just like in the previous example. Notice that this time we're creating multiple sets of instructions in one task, copying independent
gulp.src files to independent
Note the cwd parameter I'm passing to gulp.src in most tasks. The basic usage of this does what you might expect, changing the working directory of the task so you don't have to repeat redundant subdirectories on all of your paths.
It can also be used to tell Gulp to preserve your directory structure when outputting files, however. If you use the directory wildcard in your cwd like
app/** in the libs portion of the
copy task, Gulp will compare the directories it finds there with the paths provided in the base path in the first parameter, and use this to reconstruct your directory structure in the output. If this is omitted, all of your output files will be flattened into a single directory level.
The last thing to notice in this gulpfile is the
watch task. This watches the given path for any files to change, and executes the array of tasks when they do. This is great for when you're in the middle of development. It's a pain to switch to the terminal and run
gulp every time you make a small change, so set up a watch task like this to save you the trouble. It's not included in the
default task here, so keep in mind you would run it with
gulp watch from the command line.
Tasks in Parallel and Series
A big caveat to watch out for when using Gulp is that all tasks are executed in parallel by default. This is very useful, but can cause problems if you're not careful.
In the gulpfile above, our clean task must complete before any of our tasks that output files into the dist/ folder (scripts, imagemin, and copy). We indicate that by passing
['clean'] in as the second parameter to the definitions of these tasks. This parameter is an array of any tasks that need to complete before the task being defined is run, so you can include as many as you want.
Just make sure that your task that you want to complete first returns the Gulp stream (which is the result of a Gulp operation) when it completes, as done above in the clean task. This tells Gulp that it has completed. You can also take a callback as the parameter to your task and call it when the task completes, in case you aren't generating a stream. The documentation explains this in detail
You should be able to set up the build process for a sizeable app with this guide alone, but I hope you don't stop there with learning Gulp. Don't forget to refer to the Gulp plugin directory to get ideas on how you can use Gulp to automate much more than is covered in this article. There are already hundreds of plugins available, but Gulp is still lagging far behind Grunt in this area. If you're feeling really adventurous, check out the documentation on creating your own plugin!
Update 2014.04.13 I added the above clarification about tasks in parallel and series due to running into problems with this in my own Gulp projects.