防止错误破坏/崩溃 gulp watch

Posted

技术标签:

【中文标题】防止错误破坏/崩溃 gulp watch【英文标题】:Prevent errors from breaking / crashing gulp watch 【发布时间】:2014-07-21 04:54:47 【问题描述】:

我正在运行 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 stop 出现错误 - 显然不是我想要的。

As recommended elsewhere I tried this

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)

【问题讨论】:

在github.com/gulpjs/gulp/tree/master/docs/recipes 上查看官方 gulp 食谱,其中有一个使用 stream-combiner2 组合流到处理错误的食谱,这是官方答案! 【参考方案1】:

您的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'))
)

或者,如果你不介意包含另一个模块,你可以使用 gulp-utillog 函数来防止你在你的gulpfile:

.on('error', gutil.log)

但我可能建议您看看很棒的 gulp-plumber 插件,该插件用于删除 error 事件的 onerror 处理程序,从而导致流中断。它使用起来非常简单,它可以阻止您捕获所有可能失败的任务。

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

相关插件的创建者在this article 上提供更多信息。

【讨论】:

感谢您的帮助。请参阅我的编辑。我想我正在做你所说的,但它仍然不起作用 您可能希望编辑您的答案以反映最终正确的做法,这样以后发现此问题的任何人都不必阅读评论链。 这一切都很好,但为什么我必须编写自己的错误处理程序?我使用 gulp 或 grunt 或任何预处理工具的全部原因是它为我做了肮脏的工作。我的代码中某处的语法错误(必然会发生)不应该使整个系统陷入停顿。将此与 Prepros(另一个预处理器)的 GUI 进行比较 - 该工具会准确地告诉您错误在哪里,并且在出现问题时不会挂起或退出。 令人惊讶的是,即使它是官方的 gulp 配方,也没有人提到 steam-combiner2!水管工也不错,但我总是更喜欢遵循 ​​gulp 食谱,gulp 食谱由 gulp 保持最新,可以在github.com/gulpjs/gulp/tree/master/docs/recipes 查看 面对同样的难题,我决定在 gulp.watch() 周围使用一个包装器,在 watch 函数的结果中添加一个 .on('error', function() this.emit('end') ),因为它特别是我想要使其具有弹性的 watch 任务挂断。工作得非常好,失败的个别任务仍然打印自己的错误消息。见代码:github.com/specious/specious.github.io/commit/f8571d28【参考方案2】:

上面的例子对我不起作用。但是,以下是:

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

【讨论】:

这很好,但如果有错误通知也很好(目前这只是在终端中记录错误)【参考方案3】:

使用一种格式的文件

(例如:仅限 *.coffee)

如果您只想使用一种格式的文件,那么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-plumbergulp-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;

);

附注:

这里用到的节点模块和函数:

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

【讨论】:

我仍然看到这个 cmets 的改进。像速度比较,以及仅用于 .js 文件的链接(不包括缩小或 3d 方库,或来自 bower_components 的库)。但基本上它很容易调整和解决 Google.com 的这种嗅探【参考方案4】:

我喜欢使用 gulp 管道工,因为它可以向任务添加全局侦听器并显示有意义的消息。

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

【讨论】:

【参考方案5】:

我已经实施了以下 hack 作为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 测试。

【讨论】:

【参考方案6】:

对此的简单解决方案是将gulp watch 置于 Bash(或 sh)shell 内的无限循环中。

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

在编辑 javascript 时,将此命令的输出保持在屏幕上的可见区域。当您的编辑导致错误时,Gulp 将崩溃,打印其堆栈跟踪,等待一秒钟,然后继续查看您的源文件。然后您可以更正语法错误,Gulp 将通过打印出它的正常输出或再次崩溃(然后恢复)来指示编辑是否成功。

这适用于 Linux 或 Mac 终端。如果您使用的是 Windows,请使用 Cygwin 或 Ubuntu Bash (Windows 10)。

【讨论】:

这是什么语言?另外,与建议的解决方案相比,这样做有什么好处吗? 它是用 Bash/sh 编写的。与其他解决方案相比,它需要更少的代码,并且更容易记住和实施。 您能否将它的 bash 和您对此的其他想法编辑为您的答案?我不同意它比管道工更容易,但它是一种有效的(如果不是跨平台的)方法。我最初投了反对票,因为它甚至不清楚它是什么语言,除非你编辑答案,否则我不能改变我的投票。 所以您更喜欢让流崩溃并重新启动整个过程,这可能需要相当长的时间,具体取决于您使用无限循环执行的操作,而不是简单地捕获错误?对我来说似乎有点不安。说到更少的代码,它需要你 16 个字符来为你的任务添加管道工,而你的解决方案需要 36 个字符而不计算 gulp watch 感谢@GeorgeMauer 的反馈,我进行了编辑并写了它工作的环境/平台。【参考方案7】:

打字稿

这对我有用。我使用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'); 完全有效。

希望对您有所帮助。祝你好运。

【讨论】:

【参考方案8】:

这对我有用->

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('错误', console.error.bind(console)) 行,但我必须运行 吞咽 命令作为根。我在一个 php 应用程序上运行 node gulp,所以我在一台服务器上有多个帐户,这就是为什么我遇到 gulp 打破语法错误的问题,因为我没有以 root 身份运行 gulp ...也许管道工和一些如果我以 root 身份运行,这里的其他答案会对我有用。 归功于 Accio Code https://www.youtube.com/watch?v=iMR7hq4ABOw 的答案。他说,通过处理错误,它可以帮助您确定错误在哪一行以及它在控制台中是什么,而且还可以阻止 gulp 因语法错误而中断。他说这是一种轻量级的修复,所以不确定它是否适用于您正在寻找的东西。不过快速修复,值得一试。希望这对某人有帮助!

【讨论】:

以上是关于防止错误破坏/崩溃 gulp watch的主要内容,如果未能解决你的问题,请参考以下文章

忽略或防止 ESLint 错误破坏 React 项目中的构建 (create-react-project)

Nodemon 与 gulp watch 绑定时崩溃并重新启动两次以上

Angular ng build --watch 似乎在 Angular 版本 11 中被破坏了

如何防止用户破坏对象

有啥方法可以防止以角度破坏组件?

剑道网格破坏命令不刷新网格