Getting started with gulp
Step aside Grunt, there's a new task runner in town. Gulp is an intuitive, code-over-configuration, streaming build system. It's fast.
Why should I be interested? Good question. Gulp’s code-over-configuration makes it not only easy to write tasks for, but also much easier to read and maintain.
Gulp uses node.js streams, making it faster to build as it doesn’t need to write temporary files/folders to disk. If you want to learn more about streams—although not necessary—this article is a good read. Gulp allows you to input your source file(s), pipe them through a bunch of plugins and get an output at the end, rather than configuring each plugin with an input and output—like in Grunt. Let’s take a look at an example of what a basic Sass build could look like with both Grunt and gulp:
####Grunt:
Grunt requires each plugin to be configured separately, specifying source and destination paths for each plugin. For example, we input one file to the Sass plugin, which then saves the output. We then need to configure Autoprefixer to input Sass’s output, which then outputs another file. Let’s take a look at the same configuration with gulp:
####Gulp:
With gulp we only input one file. This gets modified by the Sass plugin, passed to the Autoprefixer plugin and modified, then we get one file out. This process speeds up the build process as we’re not reading and writing unnecessary files to end up with one.
So you’re interested, now what? Let’s install gulp and create a basic gulpfile with some core tasks to get started.
Installing gulp
Before we delve into configuring tasks, we need to install gulp:
This installs gulp globally, giving access to gulp’s CLI. We then need to install it locally to the project. cd
into your project and run the following (make sure you have an existing package.json
file):
This installs gulp locally to the project and saves it to the devDependencies
in the package.json
file.
Installing gulp plugins
We are going to install some plugins to achieve the following tasks:
- Sass compile (gulp-ruby-sass)
- Autoprefixer (gulp-autoprefixer)
- Minify CSS (gulp-cssnano)
- JSHint (gulp-jshint)
- Concatenation (gulp-concat)
- Uglify (gulp-uglify)
- Compress images (gulp-imagemin)
- LiveReload (gulp-livereload)
- Caching of images so only changed images are compressed (gulp-cache)
- Notify of changes (gulp-notify)
- Clean files for a clean build (del)
To install these plugins, run the following command:
This will install all necessary plugins and save them to devDependencies
in package.json
. A full list of gulp plugins can be found here.
Load in the plugins
Next, we need to create a gulpfile.js
and load in the plugins:
Phew! That seems a lot more work than Grunt, right? Gulp plugins are slightly different from Grunt plugins—they are designed to do one thing and one thing well. An example; Grunt’s imagemin
uses caching to avoid re-compressing images that are already compressed. With gulp, this would be done with a cache plugin, which can also be used to cache other things too. This adds an extra layer of flexibility to the build process. Pretty cool huh?
We can auto load all installed plugins like in Grunt, but for the purpose of this post we’ll stick to the manual method.
Creating tasks
Compile Sass, Autoprefix and minify
Firstly, we will configure Sass compiling. We’re going to compile Sass as expanded, run it through Autoprefixer and save it to our destination. We’ll then create a minified .min
version, auto-refresh the page and notify that the task is complete:
A little explanation before moving on.
This is the gulp.task
API which is used to create tasks. The above can run from Terminal with $ gulp styles
.
This is the new gulp-ruby-sass
API where we define the source file(s) and pass in any options. For many other plugins, you would use the gulp.src
API which we use later in this post (return gulp.src(...)
). It also can be a glob pattern, such as /**/*.scss
to match multiple files. By returning the stream it makes it asynchronous, ensuring the task is fully complete before we get a notification to say it’s finished.
We use .pipe()
to pipe the source file(s) into a plugin. Usually the options for a plugin are found on their respective GitHub page. I’ve linked them above for convenience. Pipes are chainable so you can add as many plugins as you need to the stream.
The gulp.dest
API is where we set the destination path. A task can have multiple destinations, one to output the expanded version and one to output the minifed version. This is demonstrated in the above styles
task.
I’d recommend checking out the gulp API documentaiton to get a better understanding of these methods. It’s not as scary as it sounds!
JSHint, concat, and minify JavaScript
Hopefully you’ll now have a good idea of how to create a task for gulp. Next, we’ll set up the scripts task to lint, concat and uglify:
Here we use the gulp.src
API to specify our input files. One thing to note is that we need to specify a reporter for JSHint. I’m using the default reporter, which should be fine for most people. More on this can be found on the JSHint website.
Compress Images
Next, we’ll set up image compression:
This will take any source images and run them through the imagemin
plugin. We can go a little further and utilise caching to save re-compressing already compressed images each time this task runs. All we need is the gulp-cache plugin—which we installed earlier. To set this up, we need to change this line:
To this:
Now only new or changed images will be compressed. Neat!
Clean up!
Before deploying, it’s a good idea to clean out the destination folders and rebuild the files—just in case any have been removed from the source and are left hanging out in the destination folder:
We don’t need to use a gulp plugin here as we can take advantage of Node modules directly within gulp. We use a return to ensure the task finishes before exiting.
The default task
We can create a default task, ran by using $ gulp
, to run all three tasks we have created:
Notice the additional array in gulp.task
. This is where we can define task dependencies. In this example, the clean task will run before the tasks in gulp.start
. Tasks in Gulp run concurrently together and have no order in which they’ll finish, so we need to make sure the clean
task is completed before running additional tasks.
Note: It’s advised against using gulp.start
in favour of executing tasks in the dependency arrary, but in this scenario to ensure clean
fully completes, it seems the best option.
Watch
To watch our files and perform the necessary task when they change, we firstly need to create a new task, then use the gulp.watch
API to begin watching files:
We specify the files we want to watch via the gulp.watch
API and define which task(s) to run via the dependency array. We can now run $ gulp watch
and any changes to .scss
, .js
or image files will run their respective tasks.
LiveReload
Gulp can also take care of automatically refreshing the page on file change. We’ll need to modify our watch
task, configuring the LiveReload server.
To make this work, you’ll need to install and enable the LiveReload browser plugin. You can also place the snippet in manually.
Putting it all together
Here we have the full gulpfile, embedded from this gist:
I’ve also put together a Gruntfile that accomplishes the same tasks, so you have take a look for a comparison, in the same gist.
If you have any questions or issues, please leave a comment below or you can find me on Twitter.