Introduction to Gulp

Being productive is something that us developers value a lot. We try to automate everything so there's little repetitive work left to do. Unfortunately there are still loads of developers who minify their JavaScript & CSS using online tools and refresh their browser manually once they save a file.

What is Gulp?

Gulp describes itself as a streaming build system. That means it's a build system that takes advantage of Node's stream capabilities. If you don't know what streams are let me rephrase their slogan for you.

Gulp is a blazing fast build system.

A build system builds your project into different builds (ie: development, production, testing). Gulp does this really well and is also quite extensible with plugins (980 at this time of writing).

When you build your project to production, you probably want to minify your JavaScript, compile your Sass and check for errors in your code. Don't do that manually, write a Gulp task and run it whenever you need to!

Setting up Gulp

To install Gulp you will need Node, installing Node is not within the scope of this article so I'll assume you will have it installed already. Once you've fired up your terminal and you're in your project folder it's time to create a package.json. You'll have to do this only once per project.

$ npm init

This will prompt you with some questions for creating a package.json. Just press enter if you don't know what to fill in or even better: check the documentation.

When you've created a package.json it's time to install Gulp and some dependencies. If you don't have Gulp installed globally yet I advise you to do so using:

npm install gulp -g  

Skip this if you have Gulp installed on your system already of course (note: on your system !== in your project folder)

Next we need to install Gulp and some dependencies into our project.

npm install gulp --save-dev  

The --save-dev flag adds the installed dependency as a devDependency, because you'll only use them when developing your application. To install Gulp plugins you use the same syntax.

npm install gulp-[plugin name] --save-dev  

Folder Structure

Before we continue you need to have a src (source) and dist (distribution) folder. The src folder will contain all your source files such as plain JavaScript, uncompressed (s)css and unoptimised images. The dist folder will be completely built up by Gulp. If you're not able to delete your dist folder and rebuild it with Gulp, you're doing something wrong!

To keep our root directory clean I like to place the src and dist folder in a /public folder. It would look something like this:

node_modules/  
public/  
  src/
    index.html
    img/
    js/
    scss/
  dist/
    index.html
    img/
    js/
    css/
  lib/
    vanilla.js
    fancybox.js
gulpfile.js  
package.json  

For the sake of this introduction I have added the dist folder + structure already. Normally you would add the dist to your .gitignore and build it yourself.

You might have noticed the gulpfile.js, this is where all the magic happens.

gulpfile.js

The gulpfile.js is your Gulp configuration. Unlike Grunt, Gulp's syntax is really easy to pick up. This is the basic syntax of a task that combines JavaScript files.

var gulp = require('gulp');  
var concat = require('gulp-concat');  
...
gulp.task('combine-js', ['lint-js'], function () {  
  return gulp.src('/public/js/**/*.js')
    .pipe(concat('all.js'))
    .pipe(gulp.dest('public/dist/js'));
});
... 
gulp.task('default', ['combine-js']);  

Honestly, doesn't that look beautiful?! Let's break it down a bit.

gulp & concat we need to require these devDependencies in our gulpfile to use them. Remember before being able to use them you need to have installed them (using --save-dev).

gulp.task(name, deps, func) is actual task declaration that takes 3 parameters, the second one is optional.

  • name - This is the name of your task, a string without spaces.
  • deps - An array with tasks te execute before the current task. In the example above it would lint the JavaScript before concatenating JavaScript files. (Of course we would have to create our lint-js task first)
  • func - Is the actual task function, you define what your task does within this function.

If you don't have any pre-task dependencies you won't have to do gulp.task('name', [], fn);. Gulp is smart enough to check if the second parameter is a function so gulp.task('name', fn); will work just fine.

gulp.src(files) is a string or array containing the file(s) / file paths.

In our example we use js/**/*.js. This is called a wildcard and means that Gulp will grab all the .js files in the js/ folder, even if they're in nested directories. You can also use an array if you want multiple files / folders.

gulp.src([  
  'public/src/js/loginForm.js'
  'public/src/js/slider/*.js'
  '!public/src/js/slider/slider-beta.js'
  ] ...);

This example would get the loginForm.js and all JavaScript files in the slider folder. You've probably noticed the '!' in front of the last array item. This means don't include this file. You'll only have to do this if your previous wildcard includes it.

gulp.pipe(...) is Gulp's stream magic. You can pipe the output of your tasks into functions. This amazing thing might be completely new to you so allow me to show you how it works with an example.

gulp.src('public/src/js/*.js')  
  .pipe(stripDebug())
  .pipe(uglify())
  .pipe(concat('script.js'))
  .pipe(gulp.dest('public/dist/js'));

This example uses stripDebug uglify and concat.

First, Gulp grabs all .js the files in the src/js directory. Then we pipe it to stripDebug, which removes all the console.logs and alerts. Next we pipe it to uglify, which compresses the JavaScript. After that we pipe it to concat('all.js') which will combine all the stripped and compressed JavaScript files into all.js. Finally we pipe it to gulp.dest() that does the actual writing of the output file for us.

Et voila! You now have a fully stripped, compressed and combined js file: public/dist/js/script.js

gulp.task('default', []) is the default task that runs when you run gulp from the command-line without any arguments. If you want to run a specific task you can do:

$ gulp task-name

Building our gulpfile

Now you know what a Gulpfile looks like, we should create a real one! I'll assume you have the src/dist folder structure. In this example we'll create a Gulpfile that combines and compresses JavaScript files, compiles sass files and reloads the browser when a file changes.

We'll use the following gulp-plugins (devDependencies) for it.

To install them all at once use:

npm install gulp-webserver gulp-concat gulp-uglify gulp-minify-html gulp-sass gulp-livereload --save-dev  

Fire up your editor and load your gulpfile.js, we're about to be productive! Let's start by including our plugins at the top of our gulpfile.

var gulp = require('gulp');  
var webserver = require('gulp-webserver');  
var concat = require('gulp-concat');  
var uglify = require('gulp-uglify');  
var minifyhtml = require('gulp-minify-html');  
var sass = require('gulp-sass');  
var livereload = require('gulp-livereload');  

I always like to create an object that contains all paths, so that it's easy to update & change things later on.

var src = 'public/src';  
var dist = 'public/dist';  
var paths = {  
  js: src + '/js/*.js',
  scss: src + '/scss/*.scss',
  html: src + '/**/*.html'
};

All that's left to do is writing the actual tasks.

// Start a webserver @ localhost:8000
gulp.task('server', function () {  
  return gulp.src(dist + '/')
    .pipe(webserver());
});

// Combine & Compress JS into one script.js
gulp.task('combine-js', function () {  
  return gulp.src(paths.js)
    .pipe(concat('script.js'))
    .pipe(uglify())
    .pipe(gulp.dest(dist + '/js'));
});

// Compile sass to css
gulp.task('compile-sass', function () {  
  return gulp.src(paths.scss)
    .pipe(sass())
    .pipe(gulp.dest(dist + '/css'));
});

// Compress HTML
gulp.task('compress-html', function () {  
  return gulp.src(paths.html)
    .pipe(minifyhtml())
    .pipe(gulp.dest(dist + '/'));
});

// Watch files and reload browser
gulp.task('watch', function () {  
  livereload.listen();
  gulp.watch(paths.js, ['combine-js']);
  gulp.watch(paths.scss, ['compile-sass']);
  gulp.watch(paths.html, ['compress-html']);
  gulp.watch(dist + '/**').on('change', livereload.changed);
});

gulp.task('default', [  
  'server', 'combine-js', 
  'compile-sass', 'compress-html', 
  'watch' ]);

Save it, run gulp and witness the magic. You can now edit files and gulp will compress them, minify them and even combine them. The possibilities are almost endless.

There's one last thing in the Gulpfile which we didn't discuss yet

gulp.watch(folder, [actions]) this tells Gulp to watch the specified folder (path) and re-run the [actions]. You don't want to minify your JavaScript again when you save your scss file, that's why we have different gulp.watch(ers).


* we use gulp-sass because it's a pure JavaScript solution. gulp-ruby-sass has more options but requires Ruby.

* you'll need the livereload Chrome plugin in order for livereload to work. Press the extension icon after running gulp.

Conclusion

Gulp is an amazing tool for websites / applications. It's able to automate all simple tasks and some more complicated ones too. Don't stop here, there are much more Gulp plugins to discover. I hope this post is of any use to you, if you liked it leave me a tweet or share this post!