pax_global_header00006660000000000000000000000064126563474060014527gustar00rootroot0000000000000052 comment=982ec116283b5b5907448ac932a96be31c7d872f gulp-plumber-1.1.0/000077500000000000000000000000001265634740600141415ustar00rootroot00000000000000gulp-plumber-1.1.0/.editorconfig000066400000000000000000000003601265634740600166150ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false [*.json] indent_size = 2 gulp-plumber-1.1.0/.gitattributes000066400000000000000000000000131265634740600170260ustar00rootroot00000000000000* text=autogulp-plumber-1.1.0/.gitignore000066400000000000000000000000471265634740600161320ustar00rootroot00000000000000.DS_Store node_modules/ temp/ coverage gulp-plumber-1.1.0/.npmignore000066400000000000000000000001241265634740600161350ustar00rootroot00000000000000temp/ coverage .editorconfig test .gitattributes .jshintrc .travis.yml gulpfile.js gulp-plumber-1.1.0/.travis.yml000066400000000000000000000001151265634740600162470ustar00rootroot00000000000000sudo: false language: node_js node_js: - '5' - '4' - '0.12' - '0.10' gulp-plumber-1.1.0/LICENSE000066400000000000000000000020451265634740600151470ustar00rootroot00000000000000Copyright 2014 Vsevolod Strukchinsky Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gulp-plumber-1.1.0/README.md000066400000000000000000000046271265634740600154310ustar00rootroot00000000000000# :monkey: gulp-plumber [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url] > Prevent pipe breaking caused by errors from [gulp](https://github.com/wearefractal/gulp) plugins This :monkey:-patch plugin is fixing [issue with Node Streams piping](https://github.com/gulpjs/gulp/issues/91). For explanations, read [this small article](https://gist.github.com/floatdrop/8269868). Briefly it replaces `pipe` method and removes standard `onerror` handler on `error` event, which unpipes streams on error by default. ## Usage :monkey: First, install `gulp-plumber` as a development dependency: ```shell npm install --save-dev gulp-plumber ``` Then, add it to your `gulpfile.js`: ```javascript var plumber = require('gulp-plumber'); var coffee = require('gulp-coffee'); gulp.src('./src/*.ext') .pipe(plumber()) .pipe(coffee()) .pipe(gulp.dest('./dist')); ``` ## API :monkey: ### :monkey: plumber([options]) Returns Stream, that fixes `pipe` methods on Streams that are next in pipeline. #### options Type: `Object` / `Function` Default: `{}` Sets options described below from its properties. If type is `Function` it will be set as `errorHandler`. #### options.inherit Type: `Boolean` Default: `true` Monkeypatch `pipe` functions in underlying streams in pipeline. #### options.errorHandler Type: `Boolean` / `Function` Default: `true` Handle errors in underlying streams and output them to console. * `function` passed - it will be attached to stream `on('error')`. * `false` passed - error handler will not be attached. * `undefined` - default error handler will be attached. ### plumber.stop() This method will return default behaviour for pipeline after it was piped. ```javascript var plumber = require('gulp-plumber'); gulp.src('./src/*.scss') .pipe(plumber()) .pipe(sass()) .pipe(uglify()) .pipe(plumber.stop()) .pipe(gulp.dest('./dist')); ``` ## License :monkey: [MIT License](http://en.wikipedia.org/wiki/MIT_License) [npm-url]: https://npmjs.org/package/gulp-plumber [npm-image]: http://img.shields.io/npm/v/gulp-plumber.svg?style=flat [travis-url]: https://travis-ci.org/floatdrop/gulp-plumber [travis-image]: http://img.shields.io/travis/floatdrop/gulp-plumber.svg?style=flat [depstat-url]: https://david-dm.org/floatdrop/gulp-plumber [depstat-image]: http://img.shields.io/david/floatdrop/gulp-plumber.svg?style=flat gulp-plumber-1.1.0/index.js000066400000000000000000000043021265634740600156050ustar00rootroot00000000000000'use strict'; var through2 = require('through2'); var EE = require('events').EventEmitter; var gutil = require('gulp-util'); function removeDefaultHandler(stream, event) { var found = false; stream.listeners(event).forEach(function (item) { if (item.name === 'on' + event) { found = item; this.removeListener(event, item); } }, stream); return found; } function wrapPanicOnErrorHandler(stream) { var oldHandler = removeDefaultHandler(stream, 'error'); if (oldHandler) { stream.on('error', function onerror2(er) { if (EE.listenerCount(stream, 'error') === 1) { this.removeListener('error', onerror2); oldHandler.call(stream, er); } }); } } function defaultErrorHandler(error) { // onerror2 and this handler if (EE.listenerCount(this, 'error') < 3) { gutil.log( gutil.colors.cyan('Plumber') + gutil.colors.red(' found unhandled error:\n'), error.toString() ); } } function plumber(opts) { opts = opts || {}; if (typeof opts === 'function') { opts = {errorHandler: opts}; } var through = through2.obj(); through._plumber = true; if (opts.errorHandler !== false) { through.errorHandler = (typeof opts.errorHandler === 'function') ? opts.errorHandler : defaultErrorHandler; } function patchPipe(stream) { if (stream.pipe2) { wrapPanicOnErrorHandler(stream); stream._pipe = stream._pipe || stream.pipe; stream.pipe = stream.pipe2; stream._plumbed = true; } } through.pipe2 = function pipe2(dest) { if (!dest) { throw new gutil.PluginError('plumber', 'Can\'t pipe to undefined'); } this._pipe.apply(this, arguments); if (dest._unplumbed) { return dest; } removeDefaultHandler(this, 'error'); if (dest._plumber) { return dest; } dest.pipe2 = pipe2; // Patching pipe method if (opts.inherit !== false) { patchPipe(dest); } // Placing custom on error handler if (this.errorHandler) { dest.errorHandler = this.errorHandler; dest.on('error', this.errorHandler.bind(dest)); } dest._plumbed = true; return dest; }; patchPipe(through); return through; } module.exports = plumber; module.exports.stop = function () { var through = through2.obj(); through._unplumbed = true; return through; }; gulp-plumber-1.1.0/package.json000066400000000000000000000020531265634740600164270ustar00rootroot00000000000000{ "name": "gulp-plumber", "version": "1.1.0", "description": "Prevent pipe breaking caused by errors from gulp plugins", "keywords": [ "gulpplugin" ], "homepage": "https://github.com/floatdrop/gulp-plumber", "bugs": "https://github.com/floatdrop/gulp-plumber/issues", "author": { "name": "Vsevolod Strukchinsky", "email": "floatdrop@gmail.com", "url": "https://github.com/floatdrop" }, "main": "./index.js", "repository": { "type": "git", "url": "git://github.com/floatdrop/gulp-plumber.git" }, "scripts": { "test": "xo && mocha -R spec" }, "dependencies": { "gulp-util": "^3", "through2": "^2" }, "devDependencies": { "coveralls": "^2.11.6", "event-stream": "^3.3.2", "gulp": "^3.9.1", "istanbul": "^0.4.2", "mocha": "^2.4.5", "mocha-lcov-reporter": "^1.0.0", "should": "^8.2.2", "through": "^2.3.8", "xo": "^0.12.1" }, "engines": { "node": ">=0.10", "npm": ">=1.2.10" }, "xo": { "ignore": [ "test/**" ] }, "license": "MIT" } gulp-plumber-1.1.0/test/000077500000000000000000000000001265634740600151205ustar00rootroot00000000000000gulp-plumber-1.1.0/test/errorHandling.js000066400000000000000000000121561265634740600202610ustar00rootroot00000000000000/*global describe, it, before, beforeEach */ 'use strict'; var should = require('should'), es = require('event-stream'), EE = require('events').EventEmitter, gutil = require('gulp-util'), gulp = require('gulp'); var plumber = require('../'); var errorMessage = 'Bang!'; var fixturesGlob = ['./test/fixtures/index.js', './test/fixtures/test.js']; var delay = 20; describe('errorHandler', function () { beforeEach(function () { this.failingEmitStream = new es.through(function (file) { this.emit('data', file); this.emit('error', new Error('Bang!')); }); var i = 0; this.failingQueueStream = new es.through(function (file) { this.queue(file); i ++; if (i === 2) { this.emit('error', new Error('Bang!')); } }); }); before(function (done) { gulp.src(fixturesGlob) .pipe(es.writeArray(function (err, array) { this.expected = array; done(); }.bind(this))); }); it('should attach custom error handler', function (done) { gulp.src(fixturesGlob) .pipe(plumber({ errorHandler: function (error) { error.toString().should.containEql(errorMessage); done(); }})) .pipe(this.failingQueueStream); }); it('should attach custom error handler with function argument', function (done) { gulp.src(fixturesGlob) .pipe(plumber(function (error) { error.toString().should.containEql(errorMessage); done(); })) .pipe(this.failingQueueStream); }); it('should attach default error handler', function (done) { var mario = plumber(); mario.errorHandler = function (error) { error.toString().should.containEql(errorMessage); done(); }; gulp.src(fixturesGlob) .pipe(mario) .pipe(this.failingQueueStream); }); it('default error handler should work', function (done) { var mario = plumber(); var _ = gutil.log; gutil.log = done.bind(null, null); gulp.src(fixturesGlob) .pipe(mario) .pipe(this.failingQueueStream) .on('end', function () { gutil.log = _; }); }); describe('should attach error handler', function () { it('in non-flowing mode', function (done) { var delayed = gutil.noop(); setTimeout(delayed.write.bind(delayed, 'data'), delay); setTimeout(delayed.write.bind(delayed, 'data'), delay); delayed .pipe(plumber({ errorHandler: done.bind(null, null) })) .pipe(this.failingQueueStream); }); // it.only('in flowing mode', function (done) { // var delayed = gutil.noop(); // setTimeout(delayed.write.bind(delayed, 'data'), delay); // setTimeout(delayed.write.bind(delayed, 'data'), delay); // delayed // .pipe(plumber({ errorHandler: done.bind(null, null) })) // // You cant do on('data') and pipe simultaniously. // .on('data', function () { }) // .pipe(this.failingQueueStream); // }); }); describe('should not attach error handler', function () { it('in non-flowing mode', function (done) { (function () { gulp.src(fixturesGlob) .pipe(plumber({ errorHandler: false })) .pipe(this.failingQueueStream) .on('end', done); }).should.throw(); done(); }); // it('in flowing mode', function (done) { // (function () { // gulp.src(fixturesGlob) // .pipe(plumber({ errorHandler: false })) // // You cant do on('data') and pipe simultaniously. // .on('data', function () { }) // .pipe(this.failingQueueStream) // .on('end', done); // }).should.throw(); // done(); // }); }); describe('throw', function () { it('on piping to undefined', function () { plumber().pipe.should.throw(/Can't pipe to undefined/); }); it('after cleanup', function (done) { var mario = plumber({ errorHandler: false }); var stream = mario.pipe(gutil.noop()); (function () { stream.emit('error', new Error(errorMessage)); }).should.throw(); EE.listenerCount(mario, 'data').should.eql(0); EE.listenerCount(mario, 'drain').should.eql(0); EE.listenerCount(mario, 'error').should.eql(0); EE.listenerCount(mario, 'close').should.eql(0); EE.listenerCount(stream, 'data').should.eql(0); EE.listenerCount(stream, 'drain').should.eql(0); EE.listenerCount(stream, 'error').should.eql(0); EE.listenerCount(stream, 'close').should.eql(0); done(); }); }); }); gulp-plumber-1.1.0/test/fixtures/000077500000000000000000000000001265634740600167715ustar00rootroot00000000000000gulp-plumber-1.1.0/test/fixtures/index.js000066400000000000000000000000001265634740600204240ustar00rootroot00000000000000gulp-plumber-1.1.0/test/fixtures/test.js000066400000000000000000000000071265634740600203030ustar00rootroot00000000000000test.jsgulp-plumber-1.1.0/test/passThrough.js000066400000000000000000000047611265634740600177750ustar00rootroot00000000000000/*global describe, it, before */ 'use strict'; var should = require('should'), es = require('event-stream'), gutil = require('gulp-util'), gulp = require('gulp'); var plumber = require('../'); var fixturesGlob = ['./test/fixtures/*']; describe('stream', function () { it('piping into second plumber should keep piping', function (done) { gulp.src(fixturesGlob) .pipe(plumber()) .pipe(gutil.noop()) .pipe(plumber()) .pipe(es.writeArray(function (err, array) { array.should.eql(this.expected); done(); }.bind(this))) .on('end', function () { done(); }); }); it('should work with es.readarray', function (done) { var expected = ['1\n', '2\n', '3\n', '4\n', '5\n']; es.readArray([1, 2, 3, 4, 5]) .pipe(plumber()) .pipe(es.stringify()) .pipe(es.writeArray(function (error, array) { array.should.eql(expected); done(); })); }); it('should emit `end` after source emit `finish`', function (done) { gulp.src(fixturesGlob) .pipe(plumber()) // Fetchout data .on('data', function () { }) .on('end', done) .on('error', done); }); describe('should passThrough all incoming files', function () { it('in non-flowing mode', function (done) { gulp.src(fixturesGlob) .pipe(plumber({ errorHandler: done })) .pipe(es.writeArray(function (err, array) { array.should.eql(this.expected); done(); }.bind(this))) .on('error', done); }); // it('in flowing mode', function (done) { // gulp.src(fixturesGlob) // .pipe(plumber({ errorHandler: done })) // // You cant do on('data') and pipe simultaniously. // .on('data', function (file) { should.exist(file); }) // .pipe(es.writeArray(function (err, array) { // array.should.eql(this.expected); // done(); // }.bind(this))) // .on('error', done); // }); }); before(function (done) { gulp.src(fixturesGlob) .pipe(es.writeArray(function (err, array) { this.expected = array; done(); }.bind(this))); }); }); gulp-plumber-1.1.0/test/pipePatching.js000066400000000000000000000060421265634740600200730ustar00rootroot00000000000000/*global describe, it, before */ 'use strict'; var should = require('should'), es = require('event-stream'), noop = require('./util').noop, gulp = require('gulp'); var plumber = require('../'); var fixturesGlob = ['./test/fixtures/*']; describe('pipe', function () { it('should keep piping after error', function (done) { var expected = [1, 3, 5]; var badBoy = es.through(function (data) { if (data % 2 === 0) { return this.emit('error', new Error(data)); } this.emit('data', data); }); var actual = []; es.readArray([1, 2, 3, 4, 5, 6]) .pipe(plumber()) .pipe(badBoy) .pipe(es.through(function (data) { actual.push(data); this.emit('data', data); })) .on('error', function (err) { done(err); }) .on('end', function () { actual.should.eql(expected); done(); }); }); it('should skip patching with `inherit` === false', function (done) { var lastNoop = noop(); var mario = plumber({ inherit: false }); gulp.src(fixturesGlob) .pipe(mario) .pipe(noop()) .pipe(noop()) .pipe(lastNoop) .on('end', function () { should.not.exist(lastNoop._plumbed); done(); }); }); describe('should be patched at itself and underlying streams', function () { it('in non-flowing mode', function (done) { var lastNoop = noop(); var mario = plumber(); var m = gulp.src(fixturesGlob) .pipe(mario) .pipe(noop()) .pipe(noop()) .pipe(lastNoop) .on('end', function () { should.exist(lastNoop._plumbed); done(); }); }); it('in flowing mode', function (done) { var lastNoop = noop(); var mario = plumber(); gulp.src(fixturesGlob) .pipe(mario) .on('data', function (file) { should.exist(file); }) .pipe(noop()) .pipe(noop()) .pipe(lastNoop) .on('end', function () { should.exist(lastNoop._plumbed); done(); }); }); }); it('piping into second plumber should does nothing', function (done) { var lastNoop = noop(); gulp.src(fixturesGlob) .pipe(plumber()) .pipe(noop()).pipe(noop()) .pipe(plumber()) .pipe(lastNoop) .on('end', function () { should.exist(lastNoop._plumbed); done(); }); }); before(function (done) { gulp.src(fixturesGlob) .pipe(es.writeArray(function (err, array) { this.expected = array; done(); }.bind(this))); }); }); gulp-plumber-1.1.0/test/pipeUnpatching.js000066400000000000000000000030261265634740600204350ustar00rootroot00000000000000/*global describe, it, before */ 'use strict'; var es = require('event-stream'), gulp = require('gulp'); var plumber = require('../'); var fixturesGlob = ['./test/fixtures/*']; describe('unpipe', function () { it('should not keep piping after error', function (done) { var expected = [1, 3, 5]; var badBoy = es.through(function (data) { if (data % 2 === 0) { return this.emit('error', new Error(data)); } this.emit('data', data); }); var badass = es.through(function (data) { if (data === 5) { return this.emit('error', new Error('Badass')); } this.emit('data', data); }); var actual = []; es.readArray([1, 2, 3, 4, 5, 6]) .pipe(plumber()) .pipe(badBoy) .pipe(es.through(function (data) { actual.push(data); this.emit('data', data); })) .pipe(plumber.stop()) .pipe(badass) .on('error', function (err) { err.should.eql(new Error('Badass')); actual.should.eql(expected); done(); }) .on('end', function () { done('Error was not fired'); }); }); before(function (done) { gulp.src(fixturesGlob) .pipe(es.writeArray(function (err, array) { this.expected = array; done(); }.bind(this))); }); }); gulp-plumber-1.1.0/test/util/000077500000000000000000000000001265634740600160755ustar00rootroot00000000000000gulp-plumber-1.1.0/test/util/index.js000066400000000000000000000002241265634740600175400ustar00rootroot00000000000000'use strict'; var through = require('through'); module.exports.noop = function () { return through(function (data) { this.queue(data); }); };