Updating A Gulp Script

One of the challenges with development is that tools and libraries evolve over time. It often becomes necessary to either upgrade to a newer library or change libraries entirely. The update from Grub 3.x to 4.x required a bit of refactoring. Neither script is overly complex but there is enough there to illustrate some of the differences.

I always think of Phil Hartman’s Saturday Night Live character whenever I think of sassy.

Original Script

This is the original version of the script. It’s pretty basic.

/*
  This is a gulp workflow based around a sass file called app.scss
  It compiles and then watches for changes in themes using the folder
  structure shown below. The source and destination are set to the
  specific theme name.

  To make it work, you make to install:

  $ npm install gulp gulp-sass gulp-concat gulp-clean-css gulp-notify

  $ gulp
*/

var gulp = require('gulp');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var cleanCSS = require('gulp-clean-css');
var notify = require('gulp-notify');

var sourcePath = './source';
var destPath = './build';

var vendor_js = [
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
];

var vendor_css = [
    "node_modules/bootstrap/dist/css/bootstrap.min.css",
    "node_modules/font-awesome/css/font-awesome.min.css"
];

gulp.task('sassy', function() {
  gulp.src(sourcePath + '/scss/app.scss')
    .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
    .pipe(concat('style.min.css'))
    .pipe(cleanCSS({compatibility: 'ie8'}))
    .pipe(gulp.dest(destPath + '/css/'))
    .pipe(notify({ title: "Gulp!", message: "Sassy compilation...sassy!" }));
});

gulp.task('copy', async function () {
  gulp.src(sourcePath + '/**/*.php')
    .pipe(gulp.dest(destPath));
});

gulp.task('vendor-concat', function() {
    gulp.src(vendor_js)
        .pipe(concat('vendor.js'))
        .pipe(gulp.dest(sourcePath + '/vendor/js'));
    gulp.src(vendor_css)
      .pipe(concat('vendor.css'))
      .pipe(cleanCSS({compatibility: 'ie8'}))
      .pipe(gulp.dest(destPath + '/vendor/css'));
});

gulp.task('watch', function() {
  gulp.watch('source/scss/*.scss', ['sassy']);
  gulp.watch('source/**/*.php', ['copy']);
});

gulp.task('default', ['sassy', 'copy', 'vendor-concat']);

Upgrading Gulp

The gulp installation instructions recommend uninstalling the existing gulp instance if present and then installing gulp at the global level. Both steps were necessary for me to get up and running. I installed it following the Quick Start Instructions.

New Script

Once I had gulp installed, I went about refactoring the script. I stripped down the gulpfile to the most basic implementation and then began adding the individual functions.

Callback Functions

I found this was the most noticeable difference between the two approaches. I also noticed there was some variation with how the callback was handled. In some examples, the function is returning the value directly and in others it is passing through the callback function. To keep it straightforward and consistent, I used the callback in all instances. If neither a callback not a return statement were present, the script would stall.


Who Watches The Watchers

After I refactored the functions and was able to execute them individually, I added a watcher function. The syntax was pretty similar. Since I only have php and scss files to worry about, I just scan for changes to those file extensions. The php files are copied directly, the scss files are processed to css.

Default Series

To execute multiple commands for the default gulp process, I defined a series that included each of the four steps and ends with the watcher. This ensures that the project is cleaned, the vendor files copied, the php and scss files refreshed, and updates are handled accordingly.

Updated Gulpfile

/*
  This is a gulp workflow based around watching for changes to scss and php 
  files and copying specific vendor files

  To make it work, npm install package.json or individually:

  $ npm install gulp gulp-sass gulp-concat gulp-clean-css del

  $ gulp
*/

const gulp = require('gulp');
const { series } = require('gulp');
const { watch } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const concat = require('gulp-concat');
const cleanCSS = require('gulp-clean-css');
const del = require('del');

const sourcePath = './source';
const destPath = './build';

const vendor_js = [
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
];

const vendor_css = [
    "node_modules/bootstrap/dist/css/bootstrap.min.css",
    "node_modules/font-awesome/css/font-awesome.min.css"
];

function clean(cb) {
  del([ 'destPath' ]);

  cb();
}

function copy(cb) {
  gulp.src(sourcePath + '/**/*.php')
    .pipe(gulp.dest(destPath));

    cb();
}

function sassy(cb) {
  gulp.src(sourcePath + '/scss/app.scss')
    .pipe(concat('style.min.css'))
    .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
    .pipe(cleanCSS({compatibility: 'ie8'}))
    .pipe(gulp.dest(destPath + '/css'));

  cb();
};

function vendorConcat(cb) {
  gulp.src(vendor_js)
  .pipe(concat('vendor.js'))
  .pipe(gulp.dest(destPath + '/vendor/js'));

  gulp.src(vendor_css)
  .pipe(concat('vendor.css'))
  .pipe(gulp.dest(destPath + '/vendor/css'));

  cb();
}

function watcher(cb) {
  watch(sourcePath + '/scss/*.scss', sassy);
  watch(sourcePath + '/**/*.php', copy);
  
  cb();
};

exports.clean = clean;
exports.sassy = sassy;
exports.copy = copy;
exports.vendorConcat = vendorConcat;
exports.watcher = watcher;

exports.default = series(clean, copy, sassy, vendorConcat, watcher);

Run, Task Runner

The purpose of using a task runner like Gulp or Grunt is to reduce the number of repetitive tasks. The possibilities extend well beyond the script. Other options like Webpack or npm scripts might be more suitable, depending on the circumstances. Whatever the approach, it should be flexible enough to do everything you need without being burdensome.