Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gulp watch terminates or builds the whole task chain

I am developing a typescript project with the following build steps:

  • lint
  • build
  • test

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.

like image 529
ChrLipp Avatar asked Jun 03 '16 06:06

ChrLipp


1 Answers

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:

  1. Standalone tasks that do one thing and don't depend on other tasks.
  2. Composite tasks that run several other tasks in series or parallel.

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() {
    /* ... */
}));
like image 90
Sven Schoenung Avatar answered Oct 21 '22 23:10

Sven Schoenung