Using Gulp: Tasks to Make Frontend Tolerable

Automate tasks to make frontend code production-ready.

NOTE:  This tutorial was written for Gulp versions <4.0.0. Check out this post for Gulp >4.0.0


Perhaps the whole obligatory-Gulp-tutorial on [Some Coding Blog] thing has become a bit cliché at this point. Haters may do as they will, but I 'll take any opportunity to jam as many SEO keywords I can get at this point. You know the ones: Gulp, ExpressJS, NodeJS, or perhaps even React Vue frontend Ubuntu framework API social cloud data entrepreneur community.

Regardless, we all need our own copy+paste references from time-to-time, or even worse: when we copy/paste our gulpfile.js from project to project and forget what they actually do. I won't tell anybody.

Quick 101

NodeJS developers use Gulp to automate necessary processes before moving their frontend code to production. This includes minifying files to make them run faster, and to also make them unreadable to people who would otherwise make fun of your mediocre Javascript which you were forced to crank out on a short timeline.

General Workflow

Let's say you're running a basic Express app. As opposed to developing and storing files in a directory such as /public, Gulp enables us to develop in one directory, and compile to another. That means we can keep our horrible uncompressed and uncompiled  source in a folder such as /src, and output them to a directory such as /dist, which would be our public-facing output. An Express file structure utilizing this would look something like this:

  |- src/
      |- js/ 
      |- scss/
  |- dist/
      |- js/ 
      |- css/
      |- img/
      |- fonts/
  |- views
      |- index.hbs
  |- routes
      |- index.js
  |- gulpfile.js
  |- node_modules/
  |- package.json
  |- app.js

Installation

First install the gulp CLI:

npm install --global gulp-cli

Next, enter your project folder and install gulp while saving it as  a project dependency.

npm install --save gulp

How it Works

Gulp doesn't do much on its own; the true magic lies within its vast library of plugins. Each individual plugin typically covers a simple task, such as compiling LESS or SASS files, or minifying client-side JavaScript and CSS. The limited scope of plugins entails a bit of setup in our gulpfile to chain said tasks together, but it also makes Gulp highly customizable to cater to your specific needs.

The Gulpfile

Gulp works by invoking a file called gulpfile.js in your main directory, which you'll need to create and set up yourself (sorry). The file is divided into two main parts: requiring (importing) plugins, and defining which tasks to run when gulp is invoked. A basic worthless gulpfile might look something like:

var gulp = require('gulp');

gulp.task('default', function () {
  console.log('Sup World!');
});

To make this file useful, we'll need to install more plugins and set up tasks for them.

Essential Plugins

Let's look at what each one does. Keep in mind there are thousands of Gulp plugins, so let's just touch on the big hitters here.

Keep in mind to install any of these plugins, you'll simply need to run the npm installation in your project directory:

npm install --save [plugin name]

gulp-uglify

Minifies Javascript or CSS files, and outputs the result into the directory of your choice. This plugin can be reused across filetypes, as we'll demonstrate in a moment.

gulp-concat

Combines minified files into a single file. This is essential for browser performance as it reduces the number of http requests being made every time your page loads.

gulp-rename

Renames files (such as those produced by gulp-concat).

gulp-sass / gulp-less

Compiles your Sass or Less files into CSS and outputs to the directory of your choice.

gulp-minify-css

Minifies CSS, as you might imagine. This can chained to gulp-sass or gulp-less to minify the CSS files those tasks produce.

gulp-autoprefixer

Thank god for this. Autoprefixer finds CSS styles and adds the browser-specific equivalents to your CSS, so you don't need to write the same style 10 times for every horrible browser in existence. This means you can write styles such as:

background: linear-gradient(to bottom, white, black);

And have them output as:

background: -webkit-gradient(linear, left top, left bottom, from(white), to(black));
background: -webkit-linear-gradient(top, white, black);
background: -o-linear-gradient(top, white, black);
background: linear-gradient(to bottom, white, black);

gulp-watch

Allows Gulp to listen for changes being made to source files, so that it may fire an event upon file change, such as:

gulp-livereload

Compiles the changes made in directories being watched via gulp-watch automatically while you work.

Next Level Pro Shit

While these plugins aren't 'essential', they are really cool and helpful.

gulp-sourcemaps

An obnoxious side effect of minifying and concating your files is when it comes time to debug errors on the frontend. Errors occurring at "line 235" are pretty useless considering your error codes are referring to the compiled files, without granting a hint as to where the problematic code may have come from in the first place. gulp-sourcemaps resolves this by adding commenting paths to which source files your code originated from.

gulp-browser-sync

By leveraging BrowserSync, this plugin immediately refreshes an open browser which links to files just changed by gulp. This means you can code, compile, and see the results in real time. This takes a bit extra effort to set up, so be sure to check their documentation.

gulp-load-plugins

Normally when creating our gulpfile, we need to start off by requiring our plugins via something like this:

var gulp = require('gulp'),
    del = require('del'),
    concat = require('gulp-concat'),
    rename = require('gulp-rename'),
    uglify = require('gulp-uglify'),
    sass = require('gulp-sass'),
    watch = require('gulp-watch'),
    livereload = require('gulp-livereload'),
    minifyCss = require('gulp-minify-css'),
    autoprefixer = require('gulp-autoprefixer');

gulp-load-plugins instead checks your package.json for any Gulp plugins and immediately requires them, thus saving you a few precious minutes. The output instead looks like:

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

Building The Gulpfile

Now that we have all these dope plugins, we can finally build our gulpfile. Here's an example (without using gulp-load-plugins for now):

var gulp = require('gulp'),
    del = require('del'),
    concat = require('gulp-concat'),
    rename = require('gulp-rename'),
    uglify = require('gulp-uglify'),
    sass = require('gulp-sass'),
    watch = require('gulp-watch'),
    livereload = require('gulp-livereload'),
    minifyCss = require('gulp-minify-css'),
    autoprefixer = require('gulp-autoprefixer');

gulp.task('styles', function() {
    return gulp.src('src/sass/*.scss')
        .pipe(sass({outputStyle: 'expanded', unix_newlines: true, linefeed: "lf"}).on('error', sass.logError))
        .pipe(autoprefixer())
        .pipe(minifyCss({
            keepSpecialComments: 1
        }))
        .pipe(rename("theme.min.css"))
        .pipe(gulp.dest('./assets/css/'));
});


gulp.task('scripts', function() {
    return gulp.src(['src/js/plugin/*.js', 'src/js/base.js'])
        .pipe(uglify())
        .pipe(concat('theme.min.js'))
        .pipe(gulp.dest('assets/js'));
});


gulp.task('ghost_config', ['scripts'], function() {
    return gulp.src(['src/js/config.js', 'assets/js/theme.min.js'])
        .pipe(concat('theme.min.js'))
        .pipe(gulp.dest('assets/js'));
});


gulp.task('default', function() {
    gulp.start('styles', 'scripts', 'ghost_config', 'watch');
});


gulp.task('watch', function() {
    gulp.watch('src/sass/*.scss', ['styles']);
    gulp.watch('src/js/*.js', ['scripts', 'ghost_config']);
    livereload.listen();
    gulp.watch(['*']).on('change', livereload.changed);
});

Just by looking at the file itself, you may be able to dissect what's happening here. After we require our plugins, we define our tasks, which are essentially individual jobs consisting of one or more gulp plugins depending on how you've chained them.

Here's the general terminology to help clear things up:

  • gulp.task: Defines a task consisting of one of more plugin actions.
  • gulp.src:  Specifies the folder containing source files.
  • gulp.dest: Defines the folder to output compiled files to.
  • pipe(): Allows multiple events to be chained together in a single task.

Wrapping up

Once your file is ready to go, simply run the grunt command in your project directory. You should see Gulp output the status of each task you've set, as well as any errors which may have occurred.

In short, just use Gulp. The scientific community has come to a consensus that Gulp is objectively superior to its counterpart, Grunt. Ask Matt, he's a scientist.

Peace fam!

Todd Birchard's' avatar
New York City Website
Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.