I am developing a typescript project with the following build steps:
I am using Gulp 4.0 as a build tool and want to have a watch task, which should trigger the test (which means that lint and build tasks are triggered prior). Currently, when an error occurs (for example a lint error), the watch task terminates.
This problem is well known and easy to solve. The typical solution is a) to prevent errors or b) to patch the pipe behaviour.
a) For the gulp-tslint I could use this config from their homepage:
gulp.task("invalid-noemit", () =>
gulp.src("input.ts")
.pipe(tslint())
.pipe(tslint.report("prose", {
emitError: false
}))
);
But when I include the emitError
flag, the lint errors are logged and all following gulp tasks are executed (build, test).
b) I could also use gulp-plumber
or catch errors manually (see here), but the behavior is the same for all this known solutions, the following gulp tasks are executed (build, test).
What I want is that the task chain stops after an error (no build and test after an lint error), but the watch task should never stop. How could I solve this? The watcher tasks look like this:
// watcher
gulp.task('watch', gulp.series('test', function doWatch() {
gulp.watch([
config.paths.sourcePattern,
config.paths.testPattern,
'gulpfile.js'
], gulp.parallel('test'));
}));
You can find the complete gulpfile.js
here.
The reason your watch stops is because an err
object is propagated up the callback chain. You have to prevent that err
from reaching the final gulp.watch()
callback.
You can do that by wrapping the callback provided by gulp.watch()
and never passing the err
object on to the original callback:
gulp.task('watch', function() {
gulp.watch([
config.paths.sourcePattern,
config.paths.testPattern,
'gulpfile.js'
], {ignoreInitial:false}, function(cb) {
gulp.series('lint', 'build', 'test')(function wrappedCb(err) {
cb(); // not passing err here
});
});
});
Note that gulp.series('lint', 'build', 'test')
doesn't actually execute the tasks. It merely returns a new function that accepts a callback. Only when this new function is invoked as gulp.series('lint', 'build', 'test')()
are the tasks actually executed.
I also added the ignoreInitial
option so that the watch is executed once after startup which seems to be what you're trying to achieve with gulp.series('test', ...)
in your watch
task.
(Aside: watching gulpfile.js
is useless. Changes to your gulpfile will not take effect until you rerun gulp watch
. There's no way around this.)
Finally you need to decouple your other tasks, so they don't have explicit dependencies on other tasks. It's tempting to translate a gulp 3.x task like this:
gulp.task('foo', ['bar'], function() { });
Into a gulp 4.x task like this:
gulp.task('foo', gulp.series('bar', function() { }));
They look similar on the surface, but they're entirely different under the hood. See this article for more on that subject.
One good strategy is to organize your tasks into two categories:
Following this principle your other tasks can be refactored into this:
gulp.task('lint', function() {
return gulp.src([
config.paths.sourcePattern,
config.paths.testPattern
])
.pipe(tslint())
.pipe(tslint.report('verbose', {
emitError: true, // we WANT to emit this err so our other tasks don't run
summarizeFailureOutput: true
}));
});
gulp.task('build-app', function doBuildApp() {
/* ... */
});
gulp.task('build-test', function doBuildTest() {
/* ... */
});
gulp.task('build', gulp.series('lint', 'build-app', 'build-test'));
gulp.task('test', gulp.series(function doPreTest() {
/* ... */
}, function doTest() {
/* ... */
}, function doPostTest() {
/* ... */
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With