This commit is contained in:
141
src/utils/watcher.ts
Normal file
141
src/utils/watcher.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import chokidar, { FSWatcher, WatchOptions } from 'chokidar';
|
||||
import * as path from 'path';
|
||||
|
||||
export interface WatcherOptions extends WatchOptions {
|
||||
debounceMs?: number;
|
||||
maxRetries?: number;
|
||||
}
|
||||
|
||||
export interface FileChangeEvent {
|
||||
type: 'add' | 'change' | 'unlink';
|
||||
path: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export type WatcherEventHandler = (event: FileChangeEvent) => void;
|
||||
|
||||
export class FileWatcher {
|
||||
private watcher: FSWatcher | null = null;
|
||||
private handlers: Map<string, Set<WatcherEventHandler>> = new Map();
|
||||
private options: WatcherOptions;
|
||||
private isRunning: boolean = false;
|
||||
|
||||
constructor(options: WatcherOptions = {}) {
|
||||
this.options = {
|
||||
debounceMs: options.debounceMs ?? 100,
|
||||
maxRetries: options.maxRetries ?? 3,
|
||||
persistent: options.persistent ?? true,
|
||||
ignoreInitial: options.ignoreInitial ?? false,
|
||||
awaitWriteFinish: options.awaitWriteFinish ?? {
|
||||
stabilityThreshold: 100,
|
||||
pollInterval: 50
|
||||
},
|
||||
...options
|
||||
};
|
||||
}
|
||||
|
||||
watch(paths: string | string[], patterns?: string[]): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const watchPaths = Array.isArray(paths) ? paths : [paths];
|
||||
|
||||
this.watcher = chokidar.watch(watchPaths, {
|
||||
...this.options,
|
||||
ignored: patterns
|
||||
});
|
||||
|
||||
this.watcher
|
||||
.on('ready', () => {
|
||||
this.isRunning = true;
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => {
|
||||
this.isRunning = false;
|
||||
reject(error);
|
||||
})
|
||||
.on('add', (filePath) => {
|
||||
this.emit('add', filePath);
|
||||
})
|
||||
.on('change', (filePath) => {
|
||||
this.emit('change', filePath);
|
||||
})
|
||||
.on('unlink', (filePath) => {
|
||||
this.emit('unlink', filePath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
on(eventType: string, handler: WatcherEventHandler): void {
|
||||
if (!this.handlers.has(eventType)) {
|
||||
this.handlers.set(eventType, new Set());
|
||||
}
|
||||
this.handlers.get(eventType)!.add(handler);
|
||||
}
|
||||
|
||||
off(eventType: string, handler: WatcherEventHandler): void {
|
||||
const eventHandlers = this.handlers.get(eventType);
|
||||
if (eventHandlers) {
|
||||
eventHandlers.delete(handler);
|
||||
}
|
||||
}
|
||||
|
||||
private emit(eventType: string, filePath: string): void {
|
||||
const event: FileChangeEvent = {
|
||||
type: eventType as 'add' | 'change' | 'unlink',
|
||||
path: path.resolve(filePath),
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
const handlers = this.handlers.get(eventType);
|
||||
if (handlers) {
|
||||
for (const handler of handlers) {
|
||||
try {
|
||||
handler(event);
|
||||
} catch (error) {
|
||||
console.error(`Error in watcher handler for ${eventType}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allHandlers = this.handlers.get('*');
|
||||
if (allHandlers) {
|
||||
for (const handler of allHandlers) {
|
||||
try {
|
||||
handler(event);
|
||||
} catch (error) {
|
||||
console.error('Error in wildcard watcher handler:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getWatchedFiles(): string[] {
|
||||
if (!this.watcher) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(this.watcher.getWatched()).flat();
|
||||
}
|
||||
|
||||
add(filePath: string): void {
|
||||
this.watcher?.add(filePath);
|
||||
}
|
||||
|
||||
unwatch(filePath: string): void {
|
||||
this.watcher?.unwatch(filePath);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.watcher) {
|
||||
this.watcher.close();
|
||||
this.watcher = null;
|
||||
this.isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.isRunning;
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileWatcher(options?: WatcherOptions): FileWatcher {
|
||||
return new FileWatcher(options);
|
||||
}
|
||||
Reference in New Issue
Block a user