Skip to content

Commit

Permalink
Fixed watchers not supporting contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
james-pre committed Dec 23, 2024
1 parent 468a43c commit 1d5e724
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/emulation/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ export function watchFile(
return;
}

const watcher = new StatWatcher(normalizedPath, opts);
const watcher = new StatWatcher(this, normalizedPath, opts);
watcher.on('change', (curr: Stats, prev: Stats) => {
const entry = statWatchers.get(normalizedPath);
if (!entry) {
Expand Down Expand Up @@ -639,7 +639,7 @@ export function watch(
options?: fs.WatchOptions | ((event: string, filename: string) => any),
listener?: (event: string, filename: string) => any
): FSWatcher {
const watcher = new FSWatcher<string>(normalizePath(path), typeof options == 'object' ? options : {});
const watcher = new FSWatcher<string>(this, normalizePath(path), typeof options == 'object' ? options : {});
listener = typeof options == 'function' ? options : listener;
watcher.on('change', listener || nop);
return watcher;
Expand Down
14 changes: 9 additions & 5 deletions src/emulation/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,13 +943,17 @@ export async function realpath(this: V_Context, path: fs.PathLike, options?: fs.
}
realpath satisfies typeof promises.realpath;

export function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<promises.FileChangeInfo<string>>;
export function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<promises.FileChangeInfo<Buffer>>;
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<promises.FileChangeInfo<string>> | AsyncIterable<promises.FileChangeInfo<Buffer>>;
export function watch<T extends string | Buffer>(filename: fs.PathLike, options: fs.WatchOptions | string = {}): AsyncIterable<promises.FileChangeInfo<T>> {
export function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<promises.FileChangeInfo<string>>;
export function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<promises.FileChangeInfo<Buffer>>;
export function watch(
this: V_Context,
filename: fs.PathLike,
options?: fs.WatchOptions | string
): AsyncIterable<promises.FileChangeInfo<string>> | AsyncIterable<promises.FileChangeInfo<Buffer>>;
export function watch<T extends string | Buffer>(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | string = {}): AsyncIterable<promises.FileChangeInfo<T>> {
return {
[Symbol.asyncIterator](): AsyncIterator<promises.FileChangeInfo<T>> {
const watcher = new FSWatcher<T>(filename.toString(), typeof options !== 'string' ? options : { encoding: options as BufferEncoding | 'buffer' });
const watcher = new FSWatcher<T>(this, filename.toString(), typeof options !== 'string' ? options : { encoding: options as BufferEncoding | 'buffer' });

// A queue to hold change events, since we need to resolve them in the async iterator
const eventQueue: ((value: IteratorResult<promises.FileChangeInfo<T>>) => void)[] = [];
Expand Down
29 changes: 22 additions & 7 deletions src/emulation/watchers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EventEmitter } from 'eventemitter3';
import type { EventEmitter as NodeEventEmitter } from 'node:events';
import type * as fs from 'node:fs';
import type { V_Context } from '../context.js';
import { ErrnoError } from '../error.js';
import { isStatsEqual, type Stats } from '../stats.js';
import { normalizePath } from '../utils.js';
Expand All @@ -24,7 +25,13 @@ class Watcher<TEvents extends Record<string, unknown[]> = Record<string, unknown
}
/* eslint-enable @typescript-eslint/no-explicit-any */

public constructor(public readonly path: string) {
public constructor(
/**
* @internal
*/
public readonly _context: V_Context,
public readonly path: string
) {
super();
}

Expand Down Expand Up @@ -71,10 +78,11 @@ export class FSWatcher<T extends string | Buffer = string | Buffer>
implements fs.FSWatcher
{
public constructor(
context: V_Context,
path: string,
public readonly options: fs.WatchOptions
) {
super(path);
super(context, path);
addWatcher(path.toString(), this);
}

Expand Down Expand Up @@ -105,10 +113,11 @@ export class StatWatcher
private previous?: Stats;

public constructor(
context: V_Context,
path: string,
private options: { persistent?: boolean; interval?: number }
) {
super(path);
super(context, path);
this.start();
}

Expand Down Expand Up @@ -185,10 +194,16 @@ export function emitChange(eventType: fs.WatchEventType, filename: string) {
while (parent !== normalizedFilename) {
normalizedFilename = parent;
parent = dirname(parent);
if (watchers.has(parent)) {
for (const watcher of watchers.get(parent)!) {
watcher.emit('change', eventType, filename.slice(parent.length + (parent == '/' ? 0 : 1)));
}
if (!watchers.has(parent)) continue;

for (const watcher of watchers.get(parent)!) {
// Strip the context root from the path if the watcher has a context

const root = watcher._context?.root;
const contextPath = root && filename.startsWith(root) ? filename.slice(root.length) : filename;
const relativePath = contextPath.slice(parent.length + (parent == '/' ? 0 : 1));

watcher.emit('change', eventType, relativePath);
}
}
}

0 comments on commit 1d5e724

Please sign in to comment.