Prevent errors from breaking / crashing gulp watch

后端 未结 8 839
我在风中等你
我在风中等你 2020-12-12 09:17

I\'m running gulp 3.6.2 and have the following task that was set up from a sample online

gulp.task(\'watch\', [\'default\'], function () {
  gulp.watch([
            


        
相关标签:
8条回答
  • 2020-12-12 09:37

    Typescript

    This is what worked for me. I work with Typescript and separated the function (to aovid confusion with this keyword) to handle less. This works with Javascript as well.

    var gulp = require('gulp');
    var less = require('gulp-less');
    
    gulp.task('less', function() {
        // writing a function to avoid confusion of 'this'
        var l = less({});
        l.on('error', function(err) {
            // *****
            // Handle the error as you like
            // *****
            l.emit('end');
        });
    
        return gulp
            .src('path/to/.less')
            .pipe(l)
            .pipe(gulp.dest('path/to/css/output/dir'))
    })
    

    Now, when you watch .less files, and an error occurs, the watch will not stop and new changes will processed as per your less task.

    NOTE : I tried with l.end();; however, it did not work. However, l.emit('end'); totally works.

    Hope this help. Good Luck.

    0 讨论(0)
  • 2020-12-12 09:42

    Your swallowError function should look like this:

    function swallowError (error) {
    
      // If you want details of the error in the console
      console.log(error.toString())
    
      this.emit('end')
    }
    

    I think you have to bind this function on the error event of the task that was falling, not the watch task, because that's not where comes the problem, you should set this error callback on each task that may fail, like plugins that breaks when you have missed a ; or something else, to prevent watch task to stop.

    Examples :

    gulp.task('all', function () {
      gulp.src('./app/script/*.coffee')
        .pipe(coffee({ bare: true }))
        .on('error', swallowError)
        .pipe(gulp.dest('./public/js'))
    
      gulp.src('css/*.scss')
        .pipe(sass({ compass: true }))
        .on('error', swallowError)
        .pipe(cssmin())
        .pipe(gulp.dest('dist'))
    })
    

    Alternately, if you don't mind to include another module, you can use the log function of gulp-util to keep you from declare an extra function in your gulpfile:

    .on('error', gutil.log)
    

    But I may recommend having a look at the awesome gulp-plumber plugin, which is used to remove the onerror handler of the error event, causing the break of the streams. It's very simple to use and it stops you from catch all the tasks that may fail.

    gulp.src('./app/script/*.coffee')
      .pipe(plumber())
      .pipe(coffee({ bare: true }))
      .pipe(gulp.dest('./public/js'))
    

    More info about this on this article by the creator of the concerned plugin.

    0 讨论(0)
  • 2020-12-12 09:42

    With one format of files

    (ex: *.coffee only)

    If you want to work only with one format of files, then gulp-plumber is your solution.

    For example rich handled errors and warning for coffeescripting:

    gulp.task('scripts', function() {
      return gulp.src(['assets/scripts/**/*.coffee'])
        .pipe(plumber())
        .pipe(coffeelint())
        .pipe(coffeelint.reporter())
        .pipe(lintThreshold(10, 0, lintThresholdHandler))
        .pipe(coffee({
          bare: true
        }))
        .on('error', swallowError)
        .pipe(concat('application.js'))
        .pipe(gulp.dest('dist/scripts'))
        .pipe(rename({ suffix: '.min' }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/scripts'))
        .pipe(notify({ message: 'Scripts task complete' }));
    });
    

    With multiple types of file formats

    (ex: *.coffee and *.js at same time)

    But if you won't to work with multiple types of file formats (for example: *.js and *.coffee), than i will post my solution.

    I will just post a self explanatory code over here, with some description before.

    gulp.task('scripts', function() {
      // plumber don't fetch errors inside gulpif(.., coffee(...)) while in watch process
      return gulp.src(['assets/scripts/**/*.js', 'assets/scripts/**/*.coffee'])
        .pipe(plumber())
        .pipe(gulpif(/[.]coffee$/, coffeelint()))
        .pipe(coffeelint.reporter())
        .pipe(lintThreshold(10, 0, lintThresholdHandler))
        .pipe(gulpif(/[.]coffee$/, coffee({ // if some error occurs on this step, plumber won't catch it
          bare: true
        })))
        .on('error', swallowError)
        .pipe(concat('application.js'))
        .pipe(gulp.dest('dist/scripts'))
        .pipe(rename({ suffix: '.min' }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/scripts'))
        .pipe(notify({ message: 'Scripts task complete' }));
    });
    

    I faced the issue with gulp-plumber and gulp-if using gulp.watch(...

    See related issue here: https://github.com/floatdrop/gulp-plumber/issues/23

    So the best option for me was:

    • Each part as file, and concatenate after. Create multiple tasks that can process each part in separate file (like grunt does), and concatenate them
    • Each part as stream, and merge streams after. Merge two streams using merge-stream (that was made from event-stream) into one and continue the job (i tried that first, and it work fine for me, so it is faster solution than previous one)

    Each part as stream, and merge streams after

    Her is the main part of my code:

    gulp.task('scripts', function() {
      coffeed = gulp.src(['assets/scripts/**/*.coffee'])
        .pipe(plumber())
        .pipe(coffeelint())
        .pipe(coffeelint.reporter())
        .pipe(lintThreshold(10, 0, lintThresholdHandler))
        .pipe(coffee({
          bare: true
        }))
        .on('error', swallowError);
    
      jsfiles = gulp.src(['assets/scripts/**/*.js']);
    
      return merge([jsfiles, coffeed])
        .pipe(concat('application.js'))
        .pipe(gulp.dest('dist/scripts'))
        .pipe(rename({ suffix: '.min' }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/scripts'))
        .pipe(notify({ message: 'Scripts task complete' }));
    });
    

    Each part as file, and concatenate after

    If to separate this into parts, then in each part there should be a result file created. For ex.:

    gulp.task('scripts-coffee', function() {
    
      return gulp.src(['assets/scripts/**/*.coffee'])
        .pipe(plumber())
        .pipe(coffeelint())
        .pipe(coffeelint.reporter())
        .pipe(lintThreshold(10, 0, lintThresholdHandler))
        .pipe(coffee({
          bare: true
        }))
        .on('error', swallowError)
        .pipe(concat('application-coffee.js'))
        .pipe(gulp.dest('dist/scripts'));
    
    });
    
    gulp.task('scripts-js', function() {
    
      return gulp.src(['assets/scripts/**/*.js'])
        .pipe(concat('application-coffee.js'))
        .pipe(gulp.dest('dist/scripts'));
    
    });
    
    gulp.task('scripts', ['scripts-js', 'scripts-coffee'], function() {
    
      var re = gulp.src([
        'dist/scripts/application-js.js', 'dist/scripts/application-coffee.js'
      ])
        .pipe(concat('application.js'))
        .pipe(gulp.dest('dist/scripts'))
        .pipe(rename({ suffix: '.min' }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/scripts'))
        .pipe(notify({ message: 'Scripts task complete' }));
    
      del(['dist/scripts/application-js.js', 'dist/scripts/application-coffee.js']);
    
      return re;
    
    });
    

    P.S.:

    Here node modules and functions that were used:

    // Load plugins
    var gulp = require('gulp'),
        uglify = require('gulp-uglify'),
        rename = require('gulp-rename'),
        concat = require('gulp-concat'),
        notify = require('gulp-notify'),
        plumber = require('gulp-plumber'),
        merge = require('ordered-merge-stream'),
        replace = require('gulp-replace'),
        del = require('del'),
        gulpif = require('gulp-if'),
        gulputil = require('gulp-util'),
        coffee = require('gulp-coffee'),
        coffeelint = require('gulp-coffeelint),
        lintThreshold = require('gulp-coffeelint-threshold');
    
    var lintThresholdHandler = function(numberOfWarnings, numberOfErrors) {
      var msg;
      gulputil.beep();
      msg = 'CoffeeLint failure; see above. Warning count: ';
      msg += numberOfWarnings;
      msg += '. Error count: ' + numberOfErrors + '.';
      gulputil.log(msg);
    };
    var swallowError = function(err) {
      gulputil.log(err.toString());
      this.emit('end');
    };
    
    0 讨论(0)
  • 2020-12-12 09:48

    I like to use gulp plumber because it can add a global listener to a task and have a meaningful message displayed.

    var plumber = require('gulp-plumber');
    
    gulp.task('compile-scss', function () {
        gulp.src('scss/main.scss')
            .pipe(plumber())
            .pipe(sass())
            .pipe(autoprefixer())
            .pipe(cssnano())
            .pipe(gulp.dest('css/'));
    });
    

    Reference : https://scotch.io/tutorials/prevent-errors-from-crashing-gulp-watch

    0 讨论(0)
  • 2020-12-12 09:50

    This worked for me ->

    var gulp = require('gulp');
    var sass = require('gulp-sass');
    
    gulp.task('sass', function(){
        setTimeout(function(){
            return gulp.src('sass/*.sass')
            .pipe(sass({indentedSyntax: true}))
            .on('error', console.error.bind(console))
            .pipe(gulp.dest('sass'));
        }, 300);
    });
    
    
    
    gulp.task('watch', function(){
        gulp.watch('sass/*.sass', ['sass']);
    });
    
    gulp.task('default', ['sass', 'watch'])
    

    I just added the .on('error', console.error.bind(console)) line, but I had to run the gulp command as root. I'm running node gulp on a php application so I have multiple accounts on one server, which is why I ran into the issue of gulp breaking on syntax errors because I was not running gulp as root... Maybe plumber and some of the other answers here would have worked for me if I ran as root. Credit to Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw for the answer. He said that by handling the error it helps you to determine what line the error is on and what it is in the console, but also stops gulp from breaking on syntax error. He said it was kind of a light weight fix, so not sure if it will work for what you are looking for. Quick fix though, worth a shot. Hope this helps someone!

    0 讨论(0)
  • 2020-12-12 09:55

    I have implemented the following hack as a workaround for https://github.com/gulpjs/gulp/issues/71:

    // Workaround for https://github.com/gulpjs/gulp/issues/71
    var origSrc = gulp.src;
    gulp.src = function () {
        return fixPipe(origSrc.apply(this, arguments));
    };
    function fixPipe(stream) {
        var origPipe = stream.pipe;
        stream.pipe = function (dest) {
            arguments[0] = dest.on('error', function (error) {
                var state = dest._readableState,
                    pipesCount = state.pipesCount,
                    pipes = state.pipes;
                if (pipesCount === 1) {
                    pipes.emit('error', error);
                } else if (pipesCount > 1) {
                    pipes.forEach(function (pipe) {
                        pipe.emit('error', error);
                    });
                } else if (dest.listeners('error').length === 1) {
                    throw error;
                }
            });
            return fixPipe(origPipe.apply(this, arguments));
        };
        return stream;
    }
    

    Add it to your gulpfile.js and use it like that:

    gulp.src(src)
        // ...
        .pipe(uglify({compress: {}}))
        .pipe(gulp.dest('./dist'))
        .on('error', function (error) {
            console.error('' + error);
        });
    

    This feels like the most natural error handling to me. If there is no error handler at all, it will throw an error. Tested with Node v0.11.13.

    0 讨论(0)
提交回复
热议问题