Web Tooling and Automatisation using gulp 4

Now working with Gulp you will discover that you run into a couple of minor problems. Especially in sequencing the different types of tasks.
Let’s take a simple clean task, it should run before all of the other tasks. However, Gulp will run all tasks in parallel.

The team that is working on Gulp has been working on a solution for this problem and in Gulp 4.0 we will get gulp.series() and gulp.parallel(); to distinguish between these two types of operations.

Sadly it looks like that currently the release of Gulp 4.0 is delayed. However, you can use the current Alpha version prior to its release.

Installing Gulp 4

You can simply install the next version of Gulp with NPM (A git client is required, as the package is not in the npm repository and will be cloned from Github)

npm install gulpjs/gulp#4.0

Updating the gulpfile.js

The syntax for tasks has changed, so we need to rewrite parts of the file:
Our existing Gulp 3 task

gulp.task("default", ["html", "scripts", "styles", "images"]);

Needs to be rewritten with gulp parallel as such:

gulp.task("default", gulp.parallel(html, scripts, styles, images));

However, we should not stop there and improve upon this task, by running the clean task before all of the other tasks. To ensure that it runs before the other tasks we use gulp.series().

gulp.task("default",
    gulp.series(cleanDist, gulp.parallel(html, scripts, styles, images))
);

Improving on the production flag

With gulp.series() we now can stop using the –production flag. We simply define a production task.

As first operation we pass in a function that simply sets the production boolean to true:

gulp.task('production', gulp.series((done) => { production = true; done(); }, 'default'));

The function uses “done” to signal gulp that the function has completed.

Hiding Tasks

Another improvement of Gulp 4.0 is that you can pass functions, as well as tasks to gulp.series and gulp.parallel.

This, in turn, lets you write normal functions that are hidden from the command line, ensuring that everybody on the project runs the default build task instead of only the HTML task.

Here is now our improved Gulpfile.js:

//Gulp Packages
const gulp = require('gulp');
const gulpIf = require('gulp-if');
const browserSync = require('browser-sync').create();
const clean = require('gulp-clean');

const pug = require("gulp-pug");

const imagemin = require('gulp-imagemin');

const sass = require("gulp-sass");
const autoprefixer = require("gulp-autoprefixer");

const ts = require("gulp-typescript");
const tslint = require("gulp-tslint");
const uglify = require('gulp-uglify');

const jasmine = require("gulp-jasmine-phantom")

//Global Variables
var production = false;

//Path Definitions
const paths = {
    html: {
        src: "src/views/**/*.pug",
        dest: "dist"
    },
    styles: {
        src: "src/styles/**/*.scss",
        dest: "dist/styles"
    },
    scripts: {
        src: "src/scripts/**/*.ts",
        dest: "dist/scripts"
    },
    images: {
        src: "src/img/*",
        dest: "dist/img"
    }
}

//Internal Tasks
function cleanDist() {
    return gulp.src('dist')
        .pipe(clean())
}

function html() {
    return gulp.src(paths.html.src)
        .pipe(pug())
        .pipe(gulp.dest(paths.html.dest))
}

function scripts () {
    return gulp.src(paths.scripts.src)
        .pipe(ts({
            out: "output.js"
        }))
        .pipe(tslint({
            formatter: "verbose"
        }))
        .pipe(tslint.report())
        .pipe(gulpIf(production, uglify()))
        .pipe(gulp.dest(paths.scripts.dest));
}

function styles() {
    let sassOptions = {};
    if (production) {
        sassOptions = {
            outputStyle: 'compressed'
        }
    }
    return gulp.src(paths.styles.src)
        .pipe(sass(sassOptions).on('error', sass.logError))
        .pipe(autoprefixer({
            browsers: ['last 2 versions']
        }))
        .pipe(gulp.dest(paths.styles.dest))
        .pipe(browserSync.stream())
}

function images () {
    return gulp.src(paths.images.src)
        .pipe(gulpIf(production, imagemin()))
        .pipe(gulp.dest(paths.images.dest));
}

//External Tasks

gulp.task("tests", () => {
    return gulp.src("src/tests/test.js")
        .pipe(jasmine({
            integration: true,
            vendor: '_build/**/*.js'
        }));
})

gulp.task("default",
    gulp.series(cleanDist, gulp.parallel(html, scripts, styles, images))
);

gulp.task('production', gulp.series((done) => { production = true; done(); }, 'default'));

gulp.task("serve", gulp.series('default', () => {
    browserSync.init({
        server: "./dist"
    });
    gulp.watch(paths.styles.src, gulp.series(styles));
    gulp.watch(paths.scripts.src,  gulp.series(scripts));
    gulp.watch(paths.images.src, gulp.series(images));
    gulp.watch(paths.html.src, gulp.series(html)).on('change', browserSync.reload);
}));

Package.json

{
  "name": "gulp4-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "browser-sync": "^2.18.7",
    "eslint": "^3.15.0",
    "gulp": "github:gulpjs/gulp#4.0",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-clean": "^0.3.2",
    "gulp-eslint": "^3.0.1",
    "gulp-if": "^2.0.2",
    "gulp-imagemin": "^3.1.1",
    "gulp-jasmine-phantom": "^3.0.0",
    "gulp-pug": "^3.2.0",
    "gulp-sass": "^3.1.0",
    "gulp-tslint": "^7.1.0",
    "gulp-typescript": "^3.1.4",
    "gulp-uglify": "^2.1.2",
    "phantomjs": "^2.1.7",
    "tslint": "^4.4.2",
    "typescript": "^2.1.6",
    "typescript-eslint-parser": "^1.0.3"
  }
}