Introduction
Gulp is a command line task runner utilizing Node.js platform. It runs custom defined repetitious tasks and manages process automation.
What makes Gulp different from other task runners is that it uses Node streams, piping output from one task as an input to the next. It reads a file once, processes it through multiple tasks, and then writes the output file. This results in faster builds because there is no need to create and read intermediary files on hard drive.
The goal of this tutorial is to introduce main concepts of Gulp and see it in action. We will make a quick project to demonstrate some of the most frequent front-end development tasks you’ll encounter, and create an integrated workflow.
You will learn:
- How to set up an automated workflow,
- Do Sass and CoffeeScript processing,
- Do asset compilation (concatenation and minification),
- Watch for change in your files and act on it,
- Automatically reload the page in the browser after change.
By the end of the tutorial you will be able to apply Gulp to your own project, customize it and be more efficient.
A brief overview of the steps to your first task:
- Install Node.js and Gulp.
- Create
package.json
and list dependencies (Gulp and plugins). - Install NPM modules.
- Create
gulpfile.js
. - Load plugin and create tasks.
- Run those tasks in the command line.
Prerequisites
Gulp is a command line tool, so you should be familiar working in terminal. In order to use Gulp you need to have Node.js installed on your system.
We will use Sass and CoffeeScript processing, but having deep knowledge of them isn’t required.
Setting up the Environment
Before we can create and run our first task, we need to have something to work on and apply Gulp to.
First we will define structure of our project, and what we want from Gulp. After that we will install Gulp and start working.
To follow along you should clone this repository so you can reuse the project files that we will rely on for the rest of the tutorial.
git clone https://github.com/malizmaj/gulp-project.git
If you are interested in the finished code, checkout the finished
branch.
Setting up a New Project
Our project has the following structure:
gulp-project
├── assets
├── gulpfile.js
├── index.html
├── package.json
├── scripts
│ ├── hello.cofee
│ └── main.js
└── styles
└── main.scss
The goal is be to create an automated workflow for working with Sass and CoffeeScript. We want to make tasks that will:
- Process
styles/main.scss
toassets/main.css
, andscripts/hello.cofee
toscripts/hello.js
, - Concatenate and minify
scripts/main.js
andscripts/hello.js
toassets/script.js
, - React when we change
scripts/hello.cofee
,styles/main.scss
,scripts/main.js
, andindex.html
, - Create server and load
index.html
so we can see the change on each save
Gulp Installation
To use Gulp, you need to install it as a global module first throught NPM (if you’re new to NPM, you can get to know it better in our Getting Started with NPM tutorial):
sudo npm install --global gulp
Now we need to download Gulp and its plugins to our project. We will specify the plugins we’re about to use in package.json
:
{
"devDependencies": {
"gulp": "latest",
"gulp-util": "latest",
"gulp-sass": "latest",
"gulp-coffee": "latest",
"gulp-uglify": "latest",
"gulp-concat": "latest",
"gulp-connect": "latest"
}
}
We list them in devDependencies
because you’ll typically run Gulp during the development stage.
Now install the dependencies by running:
npm install
This creates node_modules
directory with all the plugins in the root of your project.
Writing the First Gulp Task
All Gulp configuration goes in gulpfile.js
in the root of your project. A pattern for writing tasks is that you first load a plugin you’re about to use and then define a task which is based on that plugin.
Gulp is very plugin driven. If you want to accompish something, you need to know which plugin to use. Usually a single plugin has a single purpose, and all the plugins are just regular Node.js code.
First we load the plugins using Node.js and its require
function:
var gulp = require('gulp');
Now we can write our first task. A basic form of a task looks like this:
gulp.task('name', function() {
//implementation of the task
});
We call gulp
object and its task
method. It takes two arguments: name of the task, and a function literal (or an array of other tasks, which you will see later).
We will create a simple task to get familiar with basic methods. We will take index.html
and copy it to the folder assets
. The code for that is:
gulp.task('copy', function() {
gulp.src('index.html')
.pipe(gulp.dest('assets'))
});
Most important methods of gulp
object are:
src
where we put the name of the file we want to work with and use as an input,pipe
will take output of the previous command as pipe it as an input for the next,dest
writes the output of previous commands to the folder we specify.
To run the task open the terminal, navigate to the root of the project and run gulp
command and task name as a parameter, like this:
gulp copy
You should now have a copy of index.html
in your assets
folder.
You can also pass arrays, or use globs with src
and dest
:
folder/*.html
– will match all the HTML files infolder
root/**/*.html
– will match all the HTML files in all the folders fromroot
to its children
Using the First Plugin
Gulp relies on the plugins to do almost everything, so we need to learn how to use them.
Now we will use a plug-in called gulp-util
. Its purpose is to log custom messages to the terminal.
First we need to load the plug-in:
var gutil = require('gulp-util');
After that we write task as in the previous case:
gulp.task('log', function() {
gutil.log('== My Log Task ==')
});
Here we called plug-in we defined as gutil
, and called its log
method with our custom message. Now we can run log
task in the terminal:
$ gulp log
[19:41:37] Using gulpfile ~/gulp-project/gulpfile.js
[19:41:37] Starting 'log'...
[19:41:37] == My First Task ==
[19:41:37] Finished 'log' after 763 μs
You can learn more about gulp-util
here.
Using Preprocessors
Now we will take care of the Sass and CoffeeScript files so that the main page can use them.
Sass
Now we will create a task to process the styles/main.scss
which can be then used in our HTML file.
To do this just follow the established pattern: load the plug-in and create the task:
var sass = require('gulp-sass');
gulp.task('sass', function() {
gulp.src('styles/main.scss')
.pipe(sass({style: 'expanded'}))
.on('error', gutil.log)
.pipe(gulp.dest('assets'))
});
Here we piped main.scss
file to sass
object and as an argument we passed an object with options we want to use. Visit official Sass reference for more information about output styles, and gulp-sass
plug-in page for some other options.
We also handled a possible error message and used gulp-util
to log it.
Now we can run our sass
task:
gulp sass
We should now have assets/main.css
waiting for us. You can open index.html
to see the result.
CoffeeScript
To process CoffeeScript file we will do the same as previously:
var coffee = require('gulp-coffee');
gulp.task('coffee', function() {
gulp.src('scripts/hello.coffee')
.pipe(coffee({bare: true})
.on('error', gutil.log))
.pipe(gulp.dest('scripts'))
});
gulp-coffee
plug-in supports the same options as the standard compiler, so you can reference the official CoffeeScript website for more information.
Now run the coffee
task and there should be a new scripts/hello.js
file.
gulp coffee
Asset Compilation
Asset compilation is an act of minifying and concatenating scripts together so that the server loads the page faster.
In this task we will minify all the JavaScript files using gulp-uglify
plug-in, and then merge them using gulp-concat
.
var uglify = require('gulp-uglify'),
concat = require('gulp-concat');
gulp.task('js', function() {
gulp.src('scripts/*.js')
.pipe(uglify())
.pipe(concat('script.js'))
.pipe(gulp.dest('assets'))
});
uglify
works fine with default options, and concat
as an argument takes a name for our newly combined file.
Now call the task and asset/script.js
will be ready for index.html
to use.
gulp js
Note that coffee
and js
tasks are closely related. We can combine them in a new task that will do both tasks in the order we specify. That task first needs to execute coffee
so we have scripts to compile. We can even make it a default task – a task that runs when we run just gulp
in the terminal.
gulp.task('default', ['coffee', 'js']);
We combined two tasks in an array, in the order we want them executed. Now run default task and both tasks will execute one after the other:
gulp
Watching for File Changes
Our next goal is to automatically do all the processing tasks when a change happens in the code. We accomplish this with watch
method of the gulp
object; it comes as a standard part of the Gulp so there is no need for loading new module.
watch
method takes as arguments: source to be watched, and a task to be triggered after change. So we can define task like this:
gulp.task('watch', function() {
gulp.watch('scripts/hello.coffee', ['coffee']);
gulp.watch('scripts/*.js', ['js']);
gulp.watch('styles/main.scss', ['sass']);
});
When you run the watch
task it will listen for a change and it will keep running until stopped. You can try running it now:
gulp watch
Now try making a change to the styles/main.scss
and saving it; sass
task will execute and watch
will still keep running.
Creating Server for Live Reload
Our final task is to see a change we make in the code reflected in the browser without manual reload. After that we can integrate all the tasks into an efficient workflow.
First we will load gulp-connect
and create a new task:
var connect = require('gulp-connect');
gulp.task('connect', function() {
connect.server({
root: '.',
livereload: true
})
});
We created a task which will launch a server with some options: we set root of our server to the root of the project, and value of livereload
to true. You can visit gulp-connect
plug-in page for more information.
Now run the task:
gulp connect
Navigate to localhost:8080
in your browser and you should see index.html
open, as the server runs in the background.
Complete Integration
To be able to see the changes we make to source files, we need to reload the server after there is a change. It means that we need to establish a relation between watch
and connect
, and we need to pipe the end of some tasks to connect.reload()
.
The final gulpfile.js
looks like this:
var gulp = require('gulp'),
gutil = require('gulp-util'),
sass = require('gulp-sass'),
coffee = require('gulp-coffee'),
connect = require('gulp-connect'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat');
var coffeeSources = ['scripts/hello.coffee'],
jsSources = ['scripts/*.js'],
sassSources = ['styles/*.scss'],
htmlSources = ['**/*.html'],
outputDir = 'assets';
gulp.task('log', function() {
gutil.log('== My First Task ==')
});
gulp.task('copy', function() {
gulp.src('index.html')
.pipe(gulp.dest(outputDir))
});
gulp.task('sass', function() {
gulp.src(sassSources)
.pipe(sass({style: 'expanded'}))
.on('error', gutil.log)
.pipe(gulp.dest('assets'))
.pipe(connect.reload())
});
gulp.task('coffee', function() {
gulp.src(coffeeSources)
.pipe(coffee({bare: true})
.on('error', gutil.log))
.pipe(gulp.dest('scripts'))
});
gulp.task('js', function() {
gulp.src(jsSources)
.pipe(uglify())
.pipe(concat('script.js'))
.pipe(gulp.dest(outputDir))
.pipe(connect.reload())
});
gulp.task('watch', function() {
gulp.watch(coffeeSources, ['coffee']);
gulp.watch(jsSources, ['js']);
gulp.watch(sassSources, ['sass']);
gulp.watch(htmlSources, ['html']);
});
gulp.task('connect', function() {
connect.server({
root: '.',
livereload: true
})
});
gulp.task('html', function() {
gulp.src(htmlSources)
.pipe(connect.reload())
});
gulp.task('default', ['html', 'coffee', 'js', 'sass', 'connect', 'watch']);
Now to explain all the changes:
- We did some refactoring and created variables to hold file paths.
- Created a new
html
task so we can watch for the changes in HTML files which we then pipe toreload()
, and made the task part of thewatch
task. - Added
.pipe(connect.reload())
at the end ofjs
andsass
tasks so that the server reloads every time they execute. - Made a new
default
task that will first do all the processing, create a server and keep a watch
Now we have an integrated workflow. To try it out run:
gulp
Visit localhost:8080
. Now you can try making some change to styles/main.scss
,hello.coffee
or index.html
, and save it. In the terminal you should see that a task had run and the browser reflected that change.
Final Word
Gulp can be used to automatize a great number of menial tasks that are common during development. You should be able to use the final gulpfile.js
as a basis for your project, with some slight modification, and you can find a plug-in for your particular need.
What makes Gulp different from Grunt for example, is the approach of piping of input and output which may result in faster execution depending on your code, and preference of code over convention, which can make configuration files easier to read and understand. Grunt on the other hand is currently more mature, has a larger community, and more plugins. You can learn more about Grunt in our Getting Started with Grunt tutorial. As time goes on, the performance of both tools and number of plugins will probably level out so the choice will come down to personal preference.
You can visit these links to: