UNPKG

@masuidrive/ticket

Version:

Real-time ticket tracking viewer with Vite + Express

80 lines (68 loc) 2.08 kB
import { watch } from 'chokidar'; import { EventEmitter } from 'events'; import path from 'path'; import { createHash } from 'crypto'; import { readFile } from 'fs/promises'; export class FileWatcher extends EventEmitter { private watcher: any; private projectRoot: string; private fileHashes: Map<string, string> = new Map(); constructor(projectRoot?: string) { super(); this.projectRoot = projectRoot || process.cwd(); } private async getFileHash(filePath: string): Promise<string | null> { try { const content = await readFile(filePath, 'utf-8'); return createHash('sha256').update(content).digest('hex'); } catch (error) { return null; } } watchFile(filePath: string = 'current-ticket.md'): void { const fullPath = path.join(this.projectRoot, filePath); // Get initial hash this.getFileHash(fullPath).then(hash => { if (hash) { this.fileHashes.set(filePath, hash); } }); this.watcher = watch(fullPath, { persistent: true, ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 } }); this.watcher.on('change', async () => { // Check if content actually changed using SHA256 const newHash = await this.getFileHash(fullPath); const oldHash = this.fileHashes.get(filePath); if (newHash && newHash !== oldHash) { this.fileHashes.set(filePath, newHash); this.emit('fileChanged', filePath); } }); this.watcher.on('add', async () => { const hash = await this.getFileHash(fullPath); if (hash) { this.fileHashes.set(filePath, hash); } this.emit('fileAdded', filePath); }); this.watcher.on('unlink', () => { this.fileHashes.delete(filePath); this.emit('fileRemoved', filePath); }); this.watcher.on('error', (error: Error) => { this.emit('error', error); }); } stop(): void { if (this.watcher) { this.watcher.close(); } this.fileHashes.clear(); } }