├── .gitignore ├── index.js ├── package.json ├── readme.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var PluginError = require('plugin-error'); 3 | var through = require('through2'); 4 | var Stream = require('stream'); 5 | var utils = require('util'); 6 | var Readable = Stream.Readable; 7 | var Duplex = Stream.Duplex; 8 | var Transform = Stream.Transform; 9 | 10 | module.exports = function (func) { 11 | 12 | if (!func || typeof func != 'function') { 13 | throw new PluginError('gulp-flatMap', '`flatMap` must be called with one parameter, a function'); 14 | } 15 | 16 | var openStreams = []; 17 | var ended = false; 18 | var error = false; 19 | 20 | 21 | function closeStreamIfNoMoreOpenStreams(stream){ 22 | if(openStreams.length == 0){ 23 | if(ended && !error){ 24 | stream.push(null); 25 | } 26 | }else{ 27 | 28 | } 29 | } 30 | 31 | return through.obj(function(data, enc, done){ 32 | 33 | if (data.isStream()) { 34 | this.emit('error', new PluginError('gulp-flatMap', 'Streaming not supported')); 35 | return; 36 | } 37 | 38 | var self = this; 39 | var notYetRead = true; 40 | var readStream = new Readable({objectMode: true}); 41 | 42 | readStream._read = function(){ 43 | if(notYetRead){ 44 | notYetRead = false; 45 | readStream.push(data); 46 | }else{ 47 | readStream.push(null); 48 | } 49 | }; 50 | 51 | var resultStream = func(readStream, data); 52 | 53 | if(resultStream 54 | && typeof resultStream === 'object' 55 | && 'on' in resultStream 56 | && typeof resultStream.on === 'function'){ 57 | openStreams.push(resultStream); 58 | 59 | resultStream.on('end', function(){ 60 | openStreams.splice(openStreams.indexOf(resultStream), 1); 61 | closeStreamIfNoMoreOpenStreams(self); 62 | done(); 63 | }); 64 | 65 | resultStream.on('data', function(result){ 66 | self.push(result); 67 | }); 68 | 69 | resultStream.on('error', function(error){ 70 | console.error("error!"); 71 | done(error); 72 | }); 73 | }else{ 74 | this.emit('error', new PluginError('gulp-flatMap', 'The function must return a stream')); 75 | return; 76 | } 77 | }, function(){ 78 | ended = true; 79 | closeStreamIfNoMoreOpenStreams(this); 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-flatmap", 3 | "version": "1.0.2", 4 | "description": "map each file in a stream into multiple files that are piped out", 5 | "license": "MIT", 6 | "repository": "mariusGundersen/gulp-flatmap", 7 | "author": { 8 | "name": "Marius Gundersen", 9 | "email": "me@mariusgundersen.net", 10 | "url": "https://github.com/mariusGundersen" 11 | }, 12 | "engines": { 13 | "node": ">=0.10.0" 14 | }, 15 | "scripts": { 16 | "test": "mocha" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "gulpplugin", 23 | "flatmap", 24 | "split", 25 | "fork", 26 | "divide", 27 | "separate", 28 | "map", 29 | "gulp" 30 | ], 31 | "dependencies": { 32 | "plugin-error": "0.1.2", 33 | "through2": "2.0.3" 34 | }, 35 | "devDependencies": { 36 | "mocha": "*", 37 | "vinyl": "2.1.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # [gulp](http://gulpjs.com)-flatmap 2 | 3 | > map each file in a stream into multiple files that are piped out 4 | 5 | ## Install 6 | 7 | ```bash 8 | $ npm install --save-dev gulp-flatmap 9 | ``` 10 | 11 | 12 | ## Usage 13 | 14 | ```js 15 | var gulp = require('gulp'); 16 | var flatmap = require('gulp-flatmap'); 17 | var uglify = require('gulp-uglify'); 18 | var path = require('path'); 19 | var concat = require('gulp-concat'); 20 | 21 | gulp.task('default', function () { 22 | return gulp.src('*.json') 23 | .pipe(flatmap(function(stream, file){ 24 | var contents = JSON.parse(file.contents.toString('utf8')); 25 | //contents.files is an array 26 | return gulp.src(contents.files) 27 | //uglify each file individually 28 | .pipe(uglify()) 29 | //combine the files 30 | .pipe(concat(path.basename(file.path))); 31 | })) 32 | .pipe(gulp.dest('dist')); 33 | }); 34 | ``` 35 | 36 | 37 | ## API 38 | 39 | The flatmap method takes one argument, a function. This function is called once for each file piped to `flatmap` and is passed a stream as its first argument and the [vinyl file](https://github.com/wearefractal/vinyl) as its second argument. The stream contains only one file. 40 | 41 | You can now pipe this stream through as many steps as you want, before returning it from the function. All the streams returned from `flatmap` will be combined and their contents will be emited by `flatmap`. 42 | 43 | ## License 44 | 45 | MIT © [Marius Gundersen](https://github.com/mariusGundersen) 46 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var assert = require('assert'); 3 | var Vinyl = require('vinyl'); 4 | var flatMap = require('./index'); 5 | var through = require('through2'); 6 | 7 | it('should call the function once per file', function (cb) { 8 | 9 | var count = 0; 10 | 11 | var stream = flatMap(function(stream){ 12 | count++; 13 | return stream; 14 | }); 15 | 16 | stream.on('data', function(){ 17 | 18 | }); 19 | 20 | stream.on('end', function(){ 21 | assert.equal(count, 2); 22 | cb(); 23 | }); 24 | 25 | stream.write(new Vinyl({ 26 | base: __dirname, 27 | path: 'file.ext', 28 | contents: new Buffer('unicorns') 29 | })); 30 | 31 | stream.write(new Vinyl({ 32 | base: __dirname, 33 | path: 'file.ext', 34 | contents: new Buffer('unicorns') 35 | })); 36 | 37 | stream.end(); 38 | }); 39 | 40 | it('should require the function returns a stream', function (cb) { 41 | 42 | var stream = flatMap(function(stream){ 43 | }); 44 | 45 | stream.on('data', function (file) { 46 | }); 47 | 48 | stream.on('error', function(){ 49 | cb(); 50 | }); 51 | 52 | stream.on('end', function(){ 53 | cb.fail(); 54 | }); 55 | 56 | stream.write(new Vinyl({ 57 | base: __dirname, 58 | path: 'file.ext', 59 | contents: new Buffer('unicorns') 60 | })); 61 | 62 | stream.end(); 63 | }); 64 | 65 | it('should support multiple outputs', function (cb) { 66 | 67 | var count = 0; 68 | 69 | var stream = flatMap(function(stream){ 70 | return stream.pipe(through.obj(function(file, enc, done){ 71 | this.push(file); 72 | this.push(file); 73 | done(); 74 | })); 75 | }); 76 | 77 | stream.on('data', function (file) { 78 | count++; 79 | assert.equal(file.relative, 'file.ext'); 80 | assert.equal(file.contents.toString(), 'unicorns'); 81 | }); 82 | 83 | stream.on('end', function(){ 84 | assert.equal(count, 4); 85 | cb(); 86 | }); 87 | 88 | stream.write(new Vinyl({ 89 | base: __dirname, 90 | path: 'file.ext', 91 | contents: new Buffer('unicorns') 92 | })); 93 | 94 | stream.write(new Vinyl({ 95 | base: __dirname, 96 | path: 'file.ext', 97 | contents: new Buffer('unicorns') 98 | })); 99 | 100 | stream.end(); 101 | }); 102 | 103 | 104 | it('should throw an error if not called with a function', function(){ 105 | 106 | assert.throws(flatMap); 107 | assert.throws(flatMap.bind(null, {})); 108 | 109 | }); 110 | 111 | it('should pass the file as the second argument to the function', function(cb){ 112 | 113 | var stream = flatMap(function(stream, file){ 114 | return stream.pipe(through.obj(function(data, enc, done){ 115 | assert.equal(data, file); 116 | done(); 117 | })); 118 | }); 119 | 120 | stream.on('data', function(){ 121 | 122 | }); 123 | 124 | stream.on('end', function(){ 125 | cb(); 126 | }); 127 | 128 | stream.write(new Vinyl({ 129 | base: __dirname, 130 | path: 'file.ext', 131 | contents: new Buffer('unicorns') 132 | })); 133 | 134 | stream.end(); 135 | }); 136 | 137 | it('should handle many files', function(cb){ 138 | var stream = flatMap(function(stream, file){ 139 | return stream; 140 | }); 141 | 142 | var count = 0; 143 | 144 | stream.on('data', function(file){ 145 | count++; 146 | }); 147 | 148 | stream.on('end', function(){ 149 | assert.equal(32, count); 150 | cb(); 151 | }); 152 | 153 | for(var i = 0; i < 32; i++){ 154 | stream.write(new Vinyl({ 155 | base: __dirname, 156 | path: 'file'+i+'.ext', 157 | contents: new Buffer(i+'unicorns') 158 | })); 159 | } 160 | 161 | stream.end(); 162 | }); --------------------------------------------------------------------------------