Empêcher les erreurs de casser / écraser la montre gulp

178

J'exécute gulp 3.6.2 et j'ai la tâche suivante qui 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 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 swallowErrorméthode et essayé ce qui suit à 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)
George Mauer
la source
3
voir les recettes officielles de gulp à gulp github.com/gulpjs/gulp/tree/master/docs/recipes qui ont une recette pour ... combiner-streams-to-handle-erreurs en utilisant stream-combiner2 c'est la réponse officielle!
danday74

Réponses:

257

Votre swallowErrorfonction 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' errorévénement de la tâche qui tombait, pas à la watchtâche, car ce n'est pas de là que vient le problème, vous devez définir ce rappel d'erreur sur chaque tâche qui peut échouer, comme les plugins qui se cassent lorsque vous avez manqué un ;ou autre chose, pour éviterwatch tâche 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 peux recommander de jeter un coup d'œil à l'impressionnant plombier plugin , qui est utilisé pour supprimer le onerrorgestionnaire de l' errorévénement, provoquant la rupture des flux. C'est très simple à utiliser et cela vous empêche d'attraper toutes les tâches qui peuvent échouer.

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

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

Balthazar
la source
J'apprécie ton aide. Voir ma modification. Je pense que je fais ce que vous dites et ça ne marche toujours pas
George Mauer
2
Vous voudrez peut-être modifier votre réponse pour refléter la dernière chose à faire afin que quiconque le trouve à l'avenir ne doive pas lire la chaîne de commentaires.
George Mauer
1
Tout cela est bien beau, mais pourquoi devrais-je écrire mes propres gestionnaires d'erreurs? La raison pour laquelle j'utilise gulp ou grunt ou tout autre outil de prétraitement, c'est qu'il fait le sale boulot à ma place. Une erreur de syntaxe quelque part dans mon code (ce qui est inévitable) ne devrait pas interrompre tout le système. Comparez cela à l'interface graphique de Prepros (un autre préprocesseur) - cet outil vous indique exactement où se trouve votre erreur et ne se bloque pas ou ne se ferme pas lorsque quelque chose ne va pas.
Kokodoko
1
étonnant que personne n'ait même mentionné le steam-combiner2 même s'il s'agit de la recette officielle de la gorgée! plombier aussi bien mais je préfère toujours suivre les recettes de gulp, les recettes de gulp sont
mises
1
Face à la même énigme, j'ai décidé d'utiliser un wrapper gulp.watch()qui ajoute un .on('error', function() { this.emit('end') })au résultat de la fonction de montre, car c'est spécifiquement la tâche de montre que je veux rendre résiliente à raccrocher. Fonctionne très bien et les tâches individuelles qui échouent impriment toujours leurs propres messages d'erreur. Voir le code: github.com/specious/specious.github.io/commit/f8571d28
tknomad
20

Les exemples ci-dessus n'ont pas fonctionné pour moi. Ce qui suit 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']);
});
user1491819
la source
C'est génial, mais il serait également bon de notifier s'il y avait une erreur (actuellement, cela ne fait que consigner une erreur dans le terminal)
raison
4

Avec un format de fichiers

(ex: * .coffee uniquement)

Si vous souhaitez travailler uniquement avec un seul format de fichiers, alors gulp-plumberest votre solution.

Par exemple, les erreurs de gestion enrichie et les avertissements pour le script café:

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 travaillez pas avec plusieurs types de formats de fichiers (par exemple: *.jset *.coffee), je publierai ma solution.

Je vais juste poster un code explicite 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 rencontré le problème avec gulp-plumberet en gulp-ifutilisantgulp.watch(...

Voir le problème associé ici: https://github.com/floatdrop/gulp-plumber/issues/23

La meilleure option pour moi était donc:

  • Chaque partie en tant que fichier, et concaténée après . Créez plusieurs tâches qui peuvent traiter chaque partie dans un fichier séparé (comme le fait Grunt) et concaténez-les
  • Chaque partie en tant que flux, et fusionne les flux après . Fusionnez deux flux en utilisant merge-stream(qui a été créé à partir de event-stream) en un seul et continuez le travail (j'ai essayé cela en premier, et cela fonctionne bien pour moi, donc c'est une solution plus rapide que la précédente)

Chaque partie en tant que flux, et fusionne 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énée après

Si vous devez séparer cela en parties, dans chaque partie, un fichier de résultat doit être créé. Par exemple:

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;

});

PS:

Voici les modules et fonctions de nœud qui ont été 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');
};
Roman M. Koss
la source
Je vois encore des améliorations pour ces commentaires. Comme la comparaison de vitesse et le lien pour les fichiers .js uniquement (à l'exclusion des bibliothèques minifiées ou 3D Party, ou des bibliothèques de bower_components). Mais fondamentalement, il est facile d'ajuster et de résoudre ce reniflement par Google.com
Roman M. Koss
3

J'aime utiliser gulp plumber car il peut ajouter un auditeur global à une tâche et 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

Sameeksha Kumari
la source
2

J'ai implémenté le hack 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 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 me semble être la gestion des erreurs la plus naturelle. S'il n'y a pas du tout de gestionnaire d'erreur, une erreur est générée. Testé avec Node v0.11.13.

Joël Richard
la source
2

Une solution simple à cela est de mettre gulp watchdans une boucle infinie dans un shell Bash (ou sh).

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

Conservez la sortie de cette commande dans une zone visible de votre écran lorsque vous modifiez votre JavaScript. Lorsque vos modifications entraînent une erreur, Gulp plante, imprime sa trace de pile, attend une seconde et reprend la surveillance de 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 plantant (puis en reprenant).

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

Jesse Hogan
la source
Quelle langue est-ce? Y a-t-il un avantage à cela par rapport aux solutions suggérées?
George Mauer
C'est écrit en bash / sh. Elle nécessite moins de code que les autres solutions et est plus facile à mémoriser et à implémenter.
Jesse Hogan
Pouvez-vous modifier le fait que sa bash et vos autres réflexions à ce sujet dans votre réponse? Je ne suis pas d'accord pour dire que c'est plus facile que le plombier, mais c'est une approche valide (sinon multiplateforme). J'ai voté contre au départ parce que la langue dans laquelle il se trouvait n'était même pas claire et je ne peux pas changer mon vote à moins que vous ne modifiiez la réponse.
George Mauer
1
Vous préférez donc planter le flux et redémarrer tout le processus, ce qui peut en prendre beaucoup en fonction de ce que vous faites avec une boucle infinie plutôt que de simplement attraper l'erreur? Cela me semble un peu dérangeant. Et en parlant de moins de code, il vous faut 16 caractères pour ajouter un plombier à votre tâche alors que votre solution en prend 36 sans compter le gulp watch.
Balthazar
Merci pour les commentaires, @GeorgeMauer, j'ai fait les modifications et écrit sur les environnements / plates-formes dans lesquels il fonctionne.
Jesse Hogan
1

Manuscrit

C'est ce qui a fonctionné pour moi. Je travaille avec Typescriptet sépare la fonction (pour éviter toute confusion avec le thismot-clé) à gérer less. Cela fonctionne également 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 .lessenregistrez des fichiers et que cela errorse produit, le watchne s'arrêtera pas et les nouvelles modifications seront traitées selon votre less task.

REMARQUE : 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.

Akash
la source
1

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 la ligne .on ('error', console.error.bind (console)), mais j'ai dû exécuter la commande gulp en tant que root. J'exécute node gulp sur une application php donc j'ai plusieurs comptes sur un serveur, c'est pourquoi j'ai rencontré le problème de la rupture de gulp sur les erreurs de syntaxe parce que je n'utilisais pas gulp en tant que root ... Peut-être plombier et certains des d'autres réponses ici auraient fonctionné pour moi si j'avais couru en tant que root. Crédit à Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw pour la réponse. Il a dit qu'en gérant l'erreur, cela vous aide à déterminer sur quelle ligne se trouve l'erreur et ce qu'elle est dans la console, mais empêche également gulp de se rompre en cas d'erreur de syntaxe. Il a dit que c'était une sorte de solution légère, donc je ne sais pas si cela fonctionnera pour ce que vous recherchez. Solution rapide, ça vaut le coup. J'espère que cela aide quelqu'un!

kiko carisse
la source