Prevent Errors from breaking Gulp watch

gulp-plumber, custom error handler, gulp-prettyerror

As an intermediate javascript developer, you may using gulp these days – a great and straightforward streaming build system with a lot of advantages compared to grunt. For example, i’ve switched from a bunch of custom, ANT based scripts to gulp for the next EnlighterJS major version and it saves a lot of time!

Especially the watch tasks, which can automatically run partial build tasks when updating files. But this can cause serious trouble during the development process, in case there are some errors in your code – the task will break and you have to restart it manually.

Running a watch task#

In case a subtask failed, the whole watch task will stop without a proper error handler set. Well, you known this issue..

File /home/andi/Development/Javascript/EnlighterJS3/Source/Language/Xml.js was changed, running tasks...
[09:36:24] Starting 'jsx'...
[09:36:24] Finished 'jsx' after 8.59 ms
[09:36:24] Starting 'browser-js'...

events.js:141
      throw er; // Unhandled 'error' event
      ^
SyntaxError: /home/andi/Development/Javascript/EnlighterJS3/.tmp/EnlighterJS.browser.js: Unexpected token (1070:12)
  1068 |                 type: 'x1',
  1069 |                 filter:
> 1070 |             }
       |             ^
  1071 |         ];
  1072 |     }
  1073 | };
    at Parser.pp.raise (babel-core/node_modules/babylon/index.js:1413:13)
    at Parser.pp.unexpected (babel-core/node_modules/babylon/index.js:2895:8)
    at Parser.pp.parseExprAtom (babel-core/node_modules/babylon/index.js:746:12)
    at Parser.pp.parseExprSubscripts (babel-core/node_modules/babylon/index.js:501:19)
    at Parser.pp.parseMaybeUnary (babel-core/node_modules/babylon/index.js:481:19)
    at Parser.pp.parseExprOps (babel-core/node_modules/babylon/index.js:412:19)
    at Parser.pp.parseMaybeConditional (babel-core/node_modules/babylon/index.js:394:19)
    at Parser.pp.parseMaybeAssign (babel-core/node_modules/babylon/index.js:357:19)
    at Parser.pp.parseObjPropValue (babel-core/node_modules/babylon/index.js:1013:99)
    at Parser.pp.parseObj (babel-core/node_modules/babylon/index.js:986:10)

Plumber as Helper#

Generally, you have to add a separate onError callback to all piped task. It produces a lot of coding overhead. Plumber can do this job for you and also takes care of the streams –  Briefly it replaces pipe method and removes standard onerror handler on error event.

Example – Catch all errors and stop pipe processing#

var plumber = require('gulp-plumber');

gulp.src('./src/*.scss')
    .pipe(plumber(function(error){
        console.log("Error happend!", error.message);
        this.emit('end');
    }))
    .pipe(sass())
    .pipe(uglify())
    .pipe(plumber.stop())
    .pipe(gulp.dest('./dist'));

Putting it all together#

Well, the output doesn’t look very nice. To obtain the gulp output appearance (timeline, colors), we can use the log() method of the gulp-util package. Additionally, the plumber() call is wrapper into to helper function to use it in multiple tasks.

var gulp = require("gulp");
var gutil = require('gulp-util');
var gplumber = require('gulp-plumber');
// our custom error handler
var errorHandler = function(){
    // default appearance
    return gplumber(function(error){
        // add indentation
        var msg = error.codeFrame.replace(/\n/g, '\n    ');

        // output styling
        gutil.log('|- ' + gutil.colors.bgRed.bold('Build Error in ' + error.plugin));
        gutil.log('|- ' + gutil.colors.bgRed.bold(error.message));
        gutil.log('|- ' + gutil.colors.bgRed('>>>'));
        gutil.log('|\n    ' + msg + '\n           |');
        gutil.log('|- ' + gutil.colors.bgRed('<<<'));
    });
};

// Out Tasks
gulp.task('jsx', function () {
    return gulp.src(['Source/Views/*.jsx'])
        .pipe(errorHandler())
        .pipe(...)
    ;
};
gulp.task('js', function () {
    return gulp.src(['Source/**.js'])
        .pipe(errorHandler())
        .pipe(...)
    ;
};

Finally, the Quick Way#

To quickly enable this functionality, i’ve create the gulp-prettyerror package, which wraps the code above into a single, easy to use function:

var prettyError = require('gulp-prettyerror');

// default release build
gulp.task('browser-js', ['jsx'], function (){
    return gulp.src(['Source/Lib/**/*.js', 'Source/Browser/**/*.js', 'Source/Engine/**/*.js', '.tmp/Views.js'].concat(languageSources))
        .pipe(prettyError())

        // create sourcemaps for development
        .pipe(sourcemaps.init())

        // concat all files
        .pipe(concat('EnlighterJS.browser.js'));
});

The Result#

screenshot1