Hounshell Hounshell - 1 month ago 8
Node.js Question

How do I wrap a piped stream to make it look like a single stream?

I have a work flow, something like this:

gulp.src('**/*.js')
.pipe(doSomething())
.pipe(dest('out'));


I want
doSomething
to return a stream pipeline, say something like
uglify().pipe(header(...)).pipe(rename(...))
so that the effective result of this is

gulp.src('**/*.js')
.pipe(uglify())
.pipe(header(...))
.pipe(rename(...))
.pipe(dest('out'));


Obviously
doSomething
can't return uglify (because dest would read from that), and it can't return rename (because src would write to that). So my plan was to build a Duplex stream that acted like a wrapper around this pipeline fragment, resulting in this:

first = uglify();
last = first
.pipe(header(...))
.pipe(rename(...));

gulp.src('**/*.js')
.pipe(wrapper(first, last) /* writes go to first, reads go to last */)
.pipe(dest('out'));


I've taken a stab at writing a wrapper, but I don't have enough Node Stream experience to know that I'm going down the right path at all, and why it's not working. Here's what I've got so far:

var encapsulateStream = function(first, last, logger) {
logger('encapsulate');
let resultStream = new Duplex({
readableObjectMode: true,
writableObjectMode: true,
read(size) {
logger('read');
while (true) {
let chunk = last.read(size);
if (logger) logger('[in]', '--->', chunk);
if (!this.push(chunk) || chunk === null) {
return;
}
}
},
write(chunk, enc, next) {
logger('write');
if (logger) logger('--->', '[first]', chunk);
first.write(chunk, enc, next);
}});

resultStream.on('finish', function() {
logger('finish');
first.end();
forwardStream(last, resultStream, logger);
});

return resultStream;
};


I get "encapsulate" in my logs, but nothing else. Where am I going wrong, and is this the right approach?

Answer

There's no need to implement something like this yourself. The lazypipe library is widely used and does pretty much exactly what you want.

Here's a quick example that illustrates its usage for your use case:

var lazypipe = require('lazypipe');

var doSomething = lazypipe()
  .pipe(uglify)
  .pipe(header, ...)
  .pipe(rename, ...);

gulp.src('**/*.js')
  .pipe(doSomething())
  .pipe(gulp.dest('out'));