Evita que los errores se rompan o se estrellen.

178

Estoy ejecutando gulp 3.6.2 y tengo la siguiente tarea que se configuró a partir de una muestra en línea

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

Cada vez que hay un error en mi reloj de CoffeeScript, obviamente no es lo que quiero.

Como se recomienda en otro lugar, probé esto

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

Pero no parece funcionar.

¿Qué estoy haciendo mal?


En respuesta a la respuesta de @ Aperçu, modifiqué mi swallowErrormétodo e intenté lo siguiente:

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

Reinicié y luego creé un error de sintaxis en mi archivo de café. Mismo 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)
George Mauer
fuente
3
vea las recetas oficiales de gulp en github.com/gulpjs/gulp/tree/master/docs/recipes que tienen una receta para ... combinar-streams-to-handle-errors usando stream-combiner2 ¡esta es la respuesta oficial!
danday74

Respuestas:

257

Su swallowErrorfunción debería verse así:

function swallowError (error) {

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

  this.emit('end')
}

Creo que debe vincular esta función en errorcaso de que la tarea se caiga, no la watchtarea, porque no es allí donde viene el problema, debe configurar esta devolución de llamada de error en cada tarea que puede fallar, como complementos que se rompen cuando tiene omitió una ;u otra cosa, para evitar que la watchtarea se detenga.

Ejemplos:

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

Alternativamente, si no le importa incluir otro módulo, puede usar la función de registro de gulp-util para evitar que declare una función adicional en su gulpfile:

.on('error', gutil.log)

Pero puedo recomendar echar un vistazo al impresionante plugin gulp-plumber , que se utiliza para eliminar el onerrorcontrolador del errorevento y provocar la interrupción de las transmisiones. Es muy simple de usar y evita que detecte todas las tareas que pueden fallar.

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

Más información sobre esto en este artículo del creador del complemento en cuestión.

Baltasar
fuente
Aprecio tu ayuda. Mira mi edición. Creo que estoy haciendo lo que dices y todavía no funciona
George Mauer
2
Es posible que desee editar su respuesta para reflejar lo correcto que debe hacer para que cualquier persona que encuentre esto en el futuro no tenga que leer la cadena de comentarios.
George Mauer
1
Todo esto está muy bien, pero ¿por qué tendría que escribir mis propios controladores de errores? Toda la razón por la que uso Gulp o Grunt o cualquier herramienta de preprocesamiento, es porque hace el trabajo sucio por mí. Un error de sintaxis en algún lugar de mi código (que seguramente sucederá) no debería detener todo el sistema. Compare esto con la GUI de Prepros (otro preprocesador): esta herramienta le dice exactamente dónde está su error y no se bloquea ni se cierra cuando algo está mal.
Kokodoko
1
¡Es increíble que nadie haya mencionado el vaporizador combinador2 a pesar de que es la receta oficial para tragar! el fontanero también es bueno, pero siempre prefiero seguir las recetas de gulp, las recetas de gulp se mantienen actualizadas y se pueden ver en github.com/gulpjs/gulp/tree/master/docs/recipes
danday74
1
Frente al mismo enigma, decidí usar un envoltorio gulp.watch()que agregue un .on('error', function() { this.emit('end') })resultado al resultado de la función de reloj, ya que es específicamente la tarea de reloj que quiero hacer resistente para colgar. Funciona realmente bien, y las tareas individuales que fallan aún imprimen sus propios mensajes de error. Vea el código: github.com/specious/specious.github.io/commit/f8571d28
tknomad
20

Los ejemplos anteriores no me funcionaron. Lo siguiente hizo sin embargo:

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']);
});
usuario1491819
fuente
Esto es genial, pero también sería bueno notificar si hubo un error (actualmente esto solo registra un error en la terminal)
raison
4

Con un formato de archivos

(ej: * .café solamente)

Si desea trabajar solo con un formato de archivos, entonces gulp-plumberes su solución.

Por ejemplo, errores manejados ricos y advertencias para 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' }));
});

Con múltiples tipos de formatos de archivo

(ej: * .coffee y * .js al mismo tiempo)

Pero si no quiere trabajar con múltiples tipos de formatos de archivo (por ejemplo: *.js y *.coffee), entonces publicaré mi solución.

Voy a publicar un código autoexplicativo aquí, con alguna descripción antes.

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

Me enfrenté al problema con gulp-plumberygulp-if usandogulp.watch(...

Vea el problema relacionado aquí: https://github.com/floatdrop/gulp-plumber/issues/23

Entonces la mejor opción para mí fue:

  • Cada parte como archivo, y concatenar después . Cree múltiples tareas que puedan procesar cada parte en un archivo separado (como lo hace el gruñido) y concatenelas
  • Cada parte como secuencia, y fusionar secuencias después . Combine dos transmisiones usando merge-stream(que se hizo de event-stream) en una y continúe el trabajo (lo intenté primero, y funciona bien para mí, por lo que es una solución más rápida que la anterior)

Cada parte como secuencia y fusionar secuencias después

Ella es la parte principal de mi código:

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

Cada parte como archivo y concatenar después

Si para separar esto en partes, entonces en cada parte debe haber un archivo de resultados creado. Por ej .:

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;

});

PD:

Aquí los módulos de nodo y las funciones que se utilizaron:

// 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
fuente
Todavía veo mejoras para estos comentarios. Al igual que la comparación de velocidad, y enlace solo para archivos .js (excluyendo bibliotecas de fiestas minificadas o 3d, o libs de bower_components). Pero básicamente es fácil de ajustar y resolver este olfateo de Google.com
Roman M. Koss
3

Me gusta usar gulp plumber porque puede agregar un oyente global a una tarea y mostrar un mensaje significativo.

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

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

Sameeksha Kumari
fuente
2

He implementado el siguiente hack como solución alternativa para 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;
}

Añádelo a tu gulpfile.js y úsalo así:

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

Esto se siente como el manejo de errores más natural para mí. Si no hay ningún controlador de errores, arrojará un error. Probado con el nodo v0.11.13.

Joel Richard
fuente
2

Una solución simple a esto es poner gulp watchun bucle infinito dentro de un shell Bash (o sh).

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

Mantenga la salida de este comando en un área visible en su pantalla mientras edita su JavaScript. Cuando sus ediciones den como resultado un error, Gulp se bloqueará, imprimirá su seguimiento de la pila, esperará un segundo y continuará viendo sus archivos fuente. Luego puede corregir el error de sintaxis, y Gulp indicará si la edición fue exitosa o no imprimiendo su salida normal o bloqueándose (luego reanudando) nuevamente.

Esto funcionará en un terminal Linux o Mac. Si está utilizando Windows, use Cygwin o Ubuntu Bash (Windows 10).

Jesse Hogan
fuente
¿Qué lenguaje es este? Además, ¿hay algún beneficio en esto sobre las soluciones sugeridas?
George Mauer
Está escrito en Bash / sh. Requiere menos código que las otras soluciones y es más fácil de recordar e implementar.
Jesse Hogan
¿Puedes editar el hecho de que es bash y tus otros pensamientos sobre esto en tu respuesta? No estoy de acuerdo con que sea más fácil que el fontanero, pero es un enfoque válido (si no multiplataforma). Inicialmente voté en contra porque no estaba claro ni en qué idioma estaba y no puedo cambiar mi voto a menos que edite la respuesta.
George Mauer
1
Entonces, ¿prefiere bloquear la transmisión y reiniciar todo el proceso que puede tomar bastante dependiendo de lo que esté haciendo con un bucle infinito en lugar de simplemente detectar el error? Me parece un poco inquietante. Y hablando de menos código, requiere 16 caracteres para agregar fontanero a su tarea, mientras que su solución toma 36 sin contar el gulp watch.
Balthazar el
Gracias por los comentarios, @GeorgeMauer, hice las ediciones y escribí sobre los entornos / plataformas en las que funciona.
Jesse Hogan
1

Mecanografiado

Esto es lo que funcionó para mí. Trabajo Typescripty separé la función (para evitar la confusión con la thispalabra clave) para manejar less. Esto funciona con Javascripttambién.

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

Ahora, cuando presenta los watch .lessarchivos, y errorocurre, watchno se detendrá y se procesarán los nuevos cambios según su less task.

NOTA : probé con l.end();; Sin embargo, no funcionó. Sin embargo, l.emit('end');funciona totalmente.

Espero que esto ayude. Buena suerte.

Akash
fuente
1

Esto funcionó para mí ->

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

Acabo de agregar la línea .on ('error', console.error.bind (console)), pero tuve que ejecutar el comando gulp como root. Estoy ejecutando node gulp en una aplicación php, así que tengo varias cuentas en un servidor, por lo que me encontré con el problema de que Gulp se rompiera en los errores de sintaxis porque no estaba ejecutando gulp como root ... Tal vez fontanero y algunos de los otras respuestas aquí me habrían funcionado si me ejecutara como root. Acredite a Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw por la respuesta. Dijo que al manejar el error, le ayuda a determinar en qué línea se encuentra el error y en qué se encuentra en la consola, pero también evita que el trago se rompa en el error de sintaxis. Dijo que era una solución ligera, por lo que no estoy seguro de si funcionará para lo que está buscando. Solución rápida, sin embargo, vale la pena intentarlo. ¡Espero que esto ayude a alguien!

kiko carisse
fuente