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#