Search code examples
gulpbuild-automationgulp-watch

Create a normal "build" task with gulp-watch without having to duplicate tasks


In the past I've used gulp.watch to create a "watch" task which I use during development. In the "watch" task, I would have an individual watch which would just run whatever was needed. For example:

gulp.task("watch", function () {
    gulp.watch("**/*.js", ["js"]);
    gulp.watch("**/*.less", ["less"]);
});

And then a "build" task which would run everything together:

gulp.task("build", ["js", "less"]);

Works well, except it means that -ever- LESS file needs to be recompiled upon a single change, or every JS file for a single change.

I've switched to using gulp-watch instead, so I can now have it do incremental builds. I now no longer have a "watch" task an instead have tasks that look like this:

gulp.task("less", function () {
    return gulp.src("**/*.less")
        .pipe(watch("**/*.less"))
        .pipe(less())
        .pipe(gulp.dest("dist"));
});

This works great for incremental builds, but now I can't figure out a clean way to have a "build" task which just builds without kicking it in to a watch-mode, for things such as deployments.

Question: Is there a standard/clean way for me to create a "build" task, without watching, without having to basically duplicate all of my tasks?


Solution

  • The solution I ended up coming up with is to use a global debug value, which is set to true normally, but the build task sets to it false. Then, in the tasks, watch is only added to the pipe if debug == true.

    Example:

    var gulp = require('gulp'),
        runSequence = require('run-sequence'),
        watch = require('gulp-watch');
    var debug = true;
    
    gulp.task('copy', function () {
        var task = gulp.src('**/*.js');
    
        debug && task.pipe(watch());
    
        task.pipe(gulp.dest('dist'));
    });
    
    gulp.task('build:dev', ['copy']);
    
    gulp.task('build', function (cb) {
        debug = false;
        runSequence('build:dev', cb);
    });
    

    If I run build:dev, it'll run copy and put copy in to watch mode, because watch is piped on because the global debug value is true.

    If I run build, it sets the global debug value to false, so the watch isn't piped in, meaning it will just do the task without watching.

    I can also add things only for debug == false as well (like, uglifying).

    This isn't super clean and doesn't fit super well with the gulp philosophy, but it works reasonably well, so I'm satisfied with it.

    =========

    Update for Gulp 4.0

    I've started playing with Gulp 4.0 lately, and you now can't use runSequence (or at the very least, it's redundant), so you can't set the variable then call runSequence. What I ended up doing instead was creating a task named "debug:off" which I call at the start of my build chain:

    var gulp = require('gulp');
    var debug = true;
    
    gulp.task('debug:off', function (cb) { 
        debug = false;
        cb();
    });
    
    // ...
    
    gulp.task('build:dev', gulp.parallel('task1', 'task2'));
    gulp.task('build', gulp.serial('debug:off', 'build:dev'));
    

    Another way is instead of using a variable like that, you could also set an environment variable when you run Gulp. Works for all versions of Gulp.

    // gulpfile.js
    var debug = process.env.PROD
    
    // command-line, debug mode
    $ gulp
    
    // command-line, prod mode
    $ PROD=1 && gulp
    

    The environment variable isn't my preference, but others seem to prefer it to using a task to configure a variable.