Zapobiegaj błędom zepsucia / awarii zegarka

178

Używam gulp 3.6.2 i mam następujące zadanie, które zostało skonfigurowane na podstawie próbki 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']);
});

Za każdym razem, gdy pojawia się błąd w moim zegarku z kawą CoffeeScript, zatrzymuje się - oczywiście nie to, czego chcę.

Zgodnie z zaleceniami gdzie indziej próbowałem tego

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

ale wydaje się, że nie działa.

Co ja robię źle?


W odpowiedzi na odpowiedź @ Aperçu zmodyfikowałem swoją swallowErrormetodę i spróbowałem zamiast tego:

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

Uruchomiono ponownie, a następnie utworzyłem błąd składni w moim pliku kawy. Ten sam problem:

[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
źródło
3
zobacz oficjalne przepisy na gulp na github.com/gulpjs/gulp/tree/master/docs/recipes, które zawierają przepis na ... łączenie strumieni z błędami obsługi za pomocą stream-combiner2 to jest oficjalna odpowiedź!
danday74

Odpowiedzi:

257

Twoja swallowErrorfunkcja powinna wyglądać następująco:

function swallowError (error) {

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

  this.emit('end')
}

Myślę, że musisz powiązać tę funkcję ze errorzdarzeniem zadania, które spadało, a nie z watchzadaniem, ponieważ nie jest to miejsce, w którym pojawia się problem, powinieneś ustawić ten błąd zwrotny dla każdego zadania, które może się nie powieść, jak wtyczki, które psują się, gdy masz przegapiłeś ;coś lub coś innego, aby zapobiec watchzatrzymaniu zadania.

Przykłady:

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

Alternatywnie, jeśli nie masz nic przeciwko dołączeniu innego modułu, możesz użyć funkcji dziennika gulp-util, aby nie zadeklarować dodatkowej funkcji w swoim gulpfile:

.on('error', gutil.log)

Ale mogę polecić przyjrzenie się niesamowitej wtyczce gulp-plumber , która służy do usuwania programu onerrorobsługi errorzdarzenia, powodując przerwanie strumieni. Jest bardzo prosty w użyciu i zapobiega przechwytywaniu wszystkich zadań, które mogą się nie powieść.

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

Więcej informacji na ten temat w tym artykule autora danej wtyczki.

Balthazar
źródło
Doceniam twoją pomoc. Zobacz moją edycję. Myślę, że robię to, co mówisz, a to nadal nie działa
George Mauer
2
Możesz zmodyfikować swoją odpowiedź, aby odzwierciedlić ostateczną poprawną rzecz do zrobienia, aby każdy, kto znajdzie to w przyszłości, nie musiał czytać łańcucha komentarzy.
George Mauer
1
To wszystko jest w porządku, ale dlaczego miałbym pisać własne programy obsługi błędów? Cały powód, dla którego używam gulp, chrząknięcia lub jakiegokolwiek innego narzędzia do wstępnego przetwarzania, jest taki, że wykonuje za mnie brudną robotę. Błąd składni gdzieś w moim kodzie (który musi się zdarzyć) nie powinien spowodować zatrzymania całego systemu. Porównaj to z GUI Prepros (inny preprocesor) - to narzędzie mówi dokładnie, gdzie jest twój błąd i nie zawiesza się ani nie kończy, gdy coś jest nie tak.
Kokodoko
1
niesamowite, że nikt nawet nie wspomniał o łączniku parowym2, mimo że jest to oficjalny przepis na łyk! hydraulik też dobre, choć ale zawsze wolą podążać recepty łyk, łyk receptury są uaktualniane przez haustem i mogą być przeglądane w github.com/gulpjs/gulp/tree/master/docs/recipes
danday74
1
W obliczu tej samej zagadki zdecydowałem się na użycie opakowania, gulp.watch()które dodaje efekt .on('error', function() { this.emit('end') })do wyniku działania funkcji zegarka, ponieważ jest to szczególnie zadanie zegarka, które chcę uodpornić na zawieszanie się. Działa naprawdę dobrze, a poszczególne zadania, które kończą się niepowodzeniem, nadal wyświetlają własne komunikaty o błędach. Zobacz kod: github.com/specious/specious.github.io/commit/f8571d28
tknomad
20

Powyższe przykłady nie działają dla mnie. Jednak zrobili to:

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
źródło
To jest świetne, ale dobrze byłoby również powiadomić, jeśli wystąpił błąd (obecnie to tylko rejestruje błąd w terminalu)
raison
4

Z jednym formatem plików

(np .: *. tylko kawa)

Jeśli chcesz pracować tylko z jednym formatem plików, to gulp-plumberjest Twoje rozwiązanie.

Na przykład bogate obsługiwane błędy i ostrzeżenia dotyczące tworzenia skryptów kawowych:

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

Z wieloma typami formatów plików

(np. * .coffee i * .js w tym samym czasie)

Ale jeśli nie będziesz pracować z wieloma typami formatów plików (na przykład: *.jsi *.coffee), opublikuję moje rozwiązanie.

Po prostu opublikuję tutaj kod, który nie wymaga objaśnień, z pewnym opisem wcześniej.

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

Zmierzyłem się z problemem gulp-plumberi gulp-ifużywamgulp.watch(...

Zobacz powiązany problem tutaj: https://github.com/floatdrop/gulp-plumber/issues/23

Więc najlepszą opcją dla mnie było:

  • Każda część jako plik i połączona później . Twórz wiele zadań, które mogą przetwarzać każdą część w osobnym pliku (jak w przypadku gruntów), i łącz je
  • Każda część jako strumień i połącz strumienie po . Połącz dwa strumienie za pomocą merge-stream(który został utworzony event-stream) w jeden i kontynuuj pracę (próbowałem tego jako pierwszy i działa dobrze, więc jest to szybsze rozwiązanie niż poprzedni)

Każda część jako strumień i połącz strumienie po

Ona jest główną częścią mojego kodu:

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

Każda część jako plik i połączona później

Aby podzielić to na części, to w każdej części powinien być utworzony plik wynikowy. Na przykład:

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:

Tutaj moduły węzłów i funkcje, które zostały użyte:

// 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
źródło
Nadal widzę ulepszenia dla tych komentarzy. Podobnie jak porównanie prędkości i łącze tylko do plików .js (z wyłączeniem bibliotek minified lub 3d lub bibliotek z bower_components). Ale w zasadzie jest to łatwe do dostosowania i rozwiązania tego wąchania przez Google.com
Roman M. Koss
3

Lubię używać hydraulika Gulp, ponieważ może on dodać do zadania globalnego nasłuchiwania i wyświetlić znaczący komunikat.

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ódła: https://scotch.io/tutorials/prevent-errors-from-crashing-gulp-watch

Sameeksha Kumari
źródło
2

Zaimplementowałem następujący hack jako obejście dla 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;
}

Dodaj go do swojego pliku gulpfile.js i używaj go w ten sposób:

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

Wydaje mi się, że jest to najbardziej naturalna obsługa błędów. Jeśli w ogóle nie ma programu obsługi błędów, zgłosi błąd. Testowane z Node v0.11.13.

Joel Richard
źródło
2

Prostym rozwiązaniem tego jest umieszczenie gulp watchnieskończonej pętli w powłoce Bash (lub sh).

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

Utrzymuj wynik tego polecenia w widocznym obszarze na ekranie podczas edytowania kodu JavaScript. Kiedy Twoje zmiany spowodują błąd, Gulp ulegnie awarii, wydrukuje ślad stosu, odczekaj sekundę i wznowi oglądanie plików źródłowych. Następnie możesz poprawić błąd składni, a Gulp wskaże, czy edycja zakończyła się sukcesem, albo drukując normalne wyjście, albo ponownie zawieszając się (i wznawiając).

To zadziała w terminalu Linux lub Mac. Jeśli używasz systemu Windows, użyj Cygwin lub Ubuntu Bash (Windows 10).

Jesse Hogan
źródło
Jaki to język? Czy jest to jakaś korzyść w porównaniu z sugerowanymi rozwiązaniami?
George Mauer
Jest napisane w Bash / sh. Wymaga mniej kodu niż inne rozwiązania i jest łatwiejszy do zapamiętania i wdrożenia.
Jesse Hogan
Czy możesz edytować fakt, że to bash i inne twoje przemyślenia na ten temat w swojej odpowiedzi? Nie zgadzam się, że jest to łatwiejsze niż hydraulik, ale jest to ważne (jeśli nie wieloplatformowe) podejście. Początkowo głosowałem na odrzucenie, ponieważ nie było jasne, nawet w jakim języku jest, i nie mogę zmienić swojego głosu, dopóki nie zmienisz odpowiedzi.
George Mauer
1
Więc wolisz zawiesić strumień i ponownie uruchomić cały proces, który może zająć sporo czasu w zależności od tego, co robisz z nieskończoną pętlą, zamiast po prostu wychwycić błąd? Wydaje mi się to trochę niepokojące. Mówiąc o mniejszej ilości kodu, potrzeba 16 znaków, aby dodać hydraulika do zadania, podczas gdy twoje rozwiązanie zajmuje 36 bez liczenia gulp watch.
Balthazar
Dzięki za opinię, @GeorgeMauer, wprowadziłem zmiany i napisałem o środowiskach / platformach, w których działa.
Jesse Hogan
1

Maszynopis

To właśnie zadziałało dla mnie. Pracuję z Typescriptfunkcją i oddzielałem ją (aby aovid pomieszanie ze thissłowem kluczowym) do obsługi less. Działa to Javascriptrównież z.

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

Teraz, kiedy watch .lesspliki, a także errornastąpi, watchnie będzie zatrzymać i nowe zmiany będą przetwarzane jak na swoje less task.

UWAGA : próbowałem z l.end();; jednak to nie zadziałało. Jednak l.emit('end');całkowicie działa.

Mam nadzieję, że to pomoże. Powodzenia.

Akash
źródło
1

To zadziałało dla mnie ->

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

Właśnie dodałem wiersz .on ('błąd', console.error.bind (console)), ale musiałem uruchomić polecenie gulp jako root. Uruchamiam node gulp w aplikacji php, więc mam wiele kont na jednym serwerze, dlatego napotkałem problem zerwania łyka przy błędach składniowych, ponieważ nie uruchamiałem gulp jako root ... Może hydraulik i niektóre z inne odpowiedzi tutaj zadziałałyby, gdybym uruchomił się jako root. Kredyt dla Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw za odpowiedź. Powiedział, że obsługa błędu pomaga określić, w której linii znajduje się błąd i co znajduje się w konsoli, ale także zapobiega przełamywaniu się łyka po błędzie składni. Powiedział, że to trochę lekka poprawka, więc nie jestem pewien, czy zadziała w przypadku tego, czego szukasz. Szybka naprawa, warto spróbować. Mam nadzieję, że to komuś pomoże!

kiko carisse
źródło