aboutsummaryrefslogtreecommitdiff
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/node/fs.js65
-rw-r--r--src/js/node/fs.promises.ts51
-rw-r--r--src/js/out/modules/node/fs.js50
-rw-r--r--src/js/out/modules/node/fs.promises.js2
-rw-r--r--src/js/private.d.ts86
5 files changed, 249 insertions, 5 deletions
diff --git a/src/js/node/fs.js b/src/js/node/fs.js
index f117020dd..6b0e3954e 100644
--- a/src/js/node/fs.js
+++ b/src/js/node/fs.js
@@ -1,3 +1,5 @@
+import { EventEmitter } from "stream";
+
// Hardcoded module "node:fs"
var { direct, isPromise, isCallable } = import.meta.primordials;
var promises = import.meta.require("node:fs/promises");
@@ -7,6 +9,63 @@ var NativeReadable = _getNativeReadableStreamPrototype(2, Readable); // 2 means
var fs = Bun.fs();
var debug = process.env.DEBUG ? console.log : () => {};
+
+class FSWatcher extends EventEmitter {
+ #watcher;
+ #listener;
+ constructor(path, options, listener) {
+ super();
+
+ if (typeof options === "function") {
+ listener = options;
+ options = {};
+ } else if (typeof options === "string") {
+ options = { encoding: options };
+ }
+
+ if (typeof listener !== "function") {
+ listener = () => {};
+ }
+
+ this.#listener = listener;
+ try {
+ this.#watcher = fs.watch(path, options || {}, this.#onEvent.bind(this));
+ } catch (e) {
+ if (!e.message?.startsWith("FileNotFound")) {
+ throw e;
+ }
+ const notFound = new Error(`ENOENT: no such file or directory, watch '${path}'`);
+ notFound.code = "ENOENT";
+ notFound.errno = -2;
+ notFound.path = path;
+ notFound.syscall = "watch";
+ notFound.filename = path;
+ throw notFound;
+ }
+ }
+
+ #onEvent(eventType, filenameOrError) {
+ if (eventType === "error" || eventType === "close") {
+ this.emit(eventType, filenameOrError);
+ } else {
+ this.emit("change", eventType, filenameOrError);
+ this.#listener(eventType, filenameOrError);
+ }
+ }
+
+ close() {
+ this.#watcher?.close();
+ this.#watcher = null;
+ }
+
+ ref() {
+ this.#watcher?.ref();
+ }
+
+ unref() {
+ this.#watcher?.unref();
+ }
+}
export var access = function access(...args) {
callbackify(fs.accessSync, args);
},
@@ -153,6 +212,9 @@ export var access = function access(...args) {
rmdirSync = fs.rmdirSync.bind(fs),
Dirent = fs.Dirent,
Stats = fs.Stats,
+ watch = function watch(path, options, listener) {
+ return new FSWatcher(path, options, listener);
+ },
promises = import.meta.require("node:fs/promises");
function callbackify(fsFunction, args) {
@@ -1002,7 +1064,8 @@ export default {
writeSync,
WriteStream,
ReadStream,
-
+ watch,
+ FSWatcher,
[Symbol.for("::bunternal::")]: {
ReadStreamClass,
WriteStreamClass,
diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts
index de802928b..7df446ccb 100644
--- a/src/js/node/fs.promises.ts
+++ b/src/js/node/fs.promises.ts
@@ -1,4 +1,5 @@
// Hardcoded module "node:fs/promises"
+
// Note: `constants` is injected into the top of this file
declare var constants: typeof import("node:fs/promises").constants;
@@ -38,6 +39,55 @@ var promisify = {
},
}[notrace];
+export function watch(
+ filename: string | Buffer | URL,
+ options: { encoding?: BufferEncoding; persistent?: boolean; recursive?: boolean; signal?: AbortSignal } = {},
+) {
+ type Event = {
+ eventType: string;
+ filename: string | Buffer | undefined;
+ };
+ const events: Array<Event> = [];
+ if (filename instanceof URL) {
+ throw new TypeError("Watch URLs are not supported yet");
+ } else if (Buffer.isBuffer(filename)) {
+ filename = filename.toString();
+ } else if (typeof filename !== "string") {
+ throw new TypeError("Expected path to be a string or Buffer");
+ }
+ let nextEventResolve: Function | null = null;
+ if (typeof options === "string") {
+ options = { encoding: options };
+ }
+ fs.watch(filename, options || {}, (eventType: string, filename: string | Buffer | undefined) => {
+ events.push({ eventType, filename });
+ if (nextEventResolve) {
+ const resolve = nextEventResolve;
+ nextEventResolve = null;
+ resolve();
+ }
+ });
+ return {
+ async *[Symbol.asyncIterator]() {
+ let closed = false;
+ while (!closed) {
+ while (events.length) {
+ let event = events.shift() as Event;
+ if (event.eventType === "close") {
+ closed = true;
+ break;
+ }
+ if (event.eventType === "error") {
+ closed = true;
+ throw event.filename;
+ }
+ yield event;
+ }
+ await new Promise((resolve: Function) => (nextEventResolve = resolve));
+ }
+ },
+ };
+}
export var access = promisify(fs.accessSync),
appendFile = promisify(fs.appendFileSync),
close = promisify(fs.closeSync),
@@ -112,6 +162,7 @@ export default {
lutimes,
rm,
rmdir,
+ watch,
constants,
[Symbol.for("CommonJS")]: 0,
};
diff --git a/src/js/out/modules/node/fs.js b/src/js/out/modules/node/fs.js
index cc1e14d2b..cc3763cfc 100644
--- a/src/js/out/modules/node/fs.js
+++ b/src/js/out/modules/node/fs.js
@@ -1,3 +1,4 @@
+var {EventEmitter } = import.meta.require("node:stream");
var callbackify = function(fsFunction, args) {
try {
const result = fsFunction.apply(fs, args.slice(0, args.length - 1)), callback = args[args.length - 1];
@@ -16,7 +17,47 @@ function createWriteStream(path, options) {
return new WriteStream(path, options);
}
var { direct, isPromise, isCallable } = import.meta.primordials, promises = import.meta.require("node:fs/promises"), { Readable, NativeWritable, _getNativeReadableStreamPrototype, eos: eos_ } = import.meta.require("node:stream"), NativeReadable = _getNativeReadableStreamPrototype(2, Readable), fs = Bun.fs(), debug = process.env.DEBUG ? console.log : () => {
-}, access = function access2(...args) {
+};
+
+class FSWatcher extends EventEmitter {
+ #watcher;
+ #listener;
+ constructor(path, options, listener) {
+ super();
+ if (typeof options === "function")
+ listener = options, options = {};
+ else if (typeof options === "string")
+ options = { encoding: options };
+ if (typeof listener !== "function")
+ listener = () => {
+ };
+ this.#listener = listener;
+ try {
+ this.#watcher = fs.watch(path, options || {}, this.#onEvent.bind(this));
+ } catch (e) {
+ if (!e.message?.startsWith("FileNotFound"))
+ throw e;
+ const notFound = new Error(`ENOENT: no such file or directory, watch '${path}'`);
+ throw notFound.code = "ENOENT", notFound.errno = -2, notFound.path = path, notFound.syscall = "watch", notFound.filename = path, notFound;
+ }
+ }
+ #onEvent(eventType, filenameOrError) {
+ if (eventType === "error" || eventType === "close")
+ this.emit(eventType, filenameOrError);
+ else
+ this.emit("change", eventType, filenameOrError), this.#listener(eventType, filenameOrError);
+ }
+ close() {
+ this.#watcher?.close(), this.#watcher = null;
+ }
+ ref() {
+ this.#watcher?.ref();
+ }
+ unref() {
+ this.#watcher?.unref();
+ }
+}
+var access = function access2(...args) {
callbackify(fs.accessSync, args);
}, appendFile = function appendFile2(...args) {
callbackify(fs.appendFileSync, args);
@@ -88,7 +129,9 @@ var { direct, isPromise, isCallable } = import.meta.primordials, promises = impo
callbackify(fs.utimesSync, args);
}, lutimes = function lutimes2(...args) {
callbackify(fs.lutimesSync, args);
-}, accessSync = fs.accessSync.bind(fs), appendFileSync = fs.appendFileSync.bind(fs), closeSync = fs.closeSync.bind(fs), copyFileSync = fs.copyFileSync.bind(fs), existsSync = fs.existsSync.bind(fs), chownSync = fs.chownSync.bind(fs), chmodSync = fs.chmodSync.bind(fs), fchmodSync = fs.fchmodSync.bind(fs), fchownSync = fs.fchownSync.bind(fs), fstatSync = fs.fstatSync.bind(fs), fsyncSync = fs.fsyncSync.bind(fs), ftruncateSync = fs.ftruncateSync.bind(fs), futimesSync = fs.futimesSync.bind(fs), lchmodSync = fs.lchmodSync.bind(fs), lchownSync = fs.lchownSync.bind(fs), linkSync = fs.linkSync.bind(fs), lstatSync = fs.lstatSync.bind(fs), mkdirSync = fs.mkdirSync.bind(fs), mkdtempSync = fs.mkdtempSync.bind(fs), openSync = fs.openSync.bind(fs), readSync = fs.readSync.bind(fs), writeSync = fs.writeSync.bind(fs), readdirSync = fs.readdirSync.bind(fs), readFileSync = fs.readFileSync.bind(fs), writeFileSync = fs.writeFileSync.bind(fs), readlinkSync = fs.readlinkSync.bind(fs), realpathSync = fs.realpathSync.bind(fs), renameSync = fs.renameSync.bind(fs), statSync = fs.statSync.bind(fs), symlinkSync = fs.symlinkSync.bind(fs), truncateSync = fs.truncateSync.bind(fs), unlinkSync = fs.unlinkSync.bind(fs), utimesSync = fs.utimesSync.bind(fs), lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, promises = import.meta.require("node:fs/promises"), readStreamPathFastPathSymbol = Symbol.for("Bun.Node.readStreamPathFastPath"), readStreamSymbol = Symbol.for("Bun.NodeReadStream"), readStreamPathOrFdSymbol = Symbol.for("Bun.NodeReadStreamPathOrFd"), writeStreamSymbol = Symbol.for("Bun.NodeWriteStream"), writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath"), writeStreamPathFastPathCallSymbol = Symbol.for("Bun.NodeWriteStreamFastPathCall"), kIoDone = Symbol.for("kIoDone"), defaultReadStreamOptions = {
+}, accessSync = fs.accessSync.bind(fs), appendFileSync = fs.appendFileSync.bind(fs), closeSync = fs.closeSync.bind(fs), copyFileSync = fs.copyFileSync.bind(fs), existsSync = fs.existsSync.bind(fs), chownSync = fs.chownSync.bind(fs), chmodSync = fs.chmodSync.bind(fs), fchmodSync = fs.fchmodSync.bind(fs), fchownSync = fs.fchownSync.bind(fs), fstatSync = fs.fstatSync.bind(fs), fsyncSync = fs.fsyncSync.bind(fs), ftruncateSync = fs.ftruncateSync.bind(fs), futimesSync = fs.futimesSync.bind(fs), lchmodSync = fs.lchmodSync.bind(fs), lchownSync = fs.lchownSync.bind(fs), linkSync = fs.linkSync.bind(fs), lstatSync = fs.lstatSync.bind(fs), mkdirSync = fs.mkdirSync.bind(fs), mkdtempSync = fs.mkdtempSync.bind(fs), openSync = fs.openSync.bind(fs), readSync = fs.readSync.bind(fs), writeSync = fs.writeSync.bind(fs), readdirSync = fs.readdirSync.bind(fs), readFileSync = fs.readFileSync.bind(fs), writeFileSync = fs.writeFileSync.bind(fs), readlinkSync = fs.readlinkSync.bind(fs), realpathSync = fs.realpathSync.bind(fs), renameSync = fs.renameSync.bind(fs), statSync = fs.statSync.bind(fs), symlinkSync = fs.symlinkSync.bind(fs), truncateSync = fs.truncateSync.bind(fs), unlinkSync = fs.unlinkSync.bind(fs), utimesSync = fs.utimesSync.bind(fs), lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, watch = function watch2(path, options, listener) {
+ return new FSWatcher(path, options, listener);
+}, promises = import.meta.require("node:fs/promises"), readStreamPathFastPathSymbol = Symbol.for("Bun.Node.readStreamPathFastPath"), readStreamSymbol = Symbol.for("Bun.NodeReadStream"), readStreamPathOrFdSymbol = Symbol.for("Bun.NodeReadStreamPathOrFd"), writeStreamSymbol = Symbol.for("Bun.NodeWriteStream"), writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath"), writeStreamPathFastPathCallSymbol = Symbol.for("Bun.NodeWriteStreamFastPathCall"), kIoDone = Symbol.for("kIoDone"), defaultReadStreamOptions = {
file: void 0,
fd: void 0,
flags: "r",
@@ -590,6 +633,8 @@ var fs_default = {
writeSync,
WriteStream,
ReadStream,
+ watch,
+ FSWatcher,
[Symbol.for("::bunternal::")]: {
ReadStreamClass,
WriteStreamClass
@@ -600,6 +645,7 @@ export {
writeFileSync,
writeFile,
write,
+ watch,
utimesSync,
utimes,
unlinkSync,
diff --git a/src/js/out/modules/node/fs.promises.js b/src/js/out/modules/node/fs.promises.js
index 2780ff166..ef3330771 100644
--- a/src/js/out/modules/node/fs.promises.js
+++ b/src/js/out/modules/node/fs.promises.js
@@ -1 +1 @@
-var D=Bun.fs(),B="::bunternal::",E={[B]:(S)=>{var b={[B]:function(C,J,q){var z;try{z=S.apply(D,q),q=void 0}catch(A){q=void 0,J(A);return}C(z)}}[B];return async function(...C){return await new Promise((J,q)=>{process.nextTick(b,J,q,C)})}}}[B],G=E(D.accessSync),H=E(D.appendFileSync),I=E(D.closeSync),K=E(D.copyFileSync),L=E(D.existsSync),M=E(D.chownSync),N=E(D.chmodSync),O=E(D.fchmodSync),P=E(D.fchownSync),Q=E(D.fstatSync),R=E(D.fsyncSync),T=E(D.ftruncateSync),U=E(D.futimesSync),V=E(D.lchmodSync),W=E(D.lchownSync),X=E(D.linkSync),Y=E(D.lstatSync),Z=E(D.mkdirSync),_=E(D.mkdtempSync),$=E(D.openSync),x=E(D.readSync),j=E(D.writeSync),v=E(D.readdirSync),w=E(D.readFileSync),k=E(D.writeFileSync),F=E(D.readlinkSync),h=E(D.realpathSync),g=E(D.renameSync),u=E(D.statSync),d=E(D.symlinkSync),n=E(D.truncateSync),l=E(D.unlinkSync),a=E(D.utimesSync),c=E(D.lutimesSync),t=E(D.rmSync),y=E(D.rmdirSync),p={access:G,appendFile:H,close:I,copyFile:K,exists:L,chown:M,chmod:N,fchmod:O,fchown:P,fstat:Q,fsync:R,ftruncate:T,futimes:U,lchmod:V,lchown:W,link:X,lstat:Y,mkdir:Z,mkdtemp:_,open:$,read:x,write:j,readdir:v,readFile:w,writeFile:k,readlink:F,realpath:h,rename:g,stat:u,symlink:d,truncate:n,unlink:l,utimes:a,lutimes:c,rm:t,rmdir:y,constants,[Symbol.for("CommonJS")]:0};export{k as writeFile,j as write,a as utimes,l as unlink,n as truncate,d as symlink,u as stat,y as rmdir,t as rm,g as rename,h as realpath,F as readlink,v as readdir,w as readFile,x as read,$ as open,_ as mkdtemp,Z as mkdir,c as lutimes,Y as lstat,X as link,W as lchown,V as lchmod,U as futimes,T as ftruncate,R as fsync,Q as fstat,P as fchown,O as fchmod,L as exists,p as default,K as copyFile,I as close,M as chown,N as chmod,H as appendFile,G as access};
+function H(S,C={}){const J=[];if(S instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(S))S=S.toString();else if(typeof S!=="string")throw new TypeError("Expected path to be a string or Buffer");let b=null;if(typeof C==="string")C={encoding:C};return D.watch(S,C||{},(q,z)=>{if(J.push({eventType:q,filename:z}),b){const A=b;b=null,A()}}),{async*[Symbol.asyncIterator](){let q=!1;while(!q){while(J.length){let z=J.shift();if(z.eventType==="close"){q=!0;break}if(z.eventType==="error")throw q=!0,z.filename;yield z}await new Promise((z)=>b=z)}}}}var D=Bun.fs(),B="::bunternal::",G={[B]:(S)=>{var C={[B]:function(J,b,q){var z;try{z=S.apply(D,q),q=void 0}catch(A){q=void 0,b(A);return}J(z)}}[B];return async function(...J){return await new Promise((b,q)=>{process.nextTick(C,b,q,J)})}}}[B],I=G(D.accessSync),K=G(D.appendFileSync),L=G(D.closeSync),M=G(D.copyFileSync),N=G(D.existsSync),O=G(D.chownSync),P=G(D.chmodSync),Q=G(D.fchmodSync),U=G(D.fchownSync),V=G(D.fstatSync),W=G(D.fsyncSync),X=G(D.ftruncateSync),Y=G(D.futimesSync),Z=G(D.lchmodSync),_=G(D.lchownSync),$=G(D.linkSync),T=G(D.lstatSync),E=G(D.mkdirSync),j=G(D.mkdtempSync),R=G(D.openSync),k=G(D.readSync),x=G(D.writeSync),F=G(D.readdirSync),u=G(D.readFileSync),w=G(D.writeFileSync),g=G(D.readlinkSync),h=G(D.realpathSync),d=G(D.renameSync),c=G(D.statSync),v=G(D.symlinkSync),a=G(D.truncateSync),y=G(D.unlinkSync),l=G(D.utimesSync),t=G(D.lutimesSync),p=G(D.rmSync),n=G(D.rmdirSync),m={access:I,appendFile:K,close:L,copyFile:M,exists:N,chown:O,chmod:P,fchmod:Q,fchown:U,fstat:V,fsync:W,ftruncate:X,futimes:Y,lchmod:Z,lchown:_,link:$,lstat:T,mkdir:E,mkdtemp:j,open:R,read:k,write:x,readdir:F,readFile:u,writeFile:w,readlink:g,realpath:h,rename:d,stat:c,symlink:v,truncate:a,unlink:y,utimes:l,lutimes:t,rm:p,rmdir:n,watch:H,constants,[Symbol.for("CommonJS")]:0};export{w as writeFile,x as write,H as watch,l as utimes,y as unlink,a as truncate,v as symlink,c as stat,n as rmdir,p as rm,d as rename,h as realpath,g as readlink,F as readdir,u as readFile,k as read,R as open,j as mkdtemp,E as mkdir,t as lutimes,T as lstat,$ as link,_ as lchown,Z as lchmod,Y as futimes,X as ftruncate,W as fsync,V as fstat,U as fchown,Q as fchmod,N as exists,m as default,M as copyFile,L as close,O as chown,P as chmod,K as appendFile,I as access};
diff --git a/src/js/private.d.ts b/src/js/private.d.ts
index b6ed64801..b689c208e 100644
--- a/src/js/private.d.ts
+++ b/src/js/private.d.ts
@@ -6,11 +6,95 @@
*/
declare function $bundleError(error: string);
+type BunFSWatchOptions = { encoding?: BufferEncoding; persistent?: boolean; recursive?: boolean; signal?: AbortSignal };
+
+type BunWatchEventType = "rename" | "change" | "error" | "close";
+type BunWatchListener<T> = (event: WatchEventType, filename: T | Error | undefined) => void;
+
+interface BunFSWatcher {
+ /**
+ * Stop watching for changes on the given `BunFSWatcher`. Once stopped, the `BunFSWatcher` object is no longer usable.
+ * @since v0.6.8
+ */
+ close(): void;
+
+ /**
+ * When called, requests that the Node.js event loop not exit so long as the <BunFSWatcher> is active. Calling watcher.ref() multiple times will have no effect.
+ */
+ ref(): void;
+
+ /**
+ * When called, the active <BunFSWatcher> object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the <BunFSWatcher> object's callback is invoked. Calling watcher.unref() multiple times will have no effect.
+ */
+ unref(): void;
+}
+type BunFS = Omit<typeof import("node:fs"), "watch"> & {
+ /**
+ * Watch for changes on `filename`, where `filename` is either a file or a
+ * directory.
+ *
+ * The second argument is optional. If `options` is provided as a string, it
+ * specifies the `encoding`. Otherwise `options` should be passed as an object.
+ *
+ * The listener callback gets two arguments `(eventType, filename)`. `eventType`is either `'rename'`, `'change', 'error' or 'close'`, and `filename` is the name of the file
+ * which triggered the event, the error when `eventType` is 'error' or undefined when eventType is 'close'.
+ *
+ * On most platforms, `'rename'` is emitted whenever a filename appears or
+ * disappears in the directory.
+ *
+ *
+ * If a `signal` is passed, aborting the corresponding AbortController will close
+ * the returned `BunFSWatcher`.
+ * @since v0.6.8
+ * @param listener
+ */
+ watch(
+ filename: string,
+ options:
+ | (WatchOptions & {
+ encoding: "buffer";
+ })
+ | "buffer",
+ listener?: BunWatchListener<Buffer>,
+ ): BunFSWatcher;
+ /**
+ * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `BunFSWatcher`.
+ * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol.
+ * @param options Either the encoding for the filename provided to the listener, or an object optionally specifying encoding, persistent, and recursive options.
+ * If `encoding` is not supplied, the default of `'utf8'` is used.
+ * If `persistent` is not supplied, the default of `true` is used.
+ * If `recursive` is not supplied, the default of `false` is used.
+ */
+ watch(
+ filename: string,
+ options?: WatchOptions | BufferEncoding | null,
+ listener?: BunWatchListener<string>,
+ ): BunFSWatcher;
+ /**
+ * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `BunFSWatcher`.
+ * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol.
+ * @param options Either the encoding for the filename provided to the listener, or an object optionally specifying encoding, persistent, and recursive options.
+ * If `encoding` is not supplied, the default of `'utf8'` is used.
+ * If `persistent` is not supplied, the default of `true` is used.
+ * If `recursive` is not supplied, the default of `false` is used.
+ */
+ watch(
+ filename: string,
+ options: BunWatchListener | string,
+ listener?: BunWatchListener<string | Buffer>,
+ ): BunFSWatcher;
+ /**
+ * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `BunFSWatcher`.
+ * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol.
+ */
+ watch(filename: string, listener?: BunWatchListener<string>): BunFSWatcher;
+};
+
declare module "bun" {
var TOML: {
parse(contents: string): any;
};
- function fs(): typeof import("node:fs");
+ function fs(): BunFS;
function _Os(): typeof import("node:os");
function jest(): typeof import("bun:test");
var main: string;