Предотвращение ошибок от взлома / сбоя


178

Я запускаю gulp 3.6.2 и имею следующую задачу, которая была настроена из примера онлайн

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

Каждый раз, когда происходит ошибка в моих остановках на CoffeeScript, очевидно, не то, что я хочу.

Как рекомендовано в другом месте, я попробовал это

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

но это не похоже на работу.

Что я делаю не так?


В ответ на ответ @ Aperçu я изменил свой swallowErrorметод и попробовал следующее:

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

Перезапустил, а затем создал синтаксическую ошибку в моем файле кофе. Та же проблема:

[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
см. официальные рецепты глотка на github.com/gulpjs/gulp/tree/master/docs/recipes, у которых есть рецепт для ... объединения потоков с обработкой ошибок с помощью stream-combiner2, это официальный ответ!
danday74 15.12.15

Ответы:


257

Ваша swallowErrorфункция должна выглядеть так:

function swallowError (error) {

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

  this.emit('end')
}

Я думаю, что вы должны привязать эту функцию к errorсобытию, которое выпало из задачи, а не к watchзадаче, потому что проблема не в этом, вы должны устанавливать этот обратный вызов ошибки для каждой задачи, которая может дать сбой, например, плагинов, которые ломаются, когда у вас есть пропустил ;или что-то еще, чтобы предотвратить watchзадачу остановить.

Примеры :

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

В качестве альтернативы, если вы не против включить другой модуль, вы можете использовать функцию log в gulp-util, чтобы удержать вас от объявления дополнительной функции в вашем gulpfile:

.on('error', gutil.log)

Но я могу порекомендовать взглянуть на удивительный плагин gulp-plumber , который используется для удаления onerrorобработчика errorсобытия, вызывающего разрыв потоков. Он очень прост в использовании и не дает вам поймать все задачи, которые могут быть не выполнены.

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

Подробнее об этом в этой статье создатель соответствующего плагина.


Я ценю вашу помощь. Смотрите мое редактирование. Я думаю, что я делаю то, что вы говорите, и это все еще не работает
Джордж Мауэр

2
Возможно, вы захотите отредактировать свой ответ, чтобы отразить окончательную правильную вещь, поэтому любой, кто найдет это в будущем, не должен читать цепочку комментариев.
Джордж Мауэр

1
Это все хорошо, но зачем мне писать свои собственные обработчики ошибок? Единственная причина, по которой я использую gulp, grunt или любой другой инструмент предварительной обработки, заключается в том, что он делает грязную работу за меня. Синтаксическая ошибка где-то в моем коде (которая обязательно должна произойти) не должна приводить к остановке всей системы. Сравните это с графическим интерфейсом Prepros (еще один препроцессор) - этот инструмент точно сообщает вам, где находится ваша ошибка, и не зависает и не закрывается, если что-то не так.
Кокодоко

1
Удивительно, что никто даже не упомянул steam-combiner2, хотя это официальный рецепт глотка! сантехник также хорош, но я всегда предпочитаю следовать рецептам глотка, рецепты глотка постоянно
обновляются

1
Столкнувшись с той же загадкой, я решил использовать оболочку, gulp.watch()которая добавляет .on('error', function() { this.emit('end') })результат функции часов, так как именно эту задачу я хочу сделать устойчивой к зависанию. Работает действительно хорошо, и отдельные задачи, которые не выполняются, все еще печатают свои собственные сообщения об ошибках. Посмотрите код: github.com/specious/specious.github.io/commit/f8571d28
tknomad

20

Приведенные выше примеры не работают для меня. Следующее сделал хотя:

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

Это замечательно, но также было бы хорошо уведомить, если произошла ошибка (в настоящее время это просто регистрирует ошибку в терминале)
raison

4

С одним форматом файлов

(например: * .coffee only)

Если вы хотите работать только с одним форматом файлов, то gulp-plumberэто ваше решение.

Например, ошибки с богатой обработкой и предупреждение для написания кофе:

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

С несколькими типами форматов файлов

(например: * .coffee и * .js одновременно)

Но если вы не будете работать с несколькими типами форматов файлов (например: *.jsи *.coffee), тогда я опубликую свое решение.

Я просто опубликую понятный код здесь с некоторым описанием ранее.

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

Я встал вопрос с gulp-plumberи gulp-ifиспользованиемgulp.watch(...

См. Связанную проблему здесь: https://github.com/floatdrop/gulp-plumber/issues/23

Так что лучшим вариантом для меня было:

  • Каждая часть в виде файла, и объединить после . Создайте несколько задач, которые могут обрабатывать каждую часть в отдельном файле (как это делает Grunt), и объедините их
  • Каждая часть как поток, а слияние потоков после . Объедините два потока, используя merge-stream(который был сделан из event-stream), в один и продолжайте работу (я попробовал это сначала, и это прекрасно работает для меня, так что это более быстрое решение, чем предыдущий)

Каждая часть как поток, а слияние потоков после

Она является основной частью моего кода:

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

Каждая часть в виде файла, и объединить после

Если разделить это на части, то в каждой части должен быть создан файл результатов. Например:

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:

Здесь были использованы узлы модулей и функций:

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

Я все еще вижу улучшения для этих комментариев. Как сравнение скорости и ссылки только для файлов .js (исключая минифицированные или сторонние библиотеки или библиотеки из bower_components). Но в основном его легко отрегулировать и решить с помощью Google.com
Роман М. Косс

3

Мне нравится использовать gulp plumber, потому что он может добавить глобального слушателя к задаче и отображать значимое сообщение.

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

Ссылка: https://scotch.io/tutorials/prevent-errors-from-crashing-gulp-watch


2

В качестве обходного пути для 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;
}

Добавьте его в свой gulpfile.js и используйте так:

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

Это похоже на самую естественную обработку ошибок для меня. Если вообще нет обработчика ошибок, он выдаст ошибку. Протестировано с Node v0.11.13.


2

Простым решением этого является gulp watchсоздание бесконечного цикла внутри оболочки Bash (или sh).

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

Сохраняйте вывод этой команды в видимой области на экране, пока вы редактируете свой JavaScript. Когда ваши изменения приводят к ошибке, Gulp вылетает, печатает трассировку стека, ждет секунду и возобновляет просмотр ваших исходных файлов. Затем вы можете исправить синтаксическую ошибку, и Gulp укажет, было ли редактирование успешным, либо распечатав его нормальный вывод, либо снова выйдя из строя (затем возобновив).

Это будет работать в терминале Linux или Mac. Если вы используете Windows, используйте Cygwin или Ubuntu Bash (Windows 10).


Какой это язык? Кроме того, есть ли какая-то польза от предложенных решений?
Джордж Мауэр

Это написано на Bash / sh. Он требует меньше кода, чем другие решения, и его легче запомнить и реализовать.
Джесси Хоган,

Можете ли вы отредактировать тот факт, что его bash и ваши другие мысли по этому поводу в ваш ответ? Я не согласен, что это легче, чем сантехник, но это правильный (если не кроссплатформенный) подход. Сначала я проголосовал, потому что не было ясно, даже на каком языке это было, и я не могу изменить свой голос, пока вы не отредактируете ответ.
Джордж Мауэр

1
Таким образом, вы предпочитаете прервать поток и перезапустить весь процесс, который может занять довольно много времени, в зависимости от того, что вы делаете с бесконечным циклом, а не просто отловить ошибку? Кажется, немного беспокоит меня. И если говорить о меньшем количестве кода, вам нужно 16 символов, чтобы добавить сантехника к вашей задаче, в то время как ваше решение занимает 36 без учета gulp watch.
Бальтазар

Спасибо за отзыв, @GeorgeMauer, я внес изменения и написал о средах / платформах, в которых он работает.
Джесси Хоган,

1

Машинопись

Это то, что сработало для меня. Я работаю с Typescriptэтой функцией и разделяю ее (до путаницы с thisключевыми словами) less. Это работает с тем 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'))
})

Теперь, когда вы watch .lessфайлы, и errorпроисходит, watchне будет останавливаться и новые изменения будут обрабатываться в соответствии с вашим less task.

ПРИМЕЧАНИЕ : я пробовал с l.end();; Однако это не сработало. Тем не менее, l.emit('end');полностью работает.

Надеюсь, это поможет. Удачи.


1

Это сработало для меня ->

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

Я просто добавил строку .on ('error', console.error.bind (console)), но мне пришлось запустить команду gulp от имени пользователя root. Я запускаю gulp узла в приложении php, поэтому у меня есть несколько учетных записей на одном сервере, поэтому я столкнулся с проблемой взлома gulp из-за синтаксических ошибок, потому что я не запускал gulp от имени root ... Может быть, сантехник и некоторые из другие ответы здесь работали бы для меня, если бы я работал от имени пользователя root. Благодарим за код Accio https://www.youtube.com/watch?v=iMR7hq4ABOw за ответ. Он сказал, что, обрабатывая ошибку, он помогает вам определить, в какой строке находится ошибка, и какая она есть в консоли, но также не дает глотку нарушить синтаксическую ошибку. Он сказал, что это было довольно легкое решение, так что не уверен, что это сработает для того, что вы ищете. Быстрое решение, хотя, стоит попробовать. Надеюсь, это поможет кому-то!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.