aboutsummaryrefslogtreecommitdiff
path: root/src/js/node/fs.js
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-08-31 22:31:01 -0700
committerGravatar dave caruso <me@paperdave.net> 2023-08-31 22:31:01 -0700
commitd0a58d329f67708fa3427882f04c7268746d937d (patch)
treef83de8b45833e9976f258ddf9bd31fbfd602f82d /src/js/node/fs.js
parentb07bb3ce174b500b567bb760e82d4696c88a8b28 (diff)
downloadbun-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.js1047
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();
// },