forked from enviPath/enviPy
125 lines
2.9 KiB
JavaScript
125 lines
2.9 KiB
JavaScript
'use strict';
|
|
|
|
var isStream = require('isstream');
|
|
var Stream = require('readable-stream');
|
|
var util = require('util');
|
|
|
|
// Inherit of Duplex stream
|
|
util.inherits(Duplexer, Stream.Duplex);
|
|
|
|
// Constructor
|
|
function Duplexer(options, writableStream, readableStream) {
|
|
var _this = this;
|
|
|
|
// Ensure new were used
|
|
if (!(this instanceof Duplexer)) {
|
|
return new (Duplexer.bind.apply(Duplexer,
|
|
[Duplexer].concat([].slice.call(arguments, 0))));
|
|
}
|
|
|
|
// Mapping args
|
|
if(isStream(options)) {
|
|
readableStream = writableStream;
|
|
writableStream = options;
|
|
options = {};
|
|
} else {
|
|
options = options || {};
|
|
}
|
|
this._reemitErrors = 'boolean' === typeof options.reemitErrors ?
|
|
options.reemitErrors :
|
|
true;
|
|
delete options.reemitErrors;
|
|
|
|
// Checking arguments
|
|
if(!isStream(writableStream, 'Writable', 'Duplex')) {
|
|
throw new Error('The writable stream must be an instanceof Writable or Duplex.');
|
|
}
|
|
if(!isStream(readableStream, 'Readable')) {
|
|
throw new Error('The readable stream must be an instanceof Readable.');
|
|
}
|
|
|
|
// Parent constructor
|
|
Stream.Duplex.call(this, options);
|
|
|
|
// Save streams refs
|
|
this._writable = writableStream;
|
|
this._readable = readableStream;
|
|
|
|
// Internal state
|
|
this._waitDatas = false;
|
|
this._hasDatas = false;
|
|
|
|
if('undefined' == typeof this._readable._readableState) {
|
|
this._readable = (new Stream.Readable({
|
|
objectMode: options.objectMode || false,
|
|
})).wrap(this._readable);
|
|
}
|
|
|
|
if(this._reemitErrors) {
|
|
this._writable.on('error', function(err) {
|
|
_this.emit('error', err);
|
|
});
|
|
this._readable.on('error', function(err) {
|
|
_this.emit('error', err);
|
|
});
|
|
}
|
|
|
|
this._writable.on('drain', function() {
|
|
_this.emit('drain');
|
|
});
|
|
|
|
this.once('finish', function() {
|
|
_this._writable.end();
|
|
});
|
|
|
|
this._writable.once('finish', function() {
|
|
_this.end();
|
|
});
|
|
|
|
this._readable.on('readable', function() {
|
|
_this._hasDatas = true;
|
|
if(_this._waitDatas) {
|
|
_this._pushAll();
|
|
}
|
|
});
|
|
|
|
this._readable.once('end', function() {
|
|
_this.push(null);
|
|
});
|
|
}
|
|
|
|
Duplexer.prototype._read = function() {
|
|
this._waitDatas = true;
|
|
if(this._hasDatas) {
|
|
this._pushAll();
|
|
}
|
|
};
|
|
|
|
Duplexer.prototype._pushAll = function() {
|
|
var _this = this;
|
|
var chunk;
|
|
|
|
do {
|
|
chunk = _this._readable.read();
|
|
if(null !== chunk) {
|
|
this._waitDatas = _this.push(chunk);
|
|
}
|
|
this._hasDatas = (null !== chunk);
|
|
} while(this._waitDatas && this._hasDatas);
|
|
};
|
|
|
|
Duplexer.prototype._write = function(chunk, encoding, callback) {
|
|
return this._writable.write(chunk, encoding, callback);
|
|
};
|
|
|
|
Duplexer.obj = function plexerObj(options) {
|
|
var firstArgumentIsAStream = isStream(options);
|
|
var streams = [].slice.call(arguments, firstArgumentIsAStream ? 0 : 1);
|
|
|
|
options = firstArgumentIsAStream ? {} : options;
|
|
options.objectMode = true;
|
|
return Duplexer.apply({}.undef, [options].concat(streams));
|
|
};
|
|
|
|
module.exports = Duplexer;
|