Prevenire errori di rottura / crash gulp watch


178

Sto eseguendo gulp 3.6.2 e ho il seguente compito che è stato impostato da un campione online

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

Ogni volta che c'è un errore nel mio orologio gulp CoffeeScript si ferma - ovviamente non è quello che voglio.

Come raccomandato altrove ho provato questo

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

ma non sembra funzionare.

Che cosa sto facendo di sbagliato?


In risposta alla risposta di @ Aperçu ho modificato il mio swallowErrormetodo e ho provato invece quanto segue:

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

Riavviato e quindi creato un errore di sintassi nel mio file del caffè. Stesso problema:

[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)

3
guarda le ricette ufficiali di gulp su github.com/gulpjs/gulp/tree/master/docs/recipes che hanno una ricetta per ... combinare-stream-to-handle-errori usando stream-combiner2 questa è la risposta ufficiale!
danday74,

Risposte:


257

La tua swallowErrorfunzione dovrebbe apparire così:

function swallowError (error) {

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

  this.emit('end')
}

Penso che devi associare questa funzione errorall'evento dell'attività che stava cadendo, non watchall'attività, perché non è lì che nasce il problema, dovresti impostare questo callback di errore su ogni attività che potrebbe fallire, come i plugin che si interrompono quando hai mancato un ;o qualcos'altro, per impedire il completamento watchdell'attività.

Esempi:

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

In alternativa, se non ti dispiace includere un altro modulo, puoi usare la funzione log di gulp-util per impedirti di dichiarare una funzione extra nel tuo gulpfile:

.on('error', gutil.log)

Ma posso consigliare di dare un'occhiata al fantastico plugin gulp-plumber , che viene utilizzato per rimuovere il onerrorgestore errordell'evento, causando l'interruzione dei flussi. È molto semplice da usare e ti impedisce di catturare tutte le attività che potrebbero non riuscire.

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

Maggiori informazioni su questo in questo articolo dal creatore del plugin interessato.


Apprezzo il vostro aiuto. Vedi la mia modifica. Penso di fare quello che stai dicendo e ancora non funziona
George Mauer,

2
Potresti voler modificare la tua risposta per riflettere l'ultima cosa corretta da fare in modo che chiunque la trovi in ​​futuro non debba leggere la catena di commenti.
George Mauer,

1
Va tutto bene, ma perché dovrei scrivere i miei gestori di errori? L'intero motivo per cui uso gulp o grunt o qualunque strumento di preelaborazione è che fa il lavoro sporco per me. Un errore di sintassi da qualche parte nel mio codice (che è destinato a succedere) non dovrebbe fermare l'intero sistema. Confronta questo con la GUI di Prepros (un altro preprocessore) - questo strumento ti dice esattamente dove si trova il tuo errore e non si blocca o si chiude quando qualcosa non va.
Kokodoko,

1
incredibile che nessuno abbia mai menzionato steam-combiner2, anche se è la ricetta ufficiale del gulp! idraulico buono anche se però preferisco sempre seguire le ricette gulp, le ricette gulp sono aggiornate da gulp e possono essere visualizzate su github.com/gulpjs/gulp/tree/master/docs/recipes
danday74

1
Di fronte allo stesso enigma, ho deciso di utilizzare un involucro gulp.watch()che aggiunge un .on('error', function() { this.emit('end') })risultato al risultato della funzione orologio, in quanto è specificamente il compito dell'orologio che voglio rendere resiliente per riagganciare. Funziona davvero bene e le singole attività che falliscono continuano a stampare i propri messaggi di errore. Vedi il codice: github.com/specious/specious.github.io/commit/f8571d28
tknomad

20

Gli esempi sopra non hanno funzionato per me. Quanto segue ha fatto però:

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

Questo è fantastico, ma sarebbe anche bene avvisare se si è verificato un errore (attualmente registra solo un errore nel terminale)
raison

4

Con un formato di file

(es: *. solo caffè)

Se vuoi lavorare solo con un formato di file, allora gulp-plumberè la tua soluzione.

Ad esempio, errori con gestione avanzata e avvisi per la scrittura del caffè:

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

Con più tipi di formati di file

(es: * .coffee e * .js contemporaneamente)

Ma se non vuoi lavorare con più tipi di formati di file (ad esempio: *.jse *.coffee), allora posterò la mia soluzione.

Pubblicherò semplicemente un codice esplicativo qui, con qualche descrizione prima.

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

Ho affrontato il problema con gulp-plumbere gulp-ifutilizzandogulp.watch(...

Vedi il problema correlato qui: https://github.com/floatdrop/gulp-plumber/issues/23

Quindi l'opzione migliore per me è stata:

  • Ogni parte come file e concatenare dopo . Crea più attività in grado di elaborare ciascuna parte in un file separato (come fa grunt) e concatenarle
  • Ogni parte come flusso e unire i flussi dopo . Unisci due flussi utilizzando merge-stream(che è stato creato da event-stream) in uno e continua il lavoro (l'ho provato prima, e funziona bene per me, quindi è una soluzione più veloce rispetto alla precedente)

Ogni parte come flusso e unire i flussi dopo

Lei è la parte principale del mio codice:

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

Ogni parte come file e concatenare dopo

Se separarlo in parti, in ogni parte dovrebbe essere creato un file di risultati. Per es .:

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:

Qui i moduli e le funzioni dei nodi utilizzati:

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

Vedo ancora miglioramenti per questi commenti. Come il confronto della velocità e il collegamento solo per i file .js (escluse le librerie di parti minimizzate o 3d o le librerie da bower_components). Ma fondamentalmente è facile regolare e risolvere questo fiuto da Google.com
Roman M. Koss,

3

Mi piace usare l'idraulico gulp perché può aggiungere un ascoltatore globale a un'attività e avere un messaggio significativo visualizzato.

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

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


2

Ho implementato il seguente hack come soluzione alternativa per 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;
}

Aggiungilo al tuo gulpfile.js e usalo così:

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

Mi sembra il più naturale errore nella gestione. Se non esiste alcun gestore degli errori, verrà generato un errore. Testato con Nodo v0.11.13.


2

Una soluzione semplice a ciò è quella di inserire gulp watchun ciclo infinito all'interno di una shell Bash (o sh).

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

Mantieni l'output di questo comando in un'area visibile sullo schermo mentre modifichi JavaScript. Quando le tue modifiche generano un errore, Gulp si arresta in modo anomalo, stampa la traccia dello stack, aspetta un secondo e riprende a guardare i tuoi file sorgente. È quindi possibile correggere l'errore di sintassi e Gulp indicherà se la modifica ha avuto esito positivo stampando l'output normale o arrestandosi in modo anomalo (quindi riprendendolo).

Funzionerà in un terminale Linux o Mac. Se stai usando Windows, usa Cygwin o Ubuntu Bash (Windows 10).


Che lingua è questa? Inoltre, c'è qualche vantaggio in questo rispetto alle soluzioni suggerite?
George Mauer,

È scritto in Bash / sh. Richiede meno codice rispetto alle altre soluzioni ed è più facile da ricordare e implementare.
Jesse Hogan,

Puoi modificare il fatto che la sua bash e i tuoi altri pensieri su questo nella tua risposta? Non sono d'accordo sul fatto che sia più facile dell'idraulico, ma è un approccio valido (se non multipiattaforma). Inizialmente ho votato verso il basso perché non era chiaro nemmeno in che lingua fosse e non posso cambiare il mio voto se non modifichi la risposta.
George Mauer,

1
Quindi preferisci interrompere il flusso e riavviare l'intero processo che potrebbe richiedere un po 'a seconda di cosa stai facendo con un ciclo infinito piuttosto che semplicemente catturare l'errore? Mi sembra un po 'inquietante. E parlando di meno codice, sono necessari 16 caratteri per aggiungere idraulico al tuo compito mentre la tua soluzione impiega 36 senza contare il gulp watch.
Balthazar,

Grazie per il feedback, @GeorgeMauer, ho apportato le modifiche e scritto sugli ambienti / piattaforme in cui funziona.
Jesse Hogan

1

Dattiloscritto

Questo è ciò che ha funzionato per me. Lavoro con Typescripte separo la funzione (per evitare confusione con la thisparola chiave) da gestire less. Funziona anche con 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'))
})

Ora, quando si watch .lessfile, e si errorverifica un , il watchnon si fermerà e le nuove modifiche verranno elaborate secondo il tuo less task.

NOTA : ho provato con l.end();; tuttavia, non ha funzionato. Tuttavia, l.emit('end');funziona totalmente.

Spero che questo aiuto. In bocca al lupo.


1

Questo ha funzionato per 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'])

Ho appena aggiunto la riga .on ('errore', console.error.bind (console)), ma ho dovuto eseguire il comando gulp come root. Sto eseguendo il nodo gulp su un'applicazione php, quindi ho più account su un server, motivo per cui mi sono imbattuto nel problema della rottura del gulp sugli errori di sintassi perché non stavo eseguendo gulp come root ... Forse idraulico e alcuni dei altre risposte qui avrebbero funzionato per me se avessi corso come root. Accredita il codice Accio https://www.youtube.com/watch?v=iMR7hq4ABOw per la risposta. Ha detto che gestendo l'errore ti aiuta a determinare su quale linea si trova l'errore e quale è nella console, ma impedisce anche a gulp di interrompere l'errore di sintassi. Ha detto che era una specie di correzione leggera, quindi non sono sicuro che funzionerà per quello che stai cercando. Soluzione rapida però, vale la pena provare. Spero che questo aiuti qualcuno!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.