diff options
Diffstat (limited to 'src/bun.js/process-stdio-polyfill.js')
-rw-r--r-- | src/bun.js/process-stdio-polyfill.js | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/bun.js/process-stdio-polyfill.js b/src/bun.js/process-stdio-polyfill.js new file mode 100644 index 000000000..b19a8076f --- /dev/null +++ b/src/bun.js/process-stdio-polyfill.js @@ -0,0 +1,342 @@ +var createReadStream; +var createWriteStream; + +var StdioWriteStream; +var StdinStream; + +var AbortError = class extends Error { + constructor(message = "The operation was aborted", options = void 0) { + if (options !== void 0 && typeof options !== "object") { + throw new Error( + `Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`, + ); + } + super(message, options); + this.code = "ABORT_ERR"; + this.name = "AbortError"; + } +}; + +function lazyLoadDeps({ require }) { + var { + createWriteStream: _createWriteStream, + createReadStream: _createReadStream, + } = require("node:fs", "node:process"); + createWriteStream = _createWriteStream; + createReadStream = _createReadStream; +} + +function getStdioWriteStream({ require }) { + if (!StdioWriteStream) { + var { Duplex, eos, destroy } = require("node:stream", "node:process"); + if (!createWriteStream) { + lazyLoadDeps({ require }); + } + + StdioWriteStream = class StdioWriteStream extends Duplex { + #writeStream; + #readStream; + + #readable = true; + #writable = true; + #fdPath; + + #onClose; + #onDrain; + #onFinish; + #onReadable; + + fd = 1; + get isTTY() { + return require("tty").isatty(this.fd); + } + + constructor(fd) { + super({ readable: true, writable: true }); + this.#fdPath = `/dev/fd/${fd}`; + + Object.defineProperty(this, "fd", { + value: fd, + writable: false, + configurable: false, + }); + } + + #onFinished(err) { + const cb = this.#onClose; + this.#onClose = null; + + if (cb) { + cb(err); + } else if (err) { + this.destroy(err); + } else if (!this.#readable && !this.#writable) { + this.destroy(); + } + } + + _destroy(err, callback) { + if (!err && this.#onClose !== null) { + err = new AbortError(); + } + this.#onDrain = null; + this.#onFinish = null; + if (this.#onClose === null) { + callback(err); + } else { + this.#onClose = callback; + if (this.#writeStream) destroy(this.#writeStream, err); + if (this.#readStream) destroy(this.#readStream, err); + } + } + + _write(chunk, encoding, callback) { + if (!this.#writeStream) { + this.#writeStream = createWriteStream(this.#fdPath); + + this.#writeStream.on("finish", () => { + if (this.#onFinish) { + const cb = this.#onFinish; + this.#onFinish = null; + cb(); + } + }); + + this.#writeStream.on("drain", () => { + if (this.#onDrain) { + const cb = this.#onDrain; + this.#onDrain = null; + cb(); + } + }); + + eos(this.#writeStream, (err) => { + this.#writable = false; + if (err) { + destroy(this.#writeStream, err); + } + this.#onFinished(err); + }); + } + if (this.#writeStream.write(chunk, encoding)) { + callback(); + } else { + this.#onDrain = callback; + } + } + + _final(callback) { + this.#writeStream.end(); + this.#onFinish = callback; + } + + _read() { + if (!this.#readStream) { + this.#readStream = createReadStream(this.#fdPath); + + this.#readStream.on("readable", () => { + if (this.#onReadable) { + const cb = this.#onReadable; + this.#onReadable = null; + cb(); + } else { + this.read(); + } + }); + + this.#readStream.on("end", () => { + this.push(null); + }); + + eos(this.#readStream, (err) => { + this.#readable = false; + if (err) { + destroy(this.#readStream, err); + } + this.#onFinished(err); + }); + } + while (true) { + const buf = this.#readStream.read(); + if (buf === null || !this.push(buf)) { + return; + } + } + } + }; + } + return StdioWriteStream; +} + +function getStdinStream({ require }) { + if (!StdinStream) { + var { + Readable, + Duplex, + eos, + destroy, + } = require("node:stream", "node:process"); + if (!createWriteStream) { + lazyLoadDeps({ require }); + } + + StdinStream = class StdinStream extends Duplex { + #readStream; + #writeStream; + + #readable = true; + #writable = true; + + #onFinish; + #onClose; + #onDrain; + #onReadable; + + fd = 0; + get isTTY() { + return require("tty").isatty(this.fd); + } + + constructor() { + super({ readable: true, writable: true }); + + Object.defineProperty(this, "fd", { + value: 0, + writable: false, + configurable: false, + }); + + this.#onReadable = this._read.bind(this); + } + + #onFinished(err) { + const cb = this.#onClose; + this.#onClose = null; + + if (cb) { + cb(err); + } else if (err) { + this.destroy(err); + } else if (!this.#readable && !this.#writable) { + this.destroy(); + } + } + + _destroy(err, callback) { + if (!err && this.#onClose !== null) { + err = new AbortError(); + } + if (this.#onClose === null) { + callback(err); + } else { + this.#onClose = callback; + if (this.#readStream) destroy(this.#readStream, err); + if (this.#writeStream) destroy(this.#writeStream, err); + } + } + + on(ev, cb) { + super.on(ev, cb); + if (!this.#readStream && (ev === "readable" || ev === "data")) { + this.#readStream = Readable.fromWeb(Bun.stdin.stream()); + + this.#readStream.on("readable", () => { + const cb = this.#onReadable; + this.#onReadable = null; + cb(); + }); + + this.#readStream.on("end", () => { + this.push(null); + }); + + eos(this.#readStream, (err) => { + this.#readable = false; + if (err) { + destroy(this.#readStream, err); + } + this.#onFinished(err); + }); + } + } + + _read() { + while (true) { + const buf = this.#readStream.read(); + if (buf === null || !this.push(buf)) { + this.#onReadable = this._read.bind(this); + return; + } + } + } + + _write(chunk, encoding, callback) { + if (!this.#writeStream) { + this.#writeStream = createWriteStream("/dev/fd/0"); + + this.#writeStream.on("finish", () => { + if (this.#onFinish) { + const cb = this.#onFinish; + this.#onFinish = null; + cb(); + } + }); + + this.#writeStream.on("drain", () => { + if (this.#onDrain) { + const cb = this.#onDrain; + this.#onDrain = null; + cb(); + } + }); + + eos(this.#writeStream, (err) => { + this.#writable = false; + if (err) { + destroy(this.#writeStream, err); + } + this.#onFinished(err); + }); + } + + if (this.#writeStream.write(chunk, encoding)) { + callback(); + } else { + this.#onDrain = callback; + } + } + + _final(callback) { + this.#writeStream.end(); + this.#onFinish = callback.bind(this); + } + }; + } + return StdinStream; +} + +export function stdin({ require }) { + var StdinStream = getStdinStream({ require }); + var stream = new StdinStream(); + return stream; +} + +export function stdout({ require }) { + var StdioWriteStream = getStdioWriteStream({ require }); + var stream = new StdioWriteStream(1); + return stream; +} + +export function stderr({ require }) { + var StdioWriteStream = getStdioWriteStream({ require }); + var stream = new StdioWriteStream(2); + return stream; +} + +export default { + stdin, + stdout, + stderr, + + [Symbol.for("CommonJS")]: 0, +}; |