Last week my team began prototyping a small web app. The scope of the project was small enough to rule out a full Rails site or JavaScript framework, but we still wanted to have some tooling available such as Sass and Browsersync.

Our preferred way to bring these tools together is by using Gulp. But we always run into a gotcha when configuring Browsersync with Gulp - the Browsersync docs aren't updated for Gulp 4.

The Browsersync Gulp docs suggest the following configuration:

// gulpfile.js
var gulp        = require('gulp');
var browserSync = require('browser-sync').create();
var sass        = require('gulp-sass');

// Static Server + watching scss/html files
gulp.task('serve', ['sass'], function() {

    browserSync.init({
        server: "./app"
    });

    gulp.watch("app/scss/*.scss", ['sass']);
    gulp.watch("app/*.html").on('change', browserSync.reload);
});

// Compile sass into CSS & auto-inject into browsers
gulp.task('sass', function() {
    return gulp.src("app/scss/*.scss")
        .pipe(sass())
        .pipe(gulp.dest("app/css"))
        .pipe(browserSync.stream());
});

gulp.task('default', ['serve']);

But this syntax no longer works in Gulp 4. Instead, you'll have to use the new gulp series() with async completion, like so:

// gulpfile.js
gulp.task('sass', function (done) {
    gulp.src("app/scss")
        .pipe(sass().on('error', sass.logError))
        .pipe(gulp.dest("app/css"))
        // Use stream since browsersync only cares about when files are finished compiling
        .pipe(browserSync.stream());
        done();
});

gulp.task('default', gulp.series('sass', function(){

    browserSync.init({
        server: './app'
    });

    gulp.watch("app/scss", gulp.series('sass'));
    gulp.watch('*.html').on('change', browserSync.reload);
}));

I adapted this workaround from Liquid Light's guide to upgrading to Gulp 4.

Gulp series

When running tasks in a series (such as serving, watching, and compiling Sass files with Browsersync), gulp now uses the series(), which has a different callback signature than the Browsersync docs indicate.

Async completion

Async completion is a way to denote that a serial task has completed. For a simple usecase like this, we can pass a done argument to the callback function in the sass task, and call it at the end with done();.