Skip to content

Commit

Permalink
Merge branch 'async'
Browse files Browse the repository at this point in the history
Merges #2
Merges #3
  • Loading branch information
mcmath committed Oct 19, 2016
2 parents 557e9a0 + d5dbb22 commit 2d592ba
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/coverage/
/lib/
/node_modules/
/npm-debug.log
/npm-debug.*
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/coverage/
/src/
/test/
/.babelrc
/.editorconfig
/.eslintrc.yml
Expand Down
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@
[Gulp][Gulp link] plugin for applying arbitrary transformations to
the contents of files.

* **Simple**. Just pass a callback function that takes the current file
contents and returns the desired contents.
* **Flexible**. Receive file contents as a Buffer or a string. Compatible with
pipelines in both buffer mode and streaming mode.
* **Economical**. Reduce the need for gulp-specific plugins by pairing
gulp-transform with ordinary node packages and functions.

## Install

Install via [npm][NPM link]:
Expand Down Expand Up @@ -89,9 +82,10 @@ gulp.task('cheerio', function() {

##### transformFn `function`

The callback responsible for the transformation. The return value must be a
string or a Buffer, which will replace the file's contents. The callback
is invoked once per file with the following arguments:
The callback responsible for the transformation, whose return value will replace
the file's contents. The return value may be a string, a Buffer, or a Promise
resolvable to a string or Buffer. The callback is invoked once per file with the
following arguments:

* **contents** `Buffer` | `string` <br>
The initial contents of the file. Contents are passed as a Buffer unless the
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"gulp": "3.x"
},
"dependencies": {
"es6-promise": "^4.0.5",
"gulp-util": "^3.0.7",
"lodash": "^4.13.1"
},
Expand All @@ -49,10 +50,10 @@
"chai": "^3.5.0",
"coffee-script": "^1.10.0",
"coveralls": "^2.11.9",
"eslint": "^2.11.1",
"eslint": "^2.13.1",
"event-stream": "^3.3.2",
"istanbul": "1.0.0-alpha.2",
"mocha": "^2.5.3",
"istanbul": "1.1.0-alpha.1",
"mocha": "^3.1.2",
"rimraf": "^2.5.2",
"sinon": "^1.17.4",
"sinon-chai": "^2.8.0"
Expand Down
8 changes: 6 additions & 2 deletions src/file-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ export class FileStream extends Transform {

_flush(done) {
let contents = Buffer.concat(this.data);
this.push(transform(this.fn, contents, this.file, this.opts));

done();
transform(this.fn, contents, this.file, this.opts).then((result) => {
this.push(result);
done();
}).catch((err) => {
done(err);
});
}

}
3 changes: 0 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ import {err} from './err';
export default function gulpTransform(transformFn, options) {
if (isNil(transformFn)) {
err('transformFn must be defined');

} else if (!isFunction(transformFn)) {
err('transformFn must be a function');

} else if (!isNil(options) && !isObject(options)) {
err('options must be an object');

} else {
return new PluginStream(transformFn, options);
}
Expand Down
17 changes: 10 additions & 7 deletions src/plugin-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ export class PluginStream extends Transform {
let {fn, opts} = this;

if (file.isBuffer()) {
file.contents = transform(fn, file.contents, file, opts);
}

if (file.isStream()) {
transform(fn, file.contents, file, opts).then((result) => {
file.contents = result;
next(null, file);
}).catch((err) => {
next(err);
});
} else if (file.isStream()) {
file.contents = file.contents.pipe(new FileStream(fn, file, opts));
next(null, file);
} else {
next(null, file);
}

next(null, file);
}

}
23 changes: 16 additions & 7 deletions src/transform.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import {isBuffer, isString} from 'lodash';
import {Promise} from 'es6-promise';
import {isBuffer} from 'lodash';
import {err} from './err';

export function transform(fn, contents, file, opts) {
let encoded = opts.encoding ? contents.toString(opts.encoding) : contents;
let transformed = fn.call(opts.thisArg, encoded, file);
export function transform(fn, contents, file, {encoding, thisArg}) {
let decoded = encoding ? contents.toString(encoding) : contents;
let transformed = fn.call(thisArg, decoded, file);

return isBuffer(transformed) ? transformed :
isString(transformed) ? new Buffer(transformed) :
err('transformFn must return a string or a Buffer');
return Promise.resolve(transformed).then(toBuffer);
}

function toBuffer(contents) {
if (isBuffer(contents)) {
return contents;
} else if (contents != null) {
return new Buffer(String(contents));
} else {
err('transformFn may not return or resolve to null or undefined');
}
}
11 changes: 9 additions & 2 deletions test/fixtures/fn.coffee
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{Promise} = require 'es6-promise';
{spy} = require 'sinon'

re = /one|two|three/g
Expand All @@ -7,11 +8,17 @@ dict =
two: 'deux'
three: 'trois'

translate = (content) ->
content.replace(re, (match) -> dict[match])

exports.stringFn = ->
spy (content) ->
content.replace re, (match) ->
dict[match]
translate(content)

exports.bufferFn = ->
spy (content) ->
Buffer.concat([content, content])

exports.asyncFn = ->
spy (content) ->
return Promise.resolve(translate(content))
128 changes: 96 additions & 32 deletions test/index.coffee
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
chai = require 'chai'
sinonChai = require 'sinon-chai'
{match: {any, instanceOf}} = require 'sinon'
{File: {isVinyl}} = require 'gulp-util'
{File: {isVinyl}, PluginError} = require 'gulp-util'
{wait} = require 'event-stream'
{buffer, string} = require './fixtures/content'
{buffered, streaming} = require './fixtures/file'
{bufferFn, stringFn} = require './fixtures/fn'
{bufferFn, stringFn, asyncFn} = require './fixtures/fn'
err = require './helpers/err'
transform = require '../src'

Expand All @@ -30,10 +30,14 @@ describe 'plugin: gulp-transform', ->
it 'throws PluginError', ->
err -> transform 42

context 'returns neither a string nor a Buffer', ->
context 'returns null or undefined', ->

it 'throws PluginError', ->
err -> transform((content) -> 42).write buffered()
it 'emits PluginError', (done) ->
stream = transform((content) -> null)
stream.write buffered()
stream.on 'error', (err) ->
err.should.be.instanceOf PluginError
done()

context 'returns a Buffer or string', ->
[fn, file] = [null, null]
Expand All @@ -52,6 +56,23 @@ describe 'plugin: gulp-transform', ->
it 'is called with vinyl File as second argument', ->
fn.should.have.been.calledWith any, file

context 'returns a Promise that resolves to a string or Buffer', ->
[fn, file] = [null, null]

beforeEach ->
file = buffered()
fn = asyncFn()
transform(fn, {encoding: 'utf8'}).write(file)

it 'is called once per file', ->
fn.should.have.been.calledOnce

it 'is called with contents as first argument', ->
fn.should.have.been.calledWith string

it 'is called with vinyl File as second argument', ->
fn.should.have.been.calledWith any, file

describe 'param: options', ->

context 'not an object', ->
Expand Down Expand Up @@ -80,40 +101,83 @@ describe 'plugin: gulp-transform', ->
fn.should.have.been.calledOn undefined

describe 'mode: buffer', ->
file = null

beforeEach (done) ->
transform(bufferFn()).once('data', (_file) ->
file = _file
done()
).write buffered()
context 'synchronous', ->
file = null

beforeEach (done) ->
transform(bufferFn()).once('data', (_file) ->
file = _file
done()
).write buffered()

it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true

it 'files are in buffer mode', ->
file.isBuffer().should.be.true;

it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true
it 'transforms file contents', ->
file.contents.should.deep.equal Buffer.concat([buffer, buffer])

it 'files are in buffer mode', ->
file.isBuffer().should.be.true;
context 'async', ->
file = null

it 'transforms file contents', ->
file.contents.should.deep.equal Buffer.concat([buffer, buffer])
beforeEach (done) ->
transform(asyncFn(), {encoding: 'utf8'}).once('data', (_file) ->
file = _file
done()
).write buffered()

it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true

it 'files are in buffer mode', ->
file.isBuffer().should.be.true;

it 'transforms file contents', ->
file.contents.should.deep.equal new Buffer('un deux trois')

describe 'mode: streaming', ->
file = null

beforeEach (done) ->
transform(stringFn(), {encoding: 'utf8'}).once('data', (_file) ->
file = _file
done()
).write streaming()
context 'synchronous', ->
file = null

it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true
beforeEach (done) ->
transform(stringFn(), {encoding: 'utf8'}).once('data', (_file) ->
file = _file
done()
).write streaming()

it 'files are in streaming mode', ->
file.isStream().should.be.true
it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true

it 'transforms file contents', (done) ->
file.pipe(wait((err, data) ->
data.should.deep.equal new Buffer('un deux trois')
done()
))
it 'files are in streaming mode', ->
file.isStream().should.be.true

it 'transforms file contents', (done) ->
file.pipe(wait((err, data) ->
data.should.deep.equal new Buffer('un deux trois')
done()
))

context 'async', ->
file = null

beforeEach (done) ->
transform(asyncFn(), {encoding: 'utf8'}).once('data', (_file) ->
file = _file
done()
).write streaming()

it 'returns a stream of vinyl Files', ->
isVinyl(file).should.be.true

it 'files are in streaming mode', ->
file.isStream().should.be.true

it 'transforms file contents', (done) ->
file.pipe(wait((err, data) ->
data.should.deep.equal new Buffer('un deux trois')
done()
))

0 comments on commit 2d592ba

Please sign in to comment.