Files
scriptc/tests/corpus/6561-constructor-function-writable.cjs
T
2026-10-20 17:56:44 -05:00

142 lines
4.8 KiB
JavaScript

// Writable and Duplex streams built by constructor functions, the bl and
// tar-stream patterns: Writable.call(this, opts), Duplex.call(this),
// _write/_final/_read on the prototype, listeners added before the base
// constructor runs, and an end() override that calls the base's end.
const stream = require("stream");
const util = require("util");
function Sink(opts) {
if (!(this instanceof Sink)) return new Sink(opts);
stream.Writable.call(this, opts);
this.chunks = [];
this.finalized = false;
}
util.inherits(Sink, stream.Writable);
Sink.prototype._write = function (chunk, enc, cb) {
console.log("_write", this instanceof Sink, enc, Buffer.isBuffer(chunk), chunk.toString());
this.chunks.push(chunk);
if (this.chunks.length === 2) setImmediate(cb);
else cb();
};
Sink.prototype._final = function (cb) {
console.log("_final", this.chunks.length);
this.finalized = true;
cb();
};
const sink = new Sink({ highWaterMark: 8 });
console.log("writable", sink.writable, sink.writableHighWaterMark, sink._writableState.highWaterMark);
console.log("write a", sink.write("first"));
console.log("write b", sink.write(Buffer.from("second"), () => console.log("write b callback")));
console.log("needDrain", sink.writableNeedDrain, sink.writableLength);
sink.on("drain", () => console.log("drain", sink.writableLength));
sink.on("finish", function () {
console.log("finish", this === sink, sink.finalized, sink.writableFinished, Buffer.concat(sink.chunks).toString());
});
sink.on("close", () => {
console.log("close", sink.destroyed, sink.closed);
duplex();
});
sink.end("third", (err) => console.log("end callback", err, sink.writableEnded, sink._writableState.ended));
console.log("after end", sink.writableEnded, sink.writable);
// A bl-style list: a Duplex that collects writes and reads them back.
function BufferListStream(callback) {
if (!(this instanceof BufferListStream)) return new BufferListStream(callback);
if (typeof callback === "function") {
this._callback = callback;
const piper = function piper(err) {
if (this._callback) {
this._callback(err);
this._callback = null;
}
}.bind(this);
this.on("pipe", function onPipe(src) {
console.log("pipe event", src instanceof stream.Readable);
src.on("error", piper);
});
this.on("unpipe", function onUnpipe(src) {
console.log("unpipe event");
src.removeListener("error", piper);
});
callback = null;
}
this._bufs = [];
this.length = 0;
stream.Duplex.call(this);
}
util.inherits(BufferListStream, stream.Duplex);
BufferListStream.prototype._write = function (buf, encoding, callback) {
this._bufs.push(buf);
this.length += buf.length;
callback();
};
BufferListStream.prototype._read = function (size) {
if (!this.length) return this.push(null);
const all = Buffer.concat(this._bufs);
size = Math.min(size, this.length, 4);
this.push(all.subarray(0, size));
this._bufs = [all.subarray(size)];
this.length -= size;
};
BufferListStream.prototype.end = function end(chunk) {
stream.Duplex.prototype.end.call(this, chunk);
if (this._callback) {
this._callback(null, Buffer.concat(this._bufs));
this._callback = null;
}
};
BufferListStream.prototype.slice = function () {
return Buffer.concat(this._bufs);
};
function Letters(text) {
stream.Readable.call(this, { highWaterMark: 2 });
this._text = text;
}
util.inherits(Letters, stream.Readable);
Letters.prototype._read = function () {
const next = this._text.slice(0, 3);
this._text = this._text.slice(3);
this.push(next.length ? next : null);
};
function duplex() {
const list = new BufferListStream((err, data) => {
console.log("bl callback", err, data.toString(), data.length);
});
console.log("duplex", list.readable, list.writable, list.allowHalfOpen, list instanceof stream.Duplex);
console.log("pre-constructor listeners", list.listenerCount("pipe"), list.listenerCount("unpipe"));
list.write("hello ");
list.write("constructor ");
list.end("functions");
list.on("finish", () => {
const out = [];
list.on("data", (d) => out.push(d.toString()));
list.on("end", () => {
console.log("read back", JSON.stringify(out));
piping();
});
});
}
function piping() {
const src = new Letters("abcdefghij");
const dst = new Sink({ highWaterMark: 1 });
dst.on("pipe", (from) => console.log("dst pipe", from === src));
const returned = src.pipe(dst);
console.log("pipe returns dest", returned === dst);
dst.on("finish", () => {
console.log("piped", Buffer.concat(dst.chunks).toString(), src.readableEnded);
const keep = new Sink();
const from = new Letters("xyz");
from.pipe(keep, { end: false });
from.on("end", () => {
setImmediate(() => {
console.log("end:false keeps the destination open", keep.writable, keep.writableEnded);
keep.end();
});
});
});
}