Meet Gulp: A Streaming JavaScript Task Runner
Introduction
In this post, I’ll talk about Gulp in the context of frontend website development. I’ll explain what it is, how to install it and finally give a real-world usage example.
It’s used from the terminal and here I am using it from the Cygwin terminal on Windows but you should be able to follow this tutorial on your machine by using the standard console in Windows, Linux or Mac or anywhere else where Node.js is available.
What is Gulp?
This is a Node.js package that enables you to easily write tasks in JavaScript which help you to automate stuff you do often. It can automate common repetitive tasks in your workflow and make you more productive, e.g. it can minify your CSS, compile LESS and SASS, lint JavaScript files, send emails, etc.
On its own it actually doesn’t do much except providing a few methods for reading and writing streams and organizing how other Node.js packages or plugins work together – these are used in tasks to do actual work. This is typically just a wrapper around another node package and these wrappers are made so packages can be used in Gulp streams.
Getting started
To demonstrate how things work let’s make a simple task that outputs “Hello World” to the console.
First things first. To start with we need to install Node.js and node’s package manager npm. Download and install Node.js from nodejs.org, and npm will be installed together with the node by default.
After the installation, open your terminal and check if Node.js works by typing:
$ node -v
This should give you the Node.js version number if it is installed correctly.
Check if npm works in the same way:
$ npm -v
After both Node.js and npm are working we can install and start using them straight away. To install it globally type:
$ npm install -g gulp
If everything went according to plan your console should look something like this:
Now we’ve come to the project-specific parts. Create a test project directory for our “Hello World” task. It doesn’t matter where the directory is located. I used the following:
$ mkdir /cygdrive/c/Users/Ivo/test-project $ cd /cygdrive/c/Users/Ivo/test-project
We should now initialize our project with npm, this will create a package.json file in the directory which stores project information. In the scope of this tutorial, we’re only interested in the devDependencies section of package.json, a section which contains a list of packages that the project depends on in development.
Packages that are later installed to be used will be placed in the ./node_modules directory and their name and version will be recorded to the devDependencies list.
Use npm to initialize your project, fill in some info when npm prompts you or leave defaults:
$ npm init
Install Gulp inside your project, –save-dev will save it to your package.json as a development dependency:
$ npm install --save-dev gulp
It should now be added to our project. Its files are stored in the ./node_modules/gulp directory and its package name and version is written to package.json devDependencies.
We can now define our “Hello World” task. The tasks are defined in a file called gulpfile.js, this is just a plain node JavaScript file where the tasks are defined by convention.
Go ahead and create a file named gulpfile.js in your project directory. A basic gulpfile with a default task that outputs “Hello World” looks like this:
We start the above gulpfile by including packages via require that will be used in the tasks. Obviously, we need to include Gulp itself in the same way. A task is defined by calling this method task() and here we define a task called default with a callback function that outputs “Hello World”. This callback function will be called every time we invoke the task. Tasks can be invoked from the terminal or from other tasks. To run a task named default while in the project directory type:
$ gulp
This will run a task named default. And there you have it, a hello world task. You can define additional tasks with different names, to run these tasks, use:
$ gulp <task> <othertask>
API
This consists of only four methods: src(), dest(), task() and watch(). Detailed documentation about these can be found here. Here’s a brief description of what these methods do:
gulp.src()
Creates a stream of files that can be passed to plugins which will transform the stream in some way.
gulp.dest()
Writes a stream into files on disk.
gulp.task()
Defines a task that calls a callback function or other tasks when invoked.
gulp.watch()
Watch a list of files and call a task when any of the files change.
Streams
A typical task will open a stream of files with a gulp.src(). Pass that stream to arbitrary number of plugins or packages and finally write the files from the stream to disk with gulp.dest(). The idea is to take a stream of files and pass that stream through a series of transformations after which you write the files from the stream to the disk.
You will see the pipe() method all over the place in tasks. That’s not a part of the API but a part of node streams.
pipe() enables us to pass output from one plugin as input to another plugin efficiently via node streams. This is similar to piping programs together in the command line with vertical bar character |.
You can think of this as a “Streaming JavaScript Task Runner” and pipe() is what puts streaming in the name.
Example: compile, minify and autoprefix LESS files
Say we’re using LESS as our CSS pre-processor so let’s define a Gulp task that will compile, minify and automatically prefix our LESS files into one CSS file.
I will assume your LESS files are in a folder called ./less and your main file is main.less.
Create a new project directory, initialize it with npm and install it as we did in the “Hello World” project above. Create a file named gulpfile.js in your project directory and copy the following JavaScript into your gulpfile.js:
We start the above gulpfile by including a list of packages that are used in our tasks:
var browserslist = require('browserslist'); var gulp = require('gulp'); var gulpAutoprefixer = require('gulp-autoprefixer'); var gulpLess = require('gulp-less'); var gulpMinifyCss = require('gulp-minify-css');
Browserslist is a node package with the Can I Use browser usage statistics database. It’s included here to use it from browserslist task and output which browsers will be prefixed by autoprefixer.
gulp-autoprefixer is a wrapper (plugin) around autoprefixer-core node package which prefixes CSS, analogously gulp-less compiles LESS to CSS and gulp-minify-css minifies CSS.
Before tasks can be used all of these packages need to be added to the project via npm in the same way as you installed previously, in your project directory type:
$ npm install --save-dev browserslist $ npm install --save-dev gulp-autoprefixer $ npm install --save-dev gulp-less $ npm install --save-dev gulp-minify-css
This will write package files to the ./node_modules directory and add packages to your devDependencies list. At a later time, if you lose the ./node_modules folder or you don’t want to add it to your project’s repository it’s enough to have your package.json file around, you can then use $ npm install to add everything from devDependencies back to your project.
The gulpfile contains three tasks: less, browserslist and watch.
Task less is defined like this:
gulp.task('less', function(){ // compile less files gulp.src('less/main.less') .pipe(gulpLess()) .pipe(gulpMinifyCss()) .pipe(gulpAutoprefixer(browsers)) .pipe(gulp.dest('css/')); });
In the callback function we’re defining what the task does, here the method gulp.src() is reading ./less/main.less file and returning a stream containing an object that represents ./less/main.less, that stream is then piped to the gulpLess() function which modifies the stream by transforming LESS into CSS.
In the same way gulpMinifyCss() minifies CSS, gulpAutoprefixer() adds prefixes based on browsers settings string. Finally gulp.dest() writes the stream to ./css/main.css file.
That’s it. When the task less is invoked our less/main.less will get compiled into css/main.css.
Task watch is defined like this:
gulp.task('watch', function(){ gulp.watch('less/**/*.less', ['less']); });
Here we’re watching the whole less/ folder for changes by using gulp.watch(), when any of the files with .less extension change, it will automatically call task less.
Conclusion
This is a powerful tool which you can use to automate and customize your workflow.
If you want to know more about this concept, its official website is a good place to start. If you need plugins for your tasks visit http://gulpjs.com/plugins/, there’s a large index of friendly packages. Also here’s a list of recipes where you can see more task examples.
I hope you get more productive by writing a lot of awesome tasks. Good luck!