Introduction to gulp

8 min read

In this article, I’ll introduce you to gulp, a tool for automating development tasks. We’ll install plugins and write a few tasks of our own to make front-end development less repetitive and a little more enjoyable.

*.sassSass source
Compile SassAdd vendor prefixesMinify CSS
*.cssStylesheets
*.htmlHTML source
Minify HTML
*.htmlPages
*.jsJavaScript source
Minify JavaScript
*.jsScripts
Watch & BrowserSyncSource changes rerun the relevant task. BrowserSync serves build/, injects updated CSS, and reloads the browser for other changes.
Source files flow through Gulp tasks into build/.

What is gulp?

Here’s how the gulp website describes it: gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something.

Working with gulp mostly means writing tasks that pass files through a series of processing steps.
Think of a task as a conveyor belt, with each step doing a different job along the way.

Prerequisites

Gulp runs on Node.js and uses npm for package management. Start by installing Node.js; npm comes with it.

Installing gulp

Installing gulp is straightforward. We’ll use npm, the Node.js package manager.
First, install the gulp-cli package globally:

npm install gulp-cli -g
Then install gulp locally in your project folder:
npm install gulp -D

When you run gulp in your project folder, gulp-cli will use the version installed in that project.

The gulpfile.js file

Running gulp looks for a file named gulpfile.js and runs the default task defined in it.

To run a specific task, add its name after gulp:

gulp taskname

Project structure

Let’s create two folders: source and build. Our source files go in source. Gulp will process them and put the results in build.

The project should look like this:

Writing a task

Let’s call our first task copy. It will copy files from source to build without changing them.

var gulp = require('gulp'); // Load the locally installed gulp package

var source = 'source/', destination = 'build/'; // Define the source and build folder paths

gulp.task('copy', function() { // Define the 'copy' task
	return gulp.src(source + '*') // Read all files from the source folder
    .pipe(gulp.dest(destination)) // Write them to the build folder
});

Before we run it, let’s give it something to copy. Create index.html in the source folder with the following content:

<body>
<h1>Hello, cutie!</h1>
</body>

Now run the copy task:

gulp copy

You should now see an identical copy of index.html in the build folder. Let’s make our next task a little more useful with a gulp plugin!

Installing gulp plugins

Next, we’ll create package.json. This file describes the project and lists the packages it depends on, including our gulp plugins.

Generate package.json with:

npm init -y
The -y flag accepts the default options. You can edit them in package.json later.

Make sure gulp is listed under devDependencies, since we only need it during development. If it’s under dependencies, move it across. You can read more in the npm documentation.

Your package.json should look something like this:

{
	"name": "gulp-examples",
	"version": "1.0.0",
	"description": "",
	"main": "gulpfile.js",
	"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1"
	},
	"keywords": [],
	"author": "",
	"license": "ISC",
	"devDependencies": {
		"gulp": "^3.9.1"
	}
}

To install a gulp plugin, use:

npm install plugin-name --save-dev
The --save-dev flag saves the plugin as a development dependency in package.json.

A Sass task

We’ll use the gulp-sass plugin to compile Sass files into CSS.
We need to install it first:

npm install gulp-sass --save-dev

Create a simple Sass file called styles.sass in the source folder:

h1
	color: aquamarine
	padding: 1em
	transform: scale(0.9, 0.9)

Now add a task named sass to gulpfile.js:

var gulp = require('gulp'); // Load the locally installed gulp package
var sass = require('gulp-sass'); // (New)

//...

gulp.task('sass', function() {
	return gulp.src(source + '*.sass') // Read all files with the .sass extension
	.pipe(sass()) // (New) Compile them to CSS with gulp-sass
	.pipe(gulp.dest(destination)) // Write the CSS to the build folder
});

Run the sass task:

gulp sass
You should see a styles.css file in the build folder.

Let’s reference the CSS file in index.html:

<link rel="stylesheet" type="text/css" href="./styles.css">
<body>
<h1>Hello, cutie!</h1>
</body>

Adding steps to the Sass task

Some CSS properties need vendor prefixes to work across browsers. The gulp-autoprefixer plugin adds prefixes such as -webkit- and -ms- for us.

We can also make the CSS smaller with the gulp-clean-css plugin.

Install both plugins first:

npm install gulp-autoprefixer gulp-clean-css --save-dev

Then add two more steps to the sass task:

//...
var cleanCSS = require('gulp-clean-css'); // (New)
var autoprefixer = require('gulp-autoprefixer'); // (New)

//...

gulp.task('sass', function() {
	return gulp.src(source + '*.sass') // Read all files with the .sass extension
	.pipe(sass()) // Compile them to CSS with gulp-sass
	.pipe(autoprefixer({browsers: ['last 2 versions'], cascade: false})) // Add CSS prefixes for the last two versions of popular browsers
	.pipe(cleanCSS()) // (New) Minify CSS
	.pipe(gulp.dest(destination)) // Write the CSS to the build folder
});

Run gulp sass again. The styles.css file should now contain minified CSS with vendor prefixes.

Improving plugin loading

So far, we’ve loaded each plugin separately:

var plugin = require('gulp-plugin');
A larger project might use dozens of plugins. To keep things tidy, we’ll use gulp-load-plugins, which loads the plugins listed in package.json into one object.

Install gulp-load-plugins, then update gulpfile.js:

var gulp = require('gulp');
var plugins = require('gulp-load-plugins')(); // (New)

var source = 'source/', destination = 'build/';

gulp.task('copy', function() {
	return gulp.src(source + '*')
    .pipe(gulp.dest(destination))
});

gulp.task('sass', function() {
	return gulp.src(source + '*.sass')
	.pipe(plugins.sass()) // (Changed)
	.pipe(plugins.autoprefixer({browsers: ['last 2 versions'], cascade: false}))
	.pipe(plugins.cleanCss()) // (Changed)
	.pipe(gulp.dest(destination))
});

A tidier gulpfile

The pump package gives us another way to write these pipelines.
Install pump, then update the tasks like this:

var gulp = require('gulp');
var plugins = require('gulp-load-plugins')();
var pump = require('pump');

var source = 'source/', destination = 'build/';

gulp.task('copy', function(callback) {
	pump([
		gulp.src(source + '*'),
		gulp.dest(destination)
	], callback);
});

gulp.task('sass', function(callback) {
	pump([
		gulp.src(source + '*.sass'),
		plugins.sass(),
		plugins.autoprefixer({browsers: ['last 2 versions'], cascade: false}),
		plugins.cleanCss(),
		gulp.dest(destination)
	], callback);
});

Besides tidying up the code, pump helps handle errors from the plugins.

A JavaScript task

Let’s use gulp-uglify to minify our JavaScript files.

Here’s the task:

gulp.task('js', function(callback) {
	pump([
		gulp.src(source + '*.js'), // Read all files with the .js extension
		plugins.uglify(), // Minify JavaScript code
		gulp.dest(destination) // Write it to the build folder
	], callback);
});

Add a scripts.js file to the source folder:

document.querySelector('h1').style.backgroundColor = 'cadetblue';
Then reference it in index.html:
<link rel="stylesheet" type="text/css" href="styles.css">
<body>
<h1>Hello, cutie!</h1>
</body>
<script src="scripts.js"></script>

An HTML task

Let’s replace the copy task with an html task that minifies HTML using gulp-htmlmin.

gulp.task('html', function(callback) {
	pump([
		gulp.src(source + '*.html'),
		plugins.htmlmin({collapseWhitespace: true}),
		gulp.dest(destination)
	], callback);
});

The option tells the plugin to collapse unnecessary whitespace in the HTML.

Build and clean tasks

Now we’ll add a build task to run all the steps, and a clean task to clear out the build folder. We’ll use the del package to delete files.

// ...
var del = require('del');

// ...

gulp.task('clean', function() {
	return del(destination + '*')
});

gulp.task('build', ['clean', 'html', 'sass', 'js']);

The array after the task name lists its dependencies. Gulp runs those tasks before it runs build.

There’s a catch: all the tasks in that array run at the same time. We need clean to finish before the others start.
The gulp-sync plugin lets us set that order:

// ...
var gulpsync = require('gulp-sync')(gulp);

// ...

gulp.task('build', gulpsync.sync(['clean', ['html', 'sass', 'js']]));// Run clean first, then the other tasks

Now we can build the whole project with one command:

gulp build

Live updates with BrowserSync and watch

Rebuilding the project and reloading the browser by hand after every change gets old pretty quickly.

BrowserSync makes this easier: it injects updated CSS into the page and reloads the browser when other files change.

Gulp’s watch function will run the right tasks whenever we change a source file.

Here’s our new default task:

// ...
var browserSync = require('browser-sync').create();

// ...

gulp.task('default', ['build'],  function() {
	gulp.watch(source + '*.sass', ['sass']); // Run sass when .sass files change in the source folder
	gulp.watch(source + '*.js', ['js']);
	gulp.watch(source + '*.html', ['html']);

	gulp.watch(destination + '!(*.css)').on('change', browserSync.reload); // Reload when a built file changes, except CSS. The sass task injects CSS updates.

	browserSync.init({ // Start BrowserSync
		server: {
			baseDir: destination // BrowserSync's server will serve content from the 'build' folder
		}
	});
});

We also need to update the sass task:

gulp.task('sass', function(callback) {
	pump([
		gulp.src(source + '*.sass'),
		plugins.sass(),
		plugins.autoprefixer({browsers: ['last 2 versions'], cascade: false}),
		plugins.cleanCss(),
		gulp.dest(destination),
		browserSync.stream() // (New) Inject the updated CSS without reloading the page
	], callback);
});

Run:

gulp
Your default browser should open http://localhost:3000/. Changes to the source files should now appear in the browser automatically.

Uploading code using SFTP

Finally, let’s use gulp-sftp to create an upload task. It will build the project, then upload the files to a remote server:

gulp.task('upload', ['build'], function (callback) { // Build the project before uploading
	pump([
		gulp.src(destination + '/**/*'), // Upload the build folder and its subfolders
		plugins.sftp({
			host: 'example.com', // Remote host. Could be a hostname or an IP address.
            user: 'root', // Username.
            key: { location:'~/Keys/example.com.key', passphrase: 'secret' }, // The SSH key path and passphrase
			remotePath: '/usr/share/nginx/html/' // The destination folder on the remote host
		})
	], callback);
});

That’s just the beginning

There’s plenty more to explore with gulp: templates, loading JSON data, image optimization, and other ways to automate your work. I’ll leave those for another time.

The example on GitHub

You can find the example project on GitHub. Have fun!