diff options
Diffstat (limited to 'src/js/node/fs.js')
-rw-r--r-- | src/js/node/fs.js | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/src/js/node/fs.js b/src/js/node/fs.js index ab6816904..2c6b8cfbe 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -7,7 +7,6 @@ const Stream = require("node:stream"); const { isArrayBufferView } = require("node:util/types"); const constants = $processBindingConstants.fs; -const { COPYFILE_EXCL } = constants; var fs = Bun.fs(); class FSWatcher extends EventEmitter { @@ -65,6 +64,45 @@ class FSWatcher extends EventEmitter { unref() { this.#watcher?.unref(); } + + // https://github.com/nodejs/node/blob/9f51c55a47702dc6a0ca3569853dd7ba022bf7bb/lib/internal/fs/watchers.js#L259-L263 + start() {} +} + +/** Implemented in `node_fs_stat_watcher.zig` */ +// interface StatWatcherHandle { +// ref(); +// unref(); +// close(); +// } + +class StatWatcher extends EventEmitter { + // _handle: StatWatcherHandle; + + constructor(path, options) { + super(); + this._handle = fs.watchFile(path, options, this.#onChange.bind(this)); + } + + #onChange(curr, prev) { + this.emit("change", curr, prev); + } + + // https://github.com/nodejs/node/blob/9f51c55a47702dc6a0ca3569853dd7ba022bf7bb/lib/internal/fs/watchers.js#L259-L263 + start() {} + + stop() { + this._handle?.close(); + this._handle = null; + } + + ref() { + this._handle?.ref(); + } + + unref() { + this._handle?.unref(); + } } var access = function access(...args) { @@ -316,6 +354,54 @@ var access = function access(...args) { return new FSWatcher(path, options, listener); }; +// TODO: move this entire thing into native code. +// the reason it's not done right now is because there isnt a great way to have multiple +// listeners per StatWatcher with the current implementation in native code. the downside +// of this means we need to do path validation in the js side of things +const statWatchers = new Map(); +let _pathModule; +function getValidatedPath(p) { + if (p instanceof URL) return Bun.fileURLToPath(p); + if (typeof p !== "string") throw new TypeError("Path must be a string or URL."); + return (_pathModule ??= require("node:path")).resolve(p); +} +function watchFile(filename, options, listener) { + filename = getValidatedPath(filename); + + if (typeof options === "function") { + listener = options; + options = {}; + } + + if (typeof listener !== "function") { + throw new TypeError("listener must be a function"); + } + + var stat = statWatchers.get(filename); + if (!stat) { + stat = new StatWatcher(filename, options); + statWatchers.set(filename, stat); + } + stat.addListener("change", listener); + return stat; +} +function unwatchFile(filename, listener) { + filename = getValidatedPath(filename); + + var stat = statWatchers.get(filename); + if (!stat) return; + if (listener) { + stat.removeListener("change", listener); + if (stat.listenerCount("change") !== 0) { + return; + } + } else { + stat.removeAllListeners("change"); + } + stat.stop(); + statWatchers.delete(filename); +} + function callbackify(fsFunction, args) { try { const result = fsFunction.apply(fs, args.slice(0, args.length - 1)); @@ -1200,9 +1286,11 @@ export default { truncateSync, unlink, unlinkSync, + unwatchFile, utimes, utimesSync, watch, + watchFile, write, writeFile, writeFileSync, |