web-dev-qa-db-fra.com

Empêcher les erreurs de briser/écraser gulp watch

J'utilise gulp 3.6.2 et la tâche suivante a été configurée à partir d'un exemple en ligne.

gulp.task('watch', ['default'], function () {
  gulp.watch([
    'views/**/*.html',        
    'public/**/*.js',
    'public/**/*.css'        
  ], function (event) {
    return gulp.src(event.path)
      .pipe(refresh(lrserver));
  });

  gulp.watch(['./app/**/*.coffee'],['scripts']);
  gulp.watch('./app/**/*.scss',['scss']);
});

Chaque fois qu'il y a une erreur dans ma montre CoffeeScript Gulp, la montre s'arrête - évidemment pas ce que je veux.

Comme recommandé ailleurs j'ai essayé ceci

gulp.watch(['./app/**/*.coffee'],['scripts']).on('error', swallowError);
gulp.watch('./app/**/*.scss',['scss']).on('error', swallowError);
function swallowError (error) { error.end(); }

mais cela ne semble pas fonctionner.

Qu'est-ce que je fais mal?


En réponse à la réponse de @ Aperçu, j'ai modifié ma méthode swallowError et essayé à la place:

gulp.task('scripts', function () {
  gulp.src('./app/script/*.coffee')
    .pipe(coffee({ bare: true }))
    .pipe(gulp.dest('./public/js'))
    .on('error', swallowError);
});

Redémarré, puis créé une erreur de syntaxe dans mon fichier café. Même problème:

[gulp] Finished 'scripts' after 306 μs

stream.js:94
      throw er; // Unhandled stream error in pipe.
            ^
Error: W:\bariokart\app\script\trishell.coffee:5:1: error: unexpected *
*
^
  at Stream.modifyFile (W:\bariokart\node_modules\gulp-coffee\index.js:37:33)
  at Stream.stream.write (W:\bariokart\node_modules\gulp-coffee\node_modules\event-stream\node_modules\through\index.js:26:11)
  at Stream.ondata (stream.js:51:26)
  at Stream.EventEmitter.emit (events.js:95:17)
  at queueData (W:\bariokart\node_modules\gulp\node_modules\vinyl-fs\node_modules\map-stream\index.js:43:21)
  at next (W:\bariokart\node_modules\gulp\node_modules\vinyl-fs\node_modules\map-stream\index.js:71:7)
  at W:\bariokart\node_modules\gulp\node_modules\vinyl-fs\node_modules\map-stream\index.js:85:7
  at W:\bariokart\node_modules\gulp\node_modules\vinyl-fs\lib\src\bufferFile.js:8:5
  at fs.js:266:14
  at W:\bariokart\node_modules\gulp\node_modules\vinyl-fs\node_modules\graceful-fs\graceful-fs.js:104:5
  at Object.oncomplete (fs.js:107:15)
168
George Mauer

Votre fonction swallowError devrait ressembler à ceci:

function swallowError (error) {

  // If you want details of the error in the console
  console.log(error.toString())

  this.emit('end')
}

Je pense que vous devez lier cette fonction à l’événement error de la tâche en train de tomber, et non à la tâche watch, car ce n’est pas la source du problème, vous devez définir ce rappel d’erreur sur chaque tâche susceptible d’échouer, comme les plugins qui se cassent lorsque vous avez manqué un ; ou autre chose pour empêcher la tâche watch de s'arrêter.

Exemples :

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'))
})

Sinon, si cela ne vous dérange pas d'inclure un autre module, vous pouvez utiliser la fonction log de gulp-util pour vous empêcher de déclarer une fonction supplémentaire dans votre gulpfile:

.on('error', gutil.log)

Mais je recommanderais peut-être de jeter un coup d'oeil au plugin génial gulp-plumber, qui est utilisé pour supprimer le gestionnaire onerror de l'événement error, ce qui provoque la rupture des flux. Son utilisation est très simple et vous évite d’attraper toutes les tâches qui risquent d’échouer.

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

Plus d'informations à ce sujet sur cet article par le créateur du plugin concerné.

253
Balthazar

Les exemples ci-dessus n'ont pas fonctionné pour moi. Ce qui suit a cependant:

var plumber = require('gulp-plumber');
var liveReload = require('gulp-livereload');
var gutil = require('gulp-util');
var plumber = require('gulp-plumber');
var compass = require('gulp-compass');
var rename = require('gulp-rename');
var minifycss = require('gulp-minify-css');
var notify = require('gulp-notify');

gulp.task('styles', function () {
    //only process main.scss which imports all other required styles - including vendor files.
    return gulp.src('./assets/scss/main.scss')
            .pipe(plumber(function (error) {
                gutil.log(error.message);
                this.emit('end');
            }))
            .pipe(compass({
                config_file: './config.rb',
                css: './css'
                , sass: './assets/scss'
            }))
            //minify files
            .pipe(rename({suffix: '.min'}))
            .pipe(minifycss())

            //output
            .pipe(gulp.dest('./css'))
            .pipe(notify({message: 'Styles task complete'}));
});

gulp.task('watch', function () {
    liveReload.listen();
    gulp.watch('assets/scss/**/*.scss', ['styles']);
});
20
user1491819

Avec un format de fichier

(ex: * .café seulement)

Si vous voulez travailler avec un seul format de fichier, alors gulp-plumber est votre solution.

Par exemple, Rich gère les erreurs et les avertissements relatifs à 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' }));
});

Avec plusieurs types de formats de fichiers

(ex: * .coffee et * .js en même temps)

Mais si vous ne voulez pas travailler avec plusieurs types de formats de fichiers (par exemple: *.js et *.coffee), alors je posterai ma solution.

Je vais simplement poster un code explicatif ici, avec une description avant.

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' }));
});

J'ai fait face au problème avec gulp-plumber et gulp-if en utilisant gulp.watch(...

Voir la question associée ici: https://github.com/floatdrop/gulp-plumber/issues/23

La meilleure option pour moi était donc:

  • Chaque partie sous forme de fichier et concaténer après. Créez plusieurs tâches qui peuvent traiter chaque partie dans un fichier séparé (comme le fait Grunt) et les concaténer
  • Chaque partie en tant que flux et fusion de flux après. Fusionner deux flux en utilisant merge-stream (qui a été créé à partir de event-stream) en un et continuer le travail (j’ai essayé d’abord, et cela fonctionne bien pour moi, donc c’est une solution plus rapide que le précédent)

Chaque partie en tant que flux, et fusionner les flux après

Elle est la partie principale de mon 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' }));
});

Chaque partie en tant que fichier et concaténer après

Si séparer ceci en parties, alors dans chaque partie, un fichier de résultat devrait être créé. Pour 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 .:

Voici les modules de nœuds et les fonctions utilisés:

// 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');
};
4
Roman M. Koss

J'aime utiliser gulp plombier, car il permet d'ajouter un écouteur global à une tâche et d'afficher un message significatif. 

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/'));
});

Référence: https://scotch.io/tutorials/prevent-errors-from-crashing-gulp-watch

3
Sameeksha Kumari

Une solution simple consiste à placer gulp watch dans une boucle infinie dans un shell Bash (ou sh).

while true; do gulp; gulp watch; sleep 1; done

Conservez le résultat de cette commande dans une zone visible de votre écran lorsque vous modifiez votre code JavaScript. Lorsque vos modifications entraînent une erreur, Gulp se bloque, affiche sa trace de pile, attend une seconde et continue à regarder vos fichiers source. Vous pouvez ensuite corriger l'erreur de syntaxe et Gulp indiquera si la modification a réussi ou non en imprimant sa sortie normale ou en bloquant à nouveau (puis en reprenant).

Cela fonctionnera dans un terminal Linux ou Mac. Si vous utilisez Windows, utilisez Cygwin ou Ubuntu Bash (Windows 10).

2
Jesse Hogan

J'ai mis en œuvre le piratage suivant comme solution de contournement pour 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;
}

Ajoutez-le à votre fichier gulpfile.js et utilisez-le comme ça:

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

Cela ressemble à la gestion d'erreur la plus naturelle pour moi. S'il n'y a pas du tout de gestionnaire d'erreur, une erreur sera générée. Testé avec le noeud v0.11.13.

2
Joel Richard

TypeScript

C'est ce qui a fonctionné pour moi. Je travaille avec TypeScript et ai séparé la fonction (pour éviter toute confusion avec le mot clé this) afin de gérer less. Cela fonctionne aussi avec Javascript.

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'))
})

Désormais, lorsque vous watch.less fichiers et qu'une error se produit, la watch ne s’arrête pas et les nouvelles modifications sont traitées conformément à votre less task.

NOTE: J'ai essayé avec l.end();; Cependant, cela n'a pas fonctionné. Cependant, l.emit('end'); fonctionne totalement.

J'espère que cette aide. Bonne chance.

1
Akash

Cela a fonctionné pour moi ->

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'])

Je viens d'ajouter le .on ('error', console.error.bind (console)) , mais je devais exécuter le gulp commande en tant que racine. J'exécute node gulp sur une application php, de sorte que j'ai plusieurs comptes sur un serveur. C'est pourquoi j'ai rencontré le problème de gulp rupture sur les erreurs de syntaxe car je n'exécutais pas gulp en tant que root ... Peut-être que le plombier et certains des d’autres réponses ici auraient fonctionné pour moi si j’exécutais le compte root . Crédit de Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw pour la réponse. Il a dit qu'en gérant l'erreur, il vous aide à déterminer la ligne sur laquelle l'erreur est affichée et ce qu'elle est dans la console, mais empêche également gulp de rompre en cas d'erreur de syntaxe. Il a dit que c'était une sorte de solution légère, alors vous ne savez pas si cela fonctionnera pour ce que vous recherchez. Solution rapide cependant, vaut le coup. J'espère que cela aide quelqu'un!

0
kiko carisse