Impedir erros de quebrar / travar o relógio gulp

178

Estou executando o gulp 3.6.2 e tenho a seguinte tarefa que foi configurada a partir de uma amostra 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']);
});

Sempre que há um erro no meu relógio de gole do CoffeeScript para - obviamente não é o que eu quero.

Como recomendado em outro lugar, tentei isso

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

mas não parece funcionar.

O que estou fazendo de errado?


Em resposta à resposta do @ Aperçu, modifiquei meu swallowErrormétodo e tentei o seguinte:

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

Reiniciou e, em seguida, criou um erro de sintaxe no meu arquivo de café. O mesmo 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
fonte
3
veja as receitas oficiais do gulp em github.com/gulpjs/gulp/tree/master/docs/recipes, que têm uma receita para ... combinar fluxos de manipulação de erros usando stream-combiner2, esta é a resposta oficial!
danday74

Respostas:

257

Sua swallowErrorfunção deve ficar assim:

function swallowError (error) {

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

  this.emit('end')
}

Eu acho que você deve vincular essa função no errorevento da tarefa que estava caindo, e não na watchtarefa, porque não é aí que surge o problema, você deve definir esse retorno de chamada de erro em cada tarefa que possa falhar, como plug-ins que quebram quando você tiver perdeu uma ;ou outra coisa, para impedir que a watchtarefa pare.

Exemplos :

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

Como alternativa, se você não se importa de incluir outro módulo, pode usar a função log do gulp-util para impedir que você declare uma função extra no seu gulpfile:

.on('error', gutil.log)

Mas eu recomendo dar uma olhada no incrível plugin gulp-plumber , que é usado para remover o onerrormanipulador do errorevento, causando a interrupção dos fluxos. É muito simples de usar e impede que você pegue todas as tarefas que podem falhar.

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

Mais informações sobre este artigo pelo criador do plugin em questão.

Balthazar
fonte
Eu aprecio sua ajuda. Veja minha edição. Eu acho que eu estou fazendo o que você está dizendo e ele ainda não funciona
George Mauer
2
Você pode editar sua resposta para refletir a última coisa correta a fazer, para que qualquer pessoa que encontre isso no futuro não precise ler a cadeia de comentários.
George Mauer
1
Tudo está bem, mas por que eu teria que escrever meus próprios manipuladores de erro? Todo o motivo pelo qual eu uso gulp, grunhido ou qualquer outra ferramenta de pré-processamento é que ele faz o trabalho sujo para mim. Um erro de sintaxe em algum lugar do meu código (que está prestes a acontecer) não deve interromper o sistema inteiro. Compare isso com a GUI do Prepros (outro pré-processador) - essa ferramenta informa exatamente onde está o seu erro e não trava ou sai quando algo está errado.
precisa saber é o seguinte
1
incrível que ninguém sequer tenha mencionado o steam-combiner2, mesmo que seja a receita oficial de gole! encanador também é bom, mas eu sempre prefiro seguir receitas gulp, as receitas gulp são atualizadas por gulp e podem ser visualizadas em github.com/gulpjs/gulp/tree/master/docs/recipes
danday74
1
Enfrentando o mesmo enigma, decidi usar um invólucro gulp.watch()que adiciona um .on('error', function() { this.emit('end') })resultado à função de observação, pois é especificamente a tarefa de observação que eu quero tornar resiliente a desligar. Funciona muito bem e as tarefas individuais que falham ainda imprimem suas próprias mensagens de erro. Veja o código: github.com/specious/specious.github.io/commit/f8571d28
tknomad 18/01/18
20

Os exemplos acima não funcionaram para mim. No entanto, o seguinte:

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
fonte
Isso é ótimo, mas também seria bom notificar se havia um erro (atualmente isso apenas registra um erro no terminal) #
raison
4

Com um formato de arquivo

(ex: *. somente café)

Se você deseja trabalhar apenas com um formato de arquivo, então gulp-plumberé a sua solução.

Por exemplo, erros manipulados avançados e aviso 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' }));
});

Com vários tipos de formatos de arquivo

(ex: * .coffee e * .js ao mesmo tempo)

Mas se você não trabalhar com vários tipos de formatos de arquivo (por exemplo: *.jse *.coffee), postarei minha solução.

Vou apenas postar um código auto-explicativo aqui, com alguma descrição 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' }));
});

Eu enfrentei o problema com gulp-plumbere gulp-ifusandogulp.watch(...

Veja o problema relacionado aqui: https://github.com/floatdrop/gulp-plumber/issues/23

Então a melhor opção para mim foi:

  • Cada parte como arquivo e concatenada depois . Crie várias tarefas que podem processar cada parte em um arquivo separado (como o grunhido faz) e concatená-las
  • Cada parte como fluxo e mescla fluxos depois . Mesclar dois fluxos usando merge-stream(que foram feitos a partir de event-stream) em um e continuar o trabalho (tentei isso primeiro e funcionou bem para mim, por isso é uma solução mais rápida que a anterior)

Cada parte como fluxo e mescla fluxos após

Ela é a parte principal do meu 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 arquivo e concatenada após

Para separar isso em partes, em cada parte deve haver um arquivo de resultado criado. Por exemplo:

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:

Aqui módulos e funções do nó que foram usados:

// 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
fonte
Eu ainda vejo melhorias para esses comentários. Como a comparação de velocidade e o link apenas para arquivos .js (excluindo bibliotecas compactadas ou em 3D, ou bibliotecas de bower_components). Mas, basicamente, é fácil ajustar e resolver esse sniffing por Google.com
Roman M. Koss
3

Eu gosto de usar o encanador gulp porque ele pode adicionar um ouvinte global a uma tarefa e exibir uma mensagem significativa.

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

Referência: https://scotch.io/tutorials/prevent-errors-from-crashing-gulp-watch

Sameeksha Kumari
fonte
2

Eu implementei o seguinte hack como uma solução 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;
}

Adicione-o ao seu gulpfile.js e use-o assim:

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

Parece o mais natural tratamento de erros para mim. Se não houver nenhum manipulador de erros, ele lançará um erro. Testado com o nó v0.11.13.

Joel Richard
fonte
2

Uma solução simples para isso é colocar gulp watchum loop infinito dentro de um shell Bash (ou sh).

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

Mantenha a saída desse comando em uma área visível na tela enquanto edita seu JavaScript. Quando suas edições resultam em um erro, o Gulp falha, imprime seu rastreamento de pilha, aguarde um segundo e retome a visualização dos arquivos de origem. Você pode então corrigir o erro de sintaxe e o Gulp indicará se a edição foi ou não bem-sucedida, imprimindo sua saída normal ou travando (e continuando) novamente.

Isso funcionará em um terminal Linux ou Mac. Se você estiver usando o Windows, use Cygwin ou Ubuntu Bash (Windows 10).

Jesse Hogan
fonte
Que língua é essa? Além disso, há algum benefício nisso em relação às soluções sugeridas?
George Mauer 30/10
Está escrito em Bash / sh. Requer menos código que as outras soluções e é mais fácil de lembrar e implementar.
Jesse Hogan
Você pode editar o fato de que o bash e seus outros pensamentos sobre isso são respondidos? Não concordo que seja mais fácil que o encanador, mas é uma abordagem válida (se não multiplataforma). Votei inicialmente porque não estava claro nem em que idioma estava e não posso alterar meu voto a menos que você edite a resposta.
George Mauer
1
Então, você prefere travar o fluxo e reiniciar todo o processo, o que pode levar algum tempo, dependendo do que você está fazendo com um loop infinito, em vez de simplesmente capturar o erro? Parece um pouco perturbador para mim. E por falar em menos código, são necessários 16 caracteres para adicionar encanador à sua tarefa, enquanto sua solução leva 36 sem contar o gulp watch.
Balthazar
Obrigado pelo feedback, @GeorgeMauer, fiz as edições e escreveu sobre os ambientes / plataformas que funciona no.
Jesse Hogan
1

Texto datilografado

Isto é o que funcionou para mim. Eu trabalho com Typescripte separamos a função (para evitar confusão com a thispalavra - chave) para manipular less. Isso funciona Javascripttambém.

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

Agora, quando você watch .lessarquivar e errorocorrer um , o arquivo watchnão será interrompido e as novas alterações serão processadas conforme o seu less task.

NOTA : Eu tentei com l.end();; no entanto, não funcionou. No entanto, l.emit('end');funciona totalmente.

Espero que esta ajuda. Boa sorte.

Akash
fonte
1

Isso funcionou para mim ->

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

Acabei de adicionar a linha .on ('error', console.error.bind (console)), mas tive que executar o comando gulp como root. Estou executando o node gulp em um aplicativo php, de modo que tenho várias contas em um servidor, e foi por isso que encontrei o problema de quebra de gulp em erros de sintaxe, porque não estava executando o gulp como root ... Talvez encanador e alguns dos outras respostas aqui teriam funcionado para mim se eu corresse como root. Os nossos agradecimentos ao Código Accio https://www.youtube.com/watch?v=iMR7hq4ABOw pela resposta. Ele disse que, ao lidar com o erro, ajuda a determinar em qual linha o erro está e o que está no console, mas também impede que o gulp interrompa o erro de sintaxe. Ele disse que era uma espécie de correção leve, então não tenho certeza se funcionará para o que você está procurando. Solução rápida, no entanto, vale a pena tentar. Espero que isso ajude alguém!

kiko carisse
fonte