Files
ignis/src/shims/fs/watch.js

110 lines
2.9 KiB
JavaScript
Raw Normal View History

2026-03-07 15:42:19 +01:00
export function createFsWatch(transport) {
const watchers = new Map(); // path -> Set<listener>
return {
watch(path, options, listener) {
2026-03-11 22:08:30 +01:00
if (typeof options === "function") {
2026-03-07 15:42:19 +01:00
listener = options;
options = {};
}
if (!watchers.has(path)) {
watchers.set(path, new Set());
}
2026-03-24 02:42:31 +01:00
// Wrapper that holds both direct listener and .on() listeners
const entry = {
direct: typeof listener === "function" ? listener : null,
eventListeners: new Map(), // event name -> Set<fn>
call(eventType, filename) {
if (this.direct) {
this.direct(eventType, filename);
}
const fns = this.eventListeners.get("change");
if (fns) {
for (const fn of fns) {
try {
fn(eventType, filename);
} catch (e) {
console.error("[shim:fs:watch] Listener error:", e);
}
}
}
},
};
watchers.get(path).add(entry);
2026-03-07 15:42:19 +01:00
// Return a watcher-like object
return {
close() {
const set = watchers.get(path);
if (set) {
2026-03-24 02:42:31 +01:00
set.delete(entry);
2026-03-07 15:42:19 +01:00
if (set.size === 0) {
watchers.delete(path);
}
}
},
2026-03-24 02:42:31 +01:00
on(event, fn) {
if (!entry.eventListeners.has(event)) {
entry.eventListeners.set(event, new Set());
}
entry.eventListeners.get(event).add(fn);
2026-03-11 22:08:30 +01:00
return this;
},
2026-03-24 02:42:31 +01:00
once(event, fn) {
const wrapped = (...args) => {
this.removeListener(event, wrapped);
fn(...args);
};
return this.on(event, wrapped);
2026-03-11 22:08:30 +01:00
},
2026-03-24 02:42:31 +01:00
removeListener(event, fn) {
const fns = entry.eventListeners.get(event);
if (fns) {
fns.delete(fn);
}
2026-03-11 22:08:30 +01:00
return this;
},
2026-03-07 15:42:19 +01:00
};
},
// Internal: called when transport receives a file-change event
_dispatch(eventType, filePath) {
2026-03-24 02:42:31 +01:00
const normFile = (filePath || "").replace(/^\/+/, "");
let matched = false;
2026-03-07 15:42:19 +01:00
for (const [watchPath, listeners] of watchers) {
2026-03-24 02:42:31 +01:00
const normWatch = (watchPath || "").replace(/^\/+/, "");
// Empty normWatch means root watcher - matches everything
const isMatch =
normWatch === "" ||
normFile === normWatch ||
normFile.startsWith(normWatch + "/");
if (isMatch) {
matched = true;
const relativeName =
normWatch === ""
? normFile
: normFile.slice(normWatch.length + 1) || normFile;
for (const entry of listeners) {
2026-03-07 15:42:19 +01:00
try {
2026-03-24 02:42:31 +01:00
entry.call(eventType, relativeName);
2026-03-07 15:42:19 +01:00
} catch (e) {
2026-03-11 22:08:30 +01:00
console.error("[shim:fs:watch] Listener error:", e);
2026-03-07 15:42:19 +01:00
}
}
}
}
},
};
}