├── .travis.yml ├── .gitignore ├── .npmignore ├── .eslintrc ├── LICENSE ├── package.json ├── src └── index.js ├── README.md └── tests └── index.mocha.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | node_modules/ 4 | test/results 5 | .git 6 | .coveralls.yml 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | node_modules/ 4 | .git 5 | .coveralls.yml 6 | tests/results 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-simplifield", 3 | "env": { 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "globals": { 8 | "require": false, 9 | "describe": false, 10 | "beforeEach": false, 11 | "afterEach": false, 12 | "before": false, 13 | "after": false, 14 | "it": false, 15 | "sinon": false, 16 | "module": false 17 | }, 18 | "rules": { 19 | "strict": [2, "global"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 Nicolas Froidure, 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-streamify", 3 | "description": "Wrap old plugins to support streams.", 4 | "version": "1.0.2", 5 | "homepage": "https://github.com/nfroidure/gulp-streamify", 6 | "author": { 7 | "name": "Nicolas Froidure", 8 | "url": "http://www.insertafter.com/blog.html" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/nfroidure/gulp-streamify.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/nfroidure/gulp-streamify/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "https://github.com/nfroidure/gulp-streamify/blob/master/LICENSE" 21 | } 22 | ], 23 | "main": "src/index.js", 24 | "engines": { 25 | "node": ">= 0.10.0" 26 | }, 27 | "scripts": { 28 | "test": "mocha tests/*.mocha.js", 29 | "coveralls": "istanbul cover _mocha --report lcovonly -- tests/*.mocha.js -R spec -t 5000 && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage", 30 | "cover": "istanbul cover --report html _mocha -- tests/*.mocha.js -R spec -t 5000", 31 | "trinita": "npm-check-updates -u && npm test && git commit package.json -m \"Dependencies update\" && git push", 32 | "cli": "env NPM_RUN_CLI=1", 33 | "lint": "eslint **/*.js", 34 | "prepublish": "npm test && npm run lint" 35 | }, 36 | "keywords": [ 37 | "gulpplugin", 38 | "gulp", 39 | "gulp-plugin", 40 | "stream", 41 | "wrapper" 42 | ], 43 | "devDependencies": { 44 | "coveralls": "~2.11.4", 45 | "eslint": "^1.3.1", 46 | "eslint-config-simplifield": "^1.1.0", 47 | "gulp-util": "~3.0.6", 48 | "istanbul": "~0.3.19", 49 | "mocha": "~2.3.1", 50 | "mocha-lcov-reporter": "0.0.2", 51 | "sf-lint": "^1.0.2", 52 | "streamtest": "^1.2.1" 53 | }, 54 | "dependencies": { 55 | "plexer": "1.0.1" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Stream = require('stream'); 4 | var Duplexer = require('plexer'); 5 | 6 | // Plugin function 7 | function streamifyGulp(pluginStream) { 8 | 9 | var inputStream = new Stream.Transform({ objectMode: true }); 10 | var outputStream = new Stream.Transform({ objectMode: true }); 11 | var duplex = new Duplexer({ objectMode: true }, inputStream, outputStream); 12 | 13 | // Accepting functions returning streams 14 | if('function' === typeof pluginStream) { 15 | pluginStream = pluginStream(); 16 | } 17 | 18 | // Listening for plugin errors and reemit 19 | pluginStream.on('error', function(error) { 20 | duplex.emit('error', error); 21 | }); 22 | 23 | // Change files contents from stream to buffer and write to the plugin stream 24 | inputStream._transform = function(file, unused, cb) { 25 | // Buffering the file stream 26 | var originalStream; 27 | var buf; 28 | var bufstream; 29 | 30 | if(file.isNull() || file.isBuffer()) { 31 | inputStream.push(file); 32 | return cb(); 33 | } 34 | file.wasStream = true; 35 | originalStream = file.contents; 36 | buf = new Buffer(0); 37 | bufstream = new Stream.Writable(); 38 | 39 | // Buffer the stream 40 | bufstream._write = function(chunk, encoding, cb2) { 41 | buf = Buffer.concat([buf, chunk], buf.length + chunk.length); 42 | cb2(); 43 | }; 44 | 45 | // When buffered 46 | bufstream.once('finish', function() { 47 | // Send the buffer wrapped in a file 48 | file.contents = buf; 49 | inputStream.push(file); 50 | cb(); 51 | }); 52 | 53 | originalStream.pipe(bufstream); 54 | 55 | }; 56 | 57 | // Change files contents from buffer to stream and write to the output stream 58 | outputStream._transform = function(file, unused, cb) { 59 | var buf; 60 | var newStream; 61 | 62 | if(file.isNull() || !file.wasStream) { 63 | outputStream.push(file); 64 | return cb(); 65 | } 66 | delete file.wasStream; 67 | // Get the transformed buffer 68 | buf = file.contents; 69 | newStream = new Stream.Readable(); 70 | // Write the buffer only when datas are needed 71 | newStream._read = function() { 72 | // Write the content back to the stream 73 | newStream.push(buf); 74 | newStream.push(null); 75 | }; 76 | // Pass the file out 77 | file.contents = newStream; 78 | outputStream.push(file); 79 | cb(); 80 | }; 81 | outputStream._flush = function(cb) { 82 | setImmediate(function() { 83 | // Old streams WTF 84 | if(!pluginStream._readableState) { 85 | outputStream.emit('end'); 86 | duplex.emit('end'); 87 | } 88 | }); 89 | cb(); 90 | }; 91 | 92 | inputStream 93 | .pipe(pluginStream) 94 | .pipe(outputStream); 95 | 96 | return duplex; 97 | 98 | } 99 | 100 | // Export the plugin main function 101 | module.exports = streamifyGulp; 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-streamify 2 | > Wrap old [Gulp](http://gulpjs.com/) plugins to support streams. 3 | 4 | [![NPM version](https://badge.fury.io/js/gulp-streamify.svg)](https://npmjs.org/package/gulp-streamify) [![Build status](https://secure.travis-ci.org/nfroidure/gulp-streamify.svg)](https://travis-ci.org/nfroidure/gulp-streamify) [![Dependency Status](https://david-dm.org/nfroidure/gulp-streamify.svg)](https://david-dm.org/nfroidure/gulp-streamify) [![devDependency Status](https://david-dm.org/nfroidure/gulp-streamify/dev-status.svg)](https://david-dm.org/nfroidure/gulp-streamify#info=devDependencies) [![Coverage Status](https://coveralls.io/repos/nfroidure/gulp-streamify/badge.svg?branch=master)](https://coveralls.io/r/nfroidure/gulp-streamify?branch=master) 5 | 6 | It is pretty annoying when Gulp plugins doesn't support streams. This plugin 7 | allows you to wrap them in order to use the stream mode anyway. It is pretty 8 | useful when you want to take advantage of streams on part of your pipelines. 9 | 10 | *Note to gulp plugin developpers*: This plugin should not discourage you to 11 | support streams in your own plugins. I made this plug-in to avoid beeing 12 | stucked with a bad plugin. If your underlying library support streams, please, 13 | use it! Even if it doesn't, use 14 | [BufferStreams](https://npmjs.org/package/bufferstreams) 15 | in your plugins to support streams at the plugin level (it won't block files 16 | to buffer their contents like this library has to do to work). Here is a 17 | [sample of bufferstreams usage](https://github.com/nfroidure/gulp-ttf2eot/blob/master/src/index.js#L73) 18 | in Gulp plugins. 19 | 20 | ## Usage 21 | 22 | First, install `gulp-streamify` as a development dependency: 23 | 24 | ```shell 25 | npm install --save-dev gulp-streamify 26 | ``` 27 | 28 | Then, add it to your `gulpfile.js` and wrap all that shit: 29 | 30 | ```javascript 31 | var streamify = require('gulp-streamify'); 32 | var noStreamPlugin = require('gulp-no-stream'); 33 | 34 | gulp.task('stream', function(){ 35 | gulp.src(['**/*']) 36 | .pipe( streamify( noStreamPlugin() ) ) 37 | .pipe(gulp.dest('/tmp')); 38 | }); 39 | ``` 40 | 41 | If you have several plugins to wrap together, prefer calling `gulp-streamify` 42 | once thanks to the function form of the `gulp-streamify` constructor: 43 | ```javascript 44 | var gStreamify = require('gulp-streamify'); 45 | var noStreamPlugin = require('gulp-no-stream'); 46 | var noStreamPlugin2 = require('gulp-no-stream2'); 47 | var plexer = require('plexer'); 48 | 49 | gulp.task('stream', function(){ 50 | gulp.src(['**/*']) 51 | .pipe(streamify(function() { 52 | var instream = noStreamPlugin(); 53 | var outstream = noStreamPlugin2(); 54 | instream 55 | .pipe(anyOtherStream) 56 | .pipe(outStream); 57 | return plexer(instream, outstream); 58 | })) 59 | .pipe(gulp.dest('/tmp')); 60 | }); 61 | ``` 62 | 63 | ## API 64 | 65 | ### stream : streamify(toBeWrap) 66 | 67 | Take a stream or a function returning a stream to wrap an return a stream mode 68 | compatible stream. 69 | 70 | ## Contributing / Issues 71 | 72 | You may want to contribute to this project, pull requests are welcome if you 73 | accept to publish under the MIT licence. 74 | -------------------------------------------------------------------------------- /tests/index.mocha.js: -------------------------------------------------------------------------------- 1 | /* eslint max-nested-callbacks:[1] */ 2 | 3 | 'use strict'; 4 | 5 | var gStreamify = require('../'); 6 | var Stream = require('stream'); 7 | var streamtest = require('streamtest'); 8 | var gutil = require('gulp-util'); 9 | var assert = require('assert'); 10 | 11 | describe('gulp-streamify', function() { 12 | 13 | // Simple plugin appending test to contents for test purposes 14 | function pluginFunction() { 15 | var pluginStream = new Stream.Transform({ objectMode: true }); 16 | 17 | pluginStream._transform = function(file, unused, cb) { 18 | assert(file.contents instanceof Buffer); 19 | file.contents = Buffer.concat([file.contents, new Buffer('test')]); 20 | pluginStream.push(file); 21 | cb(); 22 | }; 23 | return pluginStream; 24 | } 25 | 26 | streamtest.versions.forEach(function(version) { 27 | 28 | describe('for ' + version + ' streams', function() { 29 | 30 | it('should pass null files through', function(done) { 31 | 32 | gStreamify(streamtest[version].fromObjects([ 33 | new gutil.File({ 34 | cwd: '/home/nfroidure/', 35 | base: '/home/nfroidure/test', 36 | path: '/home/nfroidure/test/file.js', 37 | contents: null, 38 | }), 39 | new gutil.File({ 40 | cwd: '/home/nfroidure/', 41 | base: '/home/nfroidure/test', 42 | path: '/home/nfroidure/test/file2.js', 43 | contents: null, 44 | }), 45 | ])).pipe(streamtest[version].toObjects(function(err, objs) { 46 | if(err) { 47 | return done(err); 48 | } 49 | assert.equal(objs.length, 2); 50 | done(); 51 | })); 52 | 53 | }); 54 | 55 | it('should reemit errors', function(done) { 56 | 57 | var passStream = new Stream.PassThrough({ objectMode: true }); 58 | var stream = gStreamify(passStream); 59 | var inputError = new Error('ich bin ein error'); 60 | 61 | stream.on('error', function(error) { 62 | assert.equal(error, inputError); 63 | done(); 64 | }); 65 | 66 | passStream.emit('error', inputError); 67 | 68 | }); 69 | 70 | describe('in stream mode', function() { 71 | 72 | it('should work with sync streams and sync contents', function(done) { 73 | 74 | var pluginStream = pluginFunction(); 75 | var inputStream = new Stream.PassThrough({ objectMode: true }); 76 | var fakeFile = new gutil.File({ 77 | cwd: '/home/nfroidure/', 78 | base: '/home/nfroidure/test', 79 | path: '/home/nfroidure/test/file.js', 80 | contents: new Stream.PassThrough(), 81 | }); 82 | var fakeFile2 = new gutil.File({ 83 | cwd: '/home/nfroidure/', 84 | base: '/home/nfroidure/test', 85 | path: '/home/nfroidure/test/file2.js', 86 | contents: new Stream.PassThrough(), 87 | }); 88 | 89 | inputStream 90 | .pipe(gStreamify(pluginStream)) 91 | .pipe(streamtest[version].toObjects(function(err, files) { 92 | if(err) { 93 | return done(err); 94 | } 95 | assert.equal(files.length, 2); 96 | assert.equal(files[0].cwd, '/home/nfroidure/'); 97 | assert.equal(files[0].base, '/home/nfroidure/test'); 98 | assert.equal(files[0].path, '/home/nfroidure/test/file.js'); 99 | assert.equal(files[1].cwd, '/home/nfroidure/'); 100 | assert.equal(files[1].base, '/home/nfroidure/test'); 101 | assert.equal(files[1].path, '/home/nfroidure/test/file2.js'); 102 | files[0].pipe(streamtest[version].toText(function(err2, text) { 103 | if(err2) { 104 | return done(err2); 105 | } 106 | assert.equal(text, 'plipplaptest'); 107 | files[1].pipe(streamtest[version].toText(function(err3, text2) { 108 | if(err3) { 109 | return done(err3); 110 | } 111 | assert.equal(text2, 'ploppluptest'); 112 | done(); 113 | })); 114 | })); 115 | })); 116 | 117 | inputStream.write(fakeFile); 118 | inputStream.write(fakeFile2); 119 | inputStream.end(); 120 | 121 | fakeFile.contents.write('plip'); 122 | fakeFile.contents.write('plap'); 123 | fakeFile.contents.end(); 124 | 125 | fakeFile2.contents.write('plop'); 126 | fakeFile2.contents.write('plup'); 127 | fakeFile2.contents.end(); 128 | 129 | }); 130 | 131 | it('should work with sync streams and async contents', function(done) { 132 | 133 | var pluginStream = pluginFunction(); 134 | 135 | var inputStream = new Stream.PassThrough({ objectMode: true }); 136 | var fakeFile = new gutil.File({ 137 | cwd: '/home/nfroidure/', 138 | base: '/home/nfroidure/test', 139 | path: '/home/nfroidure/test/file.js', 140 | contents: streamtest.v2.fromChunks(['plip', 'plap']), 141 | }); 142 | var fakeFile2 = new gutil.File({ 143 | cwd: '/home/nfroidure/', 144 | base: '/home/nfroidure/test', 145 | path: '/home/nfroidure/test/file2.js', 146 | contents: streamtest.v2.fromChunks(['plop', 'plup']), 147 | }); 148 | 149 | inputStream 150 | .pipe(gStreamify(pluginStream)) 151 | .pipe(streamtest[version].toObjects(function(err, files) { 152 | if(err) { 153 | return done(err); 154 | } 155 | assert.equal(files.length, 2); 156 | assert.equal(files[0].cwd, '/home/nfroidure/'); 157 | assert.equal(files[0].base, '/home/nfroidure/test'); 158 | assert.equal(files[0].path, '/home/nfroidure/test/file.js'); 159 | assert.equal(files[1].cwd, '/home/nfroidure/'); 160 | assert.equal(files[1].base, '/home/nfroidure/test'); 161 | assert.equal(files[1].path, '/home/nfroidure/test/file2.js'); 162 | files[0].pipe(streamtest[version].toText(function(err2, text) { 163 | if(err2) { 164 | return done(err2); 165 | } 166 | assert.equal(text, 'plipplaptest'); 167 | files[1].pipe(streamtest[version].toText(function(err3, text2) { 168 | if(err3) { 169 | return done(err3); 170 | } 171 | assert.equal(text2, 'ploppluptest'); 172 | done(); 173 | })); 174 | })); 175 | })); 176 | 177 | inputStream.write(fakeFile); 178 | inputStream.write(fakeFile2); 179 | inputStream.end(); 180 | 181 | }); 182 | 183 | it('should work with async streams and async contents', function(done) { 184 | 185 | var pluginStream = pluginFunction(); 186 | 187 | streamtest[version].fromObjects([ 188 | new gutil.File({ 189 | cwd: '/home/nfroidure/', 190 | base: '/home/nfroidure/test', 191 | path: '/home/nfroidure/test/file.js', 192 | contents: streamtest.v2.fromChunks(['plip', 'plap']), 193 | }), 194 | new gutil.File({ 195 | cwd: '/home/nfroidure/', 196 | base: '/home/nfroidure/test', 197 | path: '/home/nfroidure/test/file2.js', 198 | contents: streamtest.v2.fromChunks(['plip', 'plup']), 199 | }), 200 | ]) 201 | .pipe(gStreamify(pluginStream)) 202 | .pipe(streamtest[version].toObjects(function(err, files) { 203 | if(err) { 204 | return done(err); 205 | } 206 | assert.equal(files.length, 2); 207 | assert.equal(files[0].cwd, '/home/nfroidure/'); 208 | assert.equal(files[0].base, '/home/nfroidure/test'); 209 | assert.equal(files[0].path, '/home/nfroidure/test/file.js'); 210 | assert.equal(files[1].cwd, '/home/nfroidure/'); 211 | assert.equal(files[1].base, '/home/nfroidure/test'); 212 | assert.equal(files[1].path, '/home/nfroidure/test/file2.js'); 213 | files[0].pipe(streamtest[version].toText(function(err2, text) { 214 | if(err2) { 215 | return done(err2); 216 | } 217 | assert.equal(text, 'plipplaptest'); 218 | files[1].pipe(streamtest[version].toText(function(err3, text2) { 219 | if(err3) { 220 | return done(err3); 221 | } 222 | assert.equal(text2, 'plippluptest'); 223 | done(); 224 | })); 225 | })); 226 | })); 227 | 228 | }); 229 | 230 | it('should work with plugin function provinding async files streams', function(done) { 231 | 232 | streamtest[version].fromObjects([ 233 | new gutil.File({ 234 | cwd: '/home/nfroidure/', 235 | base: '/home/nfroidure/test', 236 | path: '/home/nfroidure/test/file.js', 237 | contents: streamtest.v2.fromChunks(['plip', 'plap']), 238 | }), 239 | new gutil.File({ 240 | cwd: '/home/nfroidure/', 241 | base: '/home/nfroidure/test', 242 | path: '/home/nfroidure/test/file2.js', 243 | contents: streamtest.v2.fromChunks(['plip', 'plup']), 244 | }), 245 | ]) 246 | .pipe(gStreamify(pluginFunction)) 247 | .pipe(streamtest[version].toObjects(function(err, files) { 248 | if(err) { 249 | return done(err); 250 | } 251 | assert.equal(files.length, 2); 252 | assert.equal(files[0].cwd, '/home/nfroidure/'); 253 | assert.equal(files[0].base, '/home/nfroidure/test'); 254 | assert.equal(files[0].path, '/home/nfroidure/test/file.js'); 255 | assert.equal(files[1].cwd, '/home/nfroidure/'); 256 | assert.equal(files[1].base, '/home/nfroidure/test'); 257 | assert.equal(files[1].path, '/home/nfroidure/test/file2.js'); 258 | files[0].pipe(streamtest[version].toText(function(err2, text) { 259 | if(err2) { 260 | return done(err2); 261 | } 262 | assert.equal(text, 'plipplaptest'); 263 | files[1].pipe(streamtest[version].toText(function(err3, text2) { 264 | if(err3) { 265 | return done(err3); 266 | } 267 | assert.equal(text2, 'plippluptest'); 268 | done(); 269 | })); 270 | })); 271 | })); 272 | 273 | }); 274 | 275 | }); 276 | 277 | describe('in buffer mode', function() { 278 | 279 | it('should work', function(done) { 280 | 281 | var pluginStream = pluginFunction(); 282 | 283 | streamtest[version].fromObjects([ 284 | new gutil.File({ 285 | cwd: '/home/nfroidure/', 286 | base: '/home/nfroidure/test', 287 | path: '/home/nfroidure/test/file.js', 288 | contents: new Buffer('plipplap'), 289 | }), 290 | new gutil.File({ 291 | cwd: '/home/nfroidure/', 292 | base: '/home/nfroidure/test', 293 | path: '/home/nfroidure/test/file2.js', 294 | contents: new Buffer('plipplup'), 295 | }), 296 | ]) 297 | .pipe(gStreamify(pluginStream)) 298 | .pipe(streamtest[version].toObjects(function(err, files) { 299 | if(err) { 300 | return done(err); 301 | } 302 | assert.equal(files.length, 2); 303 | assert.equal(files[0].cwd, '/home/nfroidure/'); 304 | assert.equal(files[0].base, '/home/nfroidure/test'); 305 | assert(files[0].contents instanceof Buffer); 306 | assert.equal(files[0].path, '/home/nfroidure/test/file.js'); 307 | assert.equal(files[0].contents.toString(), 'plipplaptest'); 308 | assert.equal(files[1].cwd, '/home/nfroidure/'); 309 | assert.equal(files[1].base, '/home/nfroidure/test'); 310 | assert(files[1].contents instanceof Buffer); 311 | assert.equal(files[1].path, '/home/nfroidure/test/file2.js'); 312 | assert.equal(files[1].contents.toString(), 'plippluptest'); 313 | done(); 314 | })); 315 | 316 | }); 317 | 318 | }); 319 | 320 | }); 321 | 322 | }); 323 | 324 | }); 325 | --------------------------------------------------------------------------------