diff options
author | 2023-08-31 22:31:01 -0700 | |
---|---|---|
committer | 2023-08-31 22:31:01 -0700 | |
commit | d0a58d329f67708fa3427882f04c7268746d937d (patch) | |
tree | f83de8b45833e9976f258ddf9bd31fbfd602f82d /src/js/node/fs.js | |
parent | b07bb3ce174b500b567bb760e82d4696c88a8b28 (diff) | |
download | bun-dave/es5-class-helper.tar.gz bun-dave/es5-class-helper.tar.zst bun-dave/es5-class-helper.zip |
add es5ClassCompatdave/es5-class-helper
Diffstat (limited to 'src/js/node/fs.js')
-rw-r--r-- | src/js/node/fs.js | 1047 |
1 files changed, 494 insertions, 553 deletions
diff --git a/src/js/node/fs.js b/src/js/node/fs.js index ab6816904..48960d3ee 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -5,9 +5,9 @@ const EventEmitter = require("node:events"); const promises = require("node:fs/promises"); const Stream = require("node:stream"); const { isArrayBufferView } = require("node:util/types"); +const { es5ClassCompat } = require("$shared"); const constants = $processBindingConstants.fs; -const { COPYFILE_EXCL } = constants; var fs = Bun.fs(); class FSWatcher extends EventEmitter { @@ -382,338 +382,320 @@ var defaultReadStreamOptions = { autoDestroy: true, }; -var ReadStreamClass; +class ReadStream extends Stream._getNativeReadableStreamPrototype(2, Stream.Readable) { + constructor(pathOrFd, options = defaultReadStreamOptions) { + if (typeof options !== "object" || !options) { + throw new TypeError("Expected options to be an object"); + } -ReadStream = (function (InternalReadStream) { - ReadStreamClass = InternalReadStream; - Object.defineProperty(ReadStreamClass.prototype, Symbol.toStringTag, { - value: "ReadStream", - enumerable: false, - }); - function ReadStream(path, options) { - return new InternalReadStream(path, options); - } - ReadStream.prototype = InternalReadStream.prototype; - return Object.defineProperty(ReadStream, Symbol.hasInstance, { - value(instance) { - return instance instanceof InternalReadStream; - }, - }); -})( - class ReadStream extends Stream._getNativeReadableStreamPrototype(2, Stream.Readable) { - constructor(pathOrFd, options = defaultReadStreamOptions) { - if (typeof options !== "object" || !options) { - throw new TypeError("Expected options to be an object"); - } + var { + flags = defaultReadStreamOptions.flags, + encoding = defaultReadStreamOptions.encoding, + mode = defaultReadStreamOptions.mode, + autoClose = defaultReadStreamOptions.autoClose, + emitClose = defaultReadStreamOptions.emitClose, + start = defaultReadStreamOptions.start, + end = defaultReadStreamOptions.end, + autoDestroy = defaultReadStreamOptions.autoClose, + fs = defaultReadStreamOptions.fs, + highWaterMark = defaultReadStreamOptions.highWaterMark, + fd = defaultReadStreamOptions.fd, + } = options; + + if (pathOrFd?.constructor?.name === "URL") { + pathOrFd = Bun.fileURLToPath(pathOrFd); + } - var { - flags = defaultReadStreamOptions.flags, - encoding = defaultReadStreamOptions.encoding, - mode = defaultReadStreamOptions.mode, - autoClose = defaultReadStreamOptions.autoClose, - emitClose = defaultReadStreamOptions.emitClose, - start = defaultReadStreamOptions.start, - end = defaultReadStreamOptions.end, - autoDestroy = defaultReadStreamOptions.autoClose, - fs = defaultReadStreamOptions.fs, - highWaterMark = defaultReadStreamOptions.highWaterMark, - fd = defaultReadStreamOptions.fd, - } = options; - - if (pathOrFd?.constructor?.name === "URL") { + // This is kinda hacky but we create a temporary object to assign props that we will later pull into the `this` context after we call super + var tempThis = {}; + if (fd != null) { + if (typeof fd !== "number") { + throw new TypeError("Expected options.fd to be a number"); + } + tempThis.fd = tempThis[readStreamPathOrFdSymbol] = fd; + tempThis.autoClose = false; + } else if (typeof pathOrFd === "string") { + if (pathOrFd.startsWith("file://")) { pathOrFd = Bun.fileURLToPath(pathOrFd); } + if (pathOrFd.length === 0) { + throw new TypeError("Expected path to be a non-empty string"); + } + tempThis.path = tempThis.file = tempThis[readStreamPathOrFdSymbol] = pathOrFd; + } else if (typeof pathOrFd === "number") { + pathOrFd |= 0; + if (pathOrFd < 0) { + throw new TypeError("Expected fd to be a positive integer"); + } + tempThis.fd = tempThis[readStreamPathOrFdSymbol] = pathOrFd; - // This is kinda hacky but we create a temporary object to assign props that we will later pull into the `this` context after we call super - var tempThis = {}; - if (fd != null) { - if (typeof fd !== "number") { - throw new TypeError("Expected options.fd to be a number"); - } - tempThis.fd = tempThis[readStreamPathOrFdSymbol] = fd; - tempThis.autoClose = false; - } else if (typeof pathOrFd === "string") { - if (pathOrFd.startsWith("file://")) { - pathOrFd = Bun.fileURLToPath(pathOrFd); - } - if (pathOrFd.length === 0) { - throw new TypeError("Expected path to be a non-empty string"); - } - tempThis.path = tempThis.file = tempThis[readStreamPathOrFdSymbol] = pathOrFd; - } else if (typeof pathOrFd === "number") { - pathOrFd |= 0; - if (pathOrFd < 0) { - throw new TypeError("Expected fd to be a positive integer"); - } - tempThis.fd = tempThis[readStreamPathOrFdSymbol] = pathOrFd; + tempThis.autoClose = false; + } else { + throw new TypeError("Expected a path or file descriptor"); + } - tempThis.autoClose = false; - } else { - throw new TypeError("Expected a path or file descriptor"); - } + // If fd not open for this file, open it + if (tempThis.fd === undefined) { + // NOTE: this fs is local to constructor, from options + tempThis.fd = fs.openSync(pathOrFd, flags, mode); + } + // Get FileRef from fd + var fileRef = Bun.file(tempThis.fd); + + // Get the stream controller + // We need the pointer to the underlying stream controller for the NativeReadable + var stream = fileRef.stream(); + var native = $direct(stream); + if (!native) { + $debug("no native readable stream"); + throw new Error("no native readable stream"); + } + var { stream: ptr } = native; + + super(ptr, { + ...options, + encoding, + autoDestroy, + autoClose, + emitClose, + highWaterMark, + }); - // If fd not open for this file, open it - if (tempThis.fd === undefined) { - // NOTE: this fs is local to constructor, from options - tempThis.fd = fs.openSync(pathOrFd, flags, mode); - } - // Get FileRef from fd - var fileRef = Bun.file(tempThis.fd); - - // Get the stream controller - // We need the pointer to the underlying stream controller for the NativeReadable - var stream = fileRef.stream(); - var native = $direct(stream); - if (!native) { - $debug("no native readable stream"); - throw new Error("no native readable stream"); - } - var { stream: ptr } = native; - - super(ptr, { - ...options, - encoding, - autoDestroy, - autoClose, - emitClose, - highWaterMark, - }); - - // Assign the tempThis props to this - Object.assign(this, tempThis); - this.#fileRef = fileRef; - - this.end = end; - this._read = this.#internalRead; - this.start = start; - this.flags = flags; - this.mode = mode; - this.emitClose = emitClose; - - this[readStreamPathFastPathSymbol] = - start === 0 && - end === Infinity && - autoClose && - fs === defaultReadStreamOptions.fs && - // is it an encoding which we don't need to decode? - (encoding === "buffer" || - encoding === "binary" || - encoding == null || - encoding === "utf-8" || - encoding === "utf8"); - this._readableState.autoClose = autoDestroy = autoClose; - this._readableState.highWaterMark = highWaterMark; - - if (start !== undefined) { - this.pos = start; - } + // Assign the tempThis props to this + Object.assign(this, tempThis); + this.#fileRef = fileRef; + + this.end = end; + this._read = this.#internalRead; + this.start = start; + this.flags = flags; + this.mode = mode; + this.emitClose = emitClose; + + this[readStreamPathFastPathSymbol] = + start === 0 && + end === Infinity && + autoClose && + fs === defaultReadStreamOptions.fs && + // is it an encoding which we don't need to decode? + (encoding === "buffer" || + encoding === "binary" || + encoding == null || + encoding === "utf-8" || + encoding === "utf8"); + this._readableState.autoClose = autoDestroy = autoClose; + this._readableState.highWaterMark = highWaterMark; + + if (start !== undefined) { + this.pos = start; } - #fileRef; - #fs; - file; - path; - fd = null; - flags; - mode; - start; - end; - pos; - bytesRead = 0; - #fileSize = -1; - _read; - - [readStreamSymbol] = true; - [readStreamPathOrFdSymbol]; - [readStreamPathFastPathSymbol]; - - _construct(callback) { - if (super._construct) { - super._construct(callback); - } else { - callback(); - } - this.emit("open", this.fd); - this.emit("ready"); + } + #fileRef; + #fs; + file; + path; + fd = null; + flags; + mode; + start; + end; + pos; + bytesRead = 0; + #fileSize = -1; + _read; + + [readStreamSymbol] = true; + [readStreamPathOrFdSymbol]; + [readStreamPathFastPathSymbol]; + + _construct(callback) { + if (super._construct) { + super._construct(callback); + } else { + callback(); } + this.emit("open", this.fd); + this.emit("ready"); + } - _destroy(err, cb) { - super._destroy(err, cb); - try { - var fd = this.fd; - this[readStreamPathFastPathSymbol] = false; - - if (!fd) { - cb(err); - } else { - this.#fs.close(fd, er => { - cb(er || err); - }); - this.fd = null; - } - } catch (e) { - throw e; + _destroy(err, cb) { + super._destroy(err, cb); + try { + var fd = this.fd; + this[readStreamPathFastPathSymbol] = false; + + if (!fd) { + cb(err); + } else { + this.#fs.close(fd, er => { + cb(er || err); + }); + this.fd = null; } + } catch (e) { + throw e; } + } - close(cb) { - if (typeof cb === "function") Stream.eos(this, cb); - this.destroy(); - } + close(cb) { + if (typeof cb === "function") Stream.eos(this, cb); + this.destroy(); + } - push(chunk) { - // Is it even possible for this to be less than 1? - var bytesRead = chunk?.length ?? 0; - if (bytesRead > 0) { - this.bytesRead += bytesRead; - var currPos = this.pos; - // Handle case of going through bytes before pos if bytesRead is less than pos - // If pos is undefined, we are reading through the whole file - // Otherwise we started from somewhere in the middle of the file - if (currPos !== undefined) { - // At this point we still haven't hit our `start` point - // We should discard this chunk and exit - if (this.bytesRead < currPos) { - return true; - } - // At this point, bytes read is greater than our starting position - // If the current position is still the starting position, that means - // this is the first chunk where we care about the bytes read - // and we need to subtract the bytes read from the start position (n) and slice the last n bytes - if (currPos === this.start) { - var n = this.bytesRead - currPos; - chunk = chunk.slice(-n); - var [_, ...rest] = arguments; - this.pos = this.bytesRead; - if (this.end !== undefined && this.bytesRead > this.end) { - chunk = chunk.slice(0, this.end - this.start + 1); - } - return super.push(chunk, ...rest); - } - var end = this.end; - // This is multi-chunk read case where we go passed the end of the what we want to read in the last chunk - if (end !== undefined && this.bytesRead > end) { - chunk = chunk.slice(0, end - currPos + 1); - var [_, ...rest] = arguments; - this.pos = this.bytesRead; - return super.push(chunk, ...rest); + push(chunk) { + // Is it even possible for this to be less than 1? + var bytesRead = chunk?.length ?? 0; + if (bytesRead > 0) { + this.bytesRead += bytesRead; + var currPos = this.pos; + // Handle case of going through bytes before pos if bytesRead is less than pos + // If pos is undefined, we are reading through the whole file + // Otherwise we started from somewhere in the middle of the file + if (currPos !== undefined) { + // At this point we still haven't hit our `start` point + // We should discard this chunk and exit + if (this.bytesRead < currPos) { + return true; + } + // At this point, bytes read is greater than our starting position + // If the current position is still the starting position, that means + // this is the first chunk where we care about the bytes read + // and we need to subtract the bytes read from the start position (n) and slice the last n bytes + if (currPos === this.start) { + var n = this.bytesRead - currPos; + chunk = chunk.slice(-n); + var [_, ...rest] = arguments; + this.pos = this.bytesRead; + if (this.end !== undefined && this.bytesRead > this.end) { + chunk = chunk.slice(0, this.end - this.start + 1); } + return super.push(chunk, ...rest); + } + var end = this.end; + // This is multi-chunk read case where we go passed the end of the what we want to read in the last chunk + if (end !== undefined && this.bytesRead > end) { + chunk = chunk.slice(0, end - currPos + 1); + var [_, ...rest] = arguments; this.pos = this.bytesRead; + return super.push(chunk, ...rest); } + this.pos = this.bytesRead; } - - return super.push(...arguments); } - // # + return super.push(...arguments); + } - // n should be the the highwatermark passed from Readable.read when calling internal _read (_read is set to this private fn in this class) - #internalRead(n) { - // pos is the current position in the file - // by default, if a start value is provided, pos starts at this.start - var { pos, end, bytesRead, fd, encoding } = this; + // # - n = - pos !== undefined // if there is a pos, then we are reading from that specific position in the file - ? Math.min(end - pos + 1, n) // takes smaller of length of the rest of the file to read minus the cursor position, or the highwatermark - : Math.min(end - bytesRead + 1, n); // takes the smaller of the length of the rest of the file from the bytes that we have marked read, or the highwatermark + // n should be the the highwatermark passed from Readable.read when calling internal _read (_read is set to this private fn in this class) + #internalRead(n) { + // pos is the current position in the file + // by default, if a start value is provided, pos starts at this.start + var { pos, end, bytesRead, fd, encoding } = this; - $debug("n @ fs.ReadStream.#internalRead, after clamp", n); + n = + pos !== undefined // if there is a pos, then we are reading from that specific position in the file + ? Math.min(end - pos + 1, n) // takes smaller of length of the rest of the file to read minus the cursor position, or the highwatermark + : Math.min(end - bytesRead + 1, n); // takes the smaller of the length of the rest of the file from the bytes that we have marked read, or the highwatermark - // If n is 0 or less, then we read all the file, push null to stream, ending it - if (n <= 0) { - this.push(null); - return; - } + $debug("n @ fs.ReadStream.#internalRead, after clamp", n); - // At this point, n is the lesser of the length of the rest of the file to read or the highwatermark - // Which means n is the maximum number of bytes to read - - // Basically if we don't know the file size yet, then check it - // Then if n is bigger than fileSize, set n to be fileSize - // This is a fast path to avoid allocating more than the file size for a small file (is this respected by native stream though) - if (this.#fileSize === -1 && bytesRead === 0 && pos === undefined) { - var stat = fstatSync(fd); - this.#fileSize = stat.size; - if (this.#fileSize > 0 && n > this.#fileSize) { - n = this.#fileSize + 1; - } - $debug("fileSize", this.#fileSize); - } + // If n is 0 or less, then we read all the file, push null to stream, ending it + if (n <= 0) { + this.push(null); + return; + } - // At this point, we know the file size and how much we want to read of the file - this[kIoDone] = false; - var res = super._read(n); - $debug("res -- undefined? why?", res); - if ($isPromise(res)) { - var then = res?.then; - if (then && $isCallable(then)) { - res.then( - () => { - this[kIoDone] = true; - // Tell ._destroy() that it's safe to close the fd now. - if (this.destroyed) { - this.emit(kIoDone); - } - }, - er => { - this[kIoDone] = true; - this.#errorOrDestroy(er); - }, - ); - } - } else { - this[kIoDone] = true; - if (this.destroyed) { - this.emit(kIoDone); - this.#errorOrDestroy(new Error("ERR_STREAM_PREMATURE_CLOSE")); - } + // At this point, n is the lesser of the length of the rest of the file to read or the highwatermark + // Which means n is the maximum number of bytes to read + + // Basically if we don't know the file size yet, then check it + // Then if n is bigger than fileSize, set n to be fileSize + // This is a fast path to avoid allocating more than the file size for a small file (is this respected by native stream though) + if (this.#fileSize === -1 && bytesRead === 0 && pos === undefined) { + var stat = fstatSync(fd); + this.#fileSize = stat.size; + if (this.#fileSize > 0 && n > this.#fileSize) { + n = this.#fileSize + 1; } + $debug("fileSize", this.#fileSize); } - #errorOrDestroy(err, sync = null) { - var { - _readableState: r = { destroyed: false, autoDestroy: false }, - _writableState: w = { destroyed: false, autoDestroy: false }, - } = this; - - if (w?.destroyed || r?.destroyed) { - return this; + // At this point, we know the file size and how much we want to read of the file + this[kIoDone] = false; + var res = super._read(n); + $debug("res -- undefined? why?", res); + if ($isPromise(res)) { + var then = res?.then; + if (then && $isCallable(then)) { + res.then( + () => { + this[kIoDone] = true; + // Tell ._destroy() that it's safe to close the fd now. + if (this.destroyed) { + this.emit(kIoDone); + } + }, + er => { + this[kIoDone] = true; + this.#errorOrDestroy(er); + }, + ); } - if (r?.autoDestroy || w?.autoDestroy) this.destroy(err); - else if (err) { - this.emit("error", err); + } else { + this[kIoDone] = true; + if (this.destroyed) { + this.emit(kIoDone); + this.#errorOrDestroy(new Error("ERR_STREAM_PREMATURE_CLOSE")); } } + } - pause() { - this[readStreamPathFastPathSymbol] = false; - return super.pause(); - } + #errorOrDestroy(err, sync = null) { + var { + _readableState: r = { destroyed: false, autoDestroy: false }, + _writableState: w = { destroyed: false, autoDestroy: false }, + } = this; - resume() { - this[readStreamPathFastPathSymbol] = false; - return super.resume(); + if (w?.destroyed || r?.destroyed) { + return this; } - - unshift(...args) { - this[readStreamPathFastPathSymbol] = false; - return super.unshift(...args); + if (r?.autoDestroy || w?.autoDestroy) this.destroy(err); + else if (err) { + this.emit("error", err); } + } - pipe(dest, pipeOpts) { - if (this[readStreamPathFastPathSymbol] && (pipeOpts?.end ?? true) && this._readableState?.pipes?.length === 0) { - if (writeStreamPathFastPathSymbol in dest && dest[writeStreamPathFastPathSymbol]) { - if (dest[writeStreamPathFastPathCallSymbol](this, pipeOpts)) { - return this; - } + pause() { + this[readStreamPathFastPathSymbol] = false; + return super.pause(); + } + + resume() { + this[readStreamPathFastPathSymbol] = false; + return super.resume(); + } + + unshift(...args) { + this[readStreamPathFastPathSymbol] = false; + return super.unshift(...args); + } + + pipe(dest, pipeOpts) { + if (this[readStreamPathFastPathSymbol] && (pipeOpts?.end ?? true) && this._readableState?.pipes?.length === 0) { + if (writeStreamPathFastPathSymbol in dest && dest[writeStreamPathFastPathSymbol]) { + if (dest[writeStreamPathFastPathCallSymbol](this, pipeOpts)) { + return this; } } - - this[readStreamPathFastPathSymbol] = false; - return super.pipe(dest, pipeOpts); } - }, -); + + this[readStreamPathFastPathSymbol] = false; + return super.pipe(dest, pipeOpts); + } +} +es5ClassCompat(ReadStream); function createReadStream(path, options) { return new ReadStream(path, options); @@ -734,324 +716,287 @@ var defaultWriteStreamOptions = { }, }; -var WriteStreamClass; -WriteStream = (function (InternalWriteStream) { - WriteStreamClass = InternalWriteStream; - Object.defineProperty(WriteStreamClass.prototype, Symbol.toStringTag, { - value: "WritesStream", - enumerable: false, - }); - - function WriteStream(path, options) { - return new InternalWriteStream(path, options); - } - WriteStream.prototype = InternalWriteStream.prototype; - return Object.defineProperty(WriteStream, Symbol.hasInstance, { - value(instance) { - return instance instanceof InternalWriteStream; - }, - }); -})( - class WriteStream extends Stream.NativeWritable { - constructor(path, options = defaultWriteStreamOptions) { - if (!options) { - throw new TypeError("Expected options to be an object"); - } - - var { - fs = defaultWriteStreamOptions.fs, - start = defaultWriteStreamOptions.start, - flags = defaultWriteStreamOptions.flags, - mode = defaultWriteStreamOptions.mode, - autoClose = true, - emitClose = false, - autoDestroy = autoClose, - encoding = defaultWriteStreamOptions.encoding, - fd = defaultWriteStreamOptions.fd, - pos = defaultWriteStreamOptions.pos, - } = options; - - var tempThis = {}; - if (fd != null) { - if (typeof fd !== "number") { - throw new Error("Expected options.fd to be a number"); - } - tempThis.fd = fd; - tempThis[writeStreamPathFastPathSymbol] = false; - } else if (typeof path === "string") { - if (path.length === 0) { - throw new TypeError("Expected a non-empty path"); - } - - if (path.startsWith("file:")) { - path = Bun.fileURLToPath(path); - } +class WriteStream extends Stream.NativeWritable { + constructor(path, options = defaultWriteStreamOptions) { + if (!options) { + throw new TypeError("Expected options to be an object"); + } - tempThis.path = path; - tempThis.fd = null; - tempThis[writeStreamPathFastPathSymbol] = - autoClose && - (start === undefined || start === 0) && - fs.write === defaultWriteStreamOptions.fs.write && - fs.close === defaultWriteStreamOptions.fs.close; + var { + fs = defaultWriteStreamOptions.fs, + start = defaultWriteStreamOptions.start, + flags = defaultWriteStreamOptions.flags, + mode = defaultWriteStreamOptions.mode, + autoClose = true, + emitClose = false, + autoDestroy = autoClose, + encoding = defaultWriteStreamOptions.encoding, + fd = defaultWriteStreamOptions.fd, + pos = defaultWriteStreamOptions.pos, + } = options; + + var tempThis = {}; + if (fd != null) { + if (typeof fd !== "number") { + throw new Error("Expected options.fd to be a number"); } - - if (tempThis.fd == null) { - tempThis.fd = fs.openSync(path, flags, mode); + tempThis.fd = fd; + tempThis[writeStreamPathFastPathSymbol] = false; + } else if (typeof path === "string") { + if (path.length === 0) { + throw new TypeError("Expected a non-empty path"); } - super(tempThis.fd, { - ...options, - decodeStrings: false, - autoDestroy, - emitClose, - fd: tempThis, - }); - Object.assign(this, tempThis); - - if (typeof fs?.write !== "function") { - throw new TypeError("Expected fs.write to be a function"); + if (path.startsWith("file:")) { + path = Bun.fileURLToPath(path); } - if (typeof fs?.close !== "function") { - throw new TypeError("Expected fs.close to be a function"); - } + tempThis.path = path; + tempThis.fd = null; + tempThis[writeStreamPathFastPathSymbol] = + autoClose && + (start === undefined || start === 0) && + fs.write === defaultWriteStreamOptions.fs.write && + fs.close === defaultWriteStreamOptions.fs.close; + } - if (typeof fs?.open !== "function") { - throw new TypeError("Expected fs.open to be a function"); - } + if (tempThis.fd == null) { + tempThis.fd = fs.openSync(path, flags, mode); + } - if (typeof path === "object" && path) { - if (path instanceof URL) { - path = Bun.fileURLToPath(path); - } - } + super(tempThis.fd, { + ...options, + decodeStrings: false, + autoDestroy, + emitClose, + fd: tempThis, + }); + Object.assign(this, tempThis); - if (typeof path !== "string" && typeof fd !== "number") { - throw new TypeError("Expected a path or file descriptor"); - } + if (typeof fs?.write !== "function") { + throw new TypeError("Expected fs.write to be a function"); + } - this.start = start; - this.#fs = fs; - this.flags = flags; - this.mode = mode; + if (typeof fs?.close !== "function") { + throw new TypeError("Expected fs.close to be a function"); + } - if (this.start !== undefined) { - this.pos = this.start; - } + if (typeof fs?.open !== "function") { + throw new TypeError("Expected fs.open to be a function"); + } - if (encoding !== defaultWriteStreamOptions.encoding) { - this.setDefaultEncoding(encoding); - if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") { - this[writeStreamPathFastPathSymbol] = false; - } + if (typeof path === "object" && path) { + if (path instanceof URL) { + path = Bun.fileURLToPath(path); } } - get autoClose() { - return this._writableState.autoDestroy; + if (typeof path !== "string" && typeof fd !== "number") { + throw new TypeError("Expected a path or file descriptor"); } - set autoClose(val) { - this._writableState.autoDestroy = val; - } + this.start = start; + this.#fs = fs; + this.flags = flags; + this.mode = mode; - destroySoon = this.end; // TODO: what is this for? - - // noop, node has deprecated this - open() {} - - path; - fd; - flags; - mode; - #fs; - bytesWritten = 0; - pos; - [writeStreamPathFastPathSymbol]; - [writeStreamSymbol] = true; - start; - - [writeStreamPathFastPathCallSymbol](readStream, pipeOpts) { - if (!this[writeStreamPathFastPathSymbol]) { - return false; - } + if (this.start !== undefined) { + this.pos = this.start; + } - if (this.fd !== null) { + if (encoding !== defaultWriteStreamOptions.encoding) { + this.setDefaultEncoding(encoding); + if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") { this[writeStreamPathFastPathSymbol] = false; - return false; } - - this[kIoDone] = false; - readStream[kIoDone] = false; - return Bun.write(this[writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then( - bytesWritten => { - readStream[kIoDone] = this[kIoDone] = true; - this.bytesWritten += bytesWritten; - readStream.bytesRead += bytesWritten; - this.end(); - readStream.close(); - }, - err => { - readStream[kIoDone] = this[kIoDone] = true; - this.#errorOrDestroy(err); - readStream.emit("error", err); - }, - ); } + } + + get autoClose() { + return this._writableState.autoDestroy; + } - isBunFastPathEnabled() { - return this[writeStreamPathFastPathSymbol]; + set autoClose(val) { + this._writableState.autoDestroy = val; + } + + destroySoon = this.end; // TODO: what is this for? + + // noop, node has deprecated this + open() {} + + path; + fd; + flags; + mode; + #fs; + bytesWritten = 0; + pos; + [writeStreamPathFastPathSymbol]; + [writeStreamSymbol] = true; + start; + + [writeStreamPathFastPathCallSymbol](readStream, pipeOpts) { + if (!this[writeStreamPathFastPathSymbol]) { + return false; } - disableBunFastPath() { + if (this.fd !== null) { this[writeStreamPathFastPathSymbol] = false; + return false; } - #handleWrite(er, bytes) { - if (er) { - return this.#errorOrDestroy(er); - } + this[kIoDone] = false; + readStream[kIoDone] = false; + return Bun.write(this[writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then( + bytesWritten => { + readStream[kIoDone] = this[kIoDone] = true; + this.bytesWritten += bytesWritten; + readStream.bytesRead += bytesWritten; + this.end(); + readStream.close(); + }, + err => { + readStream[kIoDone] = this[kIoDone] = true; + this.#errorOrDestroy(err); + readStream.emit("error", err); + }, + ); + } - this.bytesWritten += bytes; - } + isBunFastPathEnabled() { + return this[writeStreamPathFastPathSymbol]; + } - #internalClose(err, cb) { - this[writeStreamPathFastPathSymbol] = false; - var fd = this.fd; - this.#fs.close(fd, er => { - this.fd = null; - cb(err || er); - }); + disableBunFastPath() { + this[writeStreamPathFastPathSymbol] = false; + } + + #handleWrite(er, bytes) { + if (er) { + return this.#errorOrDestroy(er); } - _construct(callback) { - if (typeof this.fd === "number") { - callback(); - return; - } + this.bytesWritten += bytes; + } + #internalClose(err, cb) { + this[writeStreamPathFastPathSymbol] = false; + var fd = this.fd; + this.#fs.close(fd, er => { + this.fd = null; + cb(err || er); + }); + } + + _construct(callback) { + if (typeof this.fd === "number") { callback(); - this.emit("open", this.fd); - this.emit("ready"); + return; } - _destroy(err, cb) { - if (this.fd === null) { - return cb(err); - } + callback(); + this.emit("open", this.fd); + this.emit("ready"); + } - if (this[kIoDone]) { - this.once(kIoDone, () => this.#internalClose(err, cb)); - return; - } + _destroy(err, cb) { + if (this.fd === null) { + return cb(err); + } - this.#internalClose(err, cb); + if (this[kIoDone]) { + this.once(kIoDone, () => this.#internalClose(err, cb)); + return; } - [kIoDone] = false; + this.#internalClose(err, cb); + } - close(cb) { - if (cb) { - if (this.closed) { - process.nextTick(cb); - return; - } - this.on("close", cb); - } + [kIoDone] = false; - // If we are not autoClosing, we should call - // destroy on 'finish'. - if (!this.autoClose) { - this.on("finish", this.destroy); + close(cb) { + if (cb) { + if (this.closed) { + process.nextTick(cb); + return; } + this.on("close", cb); + } - // We use end() instead of destroy() because of - // https://github.com/nodejs/node/issues/2006 - this.end(); + // If we are not autoClosing, we should call + // destroy on 'finish'. + if (!this.autoClose) { + this.on("finish", this.destroy); } - write(chunk, encoding = this._writableState.defaultEncoding, cb) { - this[writeStreamPathFastPathSymbol] = false; - if (typeof chunk === "string") { - chunk = Buffer.from(chunk, encoding); - } + // We use end() instead of destroy() because of + // https://github.com/nodejs/node/issues/2006 + this.end(); + } - // TODO: Replace this when something like lseek is available - var native = this.pos === undefined; - const callback = native - ? (err, bytes) => { - this[kIoDone] = false; - this.#handleWrite(err, bytes); - this.emit(kIoDone); - if (cb) !err ? cb() : cb(err); - } - : () => {}; - this[kIoDone] = true; - if (this._write) { - return this._write(chunk, encoding, callback); - } else { - return super.write(chunk, encoding, callback, native); - } + write(chunk, encoding = this._writableState.defaultEncoding, cb) { + this[writeStreamPathFastPathSymbol] = false; + if (typeof chunk === "string") { + chunk = Buffer.from(chunk, encoding); } - end(chunk, encoding, cb) { - var native = this.pos === undefined; - return super.end(chunk, encoding, cb, native); + // TODO: Replace this when something like lseek is available + var native = this.pos === undefined; + const callback = native + ? (err, bytes) => { + this[kIoDone] = false; + this.#handleWrite(err, bytes); + this.emit(kIoDone); + if (cb) !err ? cb() : cb(err); + } + : () => {}; + this[kIoDone] = true; + if (this._write) { + return this._write(chunk, encoding, callback); + } else { + return super.write(chunk, encoding, callback, native); } + } - _write = undefined; - _writev = undefined; + end(chunk, encoding, cb) { + var native = this.pos === undefined; + return super.end(chunk, encoding, cb, native); + } - get pending() { - return this.fd === null; - } + _write = undefined; + _writev = undefined; - _destroy(err, cb) { - this.close(err, cb); - } + get pending() { + return this.fd === null; + } - #errorOrDestroy(err) { - var { - _readableState: r = { destroyed: false, autoDestroy: false }, - _writableState: w = { destroyed: false, autoDestroy: false }, - } = this; + _destroy(err, cb) { + this.close(err, cb); + } - if (w?.destroyed || r?.destroyed) { - return this; - } - if (r?.autoDestroy || w?.autoDestroy) this.destroy(err); - else if (err) { - this.emit("error", err); - } + #errorOrDestroy(err) { + var { + _readableState: r = { destroyed: false, autoDestroy: false }, + _writableState: w = { destroyed: false, autoDestroy: false }, + } = this; + + if (w?.destroyed || r?.destroyed) { + return this; } - }, -); + if (r?.autoDestroy || w?.autoDestroy) this.destroy(err); + else if (err) { + this.emit("error", err); + } + } +} +es5ClassCompat(WriteStream); function createWriteStream(path, options) { // const WriteStream = getLazyWriteStream(); return new WriteStream(path, options); } -// NOTE: This was too smart and doesn't actually work -// WriteStream = Object.defineProperty( -// function WriteStream(path, options) { -// var _InternalWriteStream = getLazyWriteStream(); -// return new _InternalWriteStream(path, options); -// }, -// Symbol.hasInstance, -// { value: (instance) => instance[writeStreamSymbol] === true }, -// ); - -// ReadStream = Object.defineProperty( -// function ReadStream(path, options) { -// var _InternalReadStream = getLazyReadStream(); -// return new _InternalReadStream(path, options); -// }, -// Symbol.hasInstance, -// { value: (instance) => instance[readStreamSymbol] === true }, -// ); - Object.defineProperties(fs, { createReadStream: { value: createReadStream, @@ -1209,10 +1154,6 @@ export default { writeSync, writev, writevSync, - [Symbol.for("::bunternal::")]: { - ReadStreamClass, - WriteStreamClass, - }, // get WriteStream() { // return getLazyWriteStream(); // }, |