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.
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:
- source
- build
- gulpfile.js
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!