UNPKG

turbowatch

Version:

Extremely fast file change detector and task orchestrator for Node.js.

119 lines (98 loc) 2.69 kB
/* eslint-disable canonical/filename-match-regex */ import { FileWatchingBackend } from './FileWatchingBackend'; import { glob } from 'glob'; import { type FSWatcher as NativeFSWatcher, watch } from 'node:fs'; import { lstat, realpath } from 'node:fs/promises'; import path from 'node:path'; const findSymlinks = async (project: string) => { const filenames = await glob('./**/*/', { absolute: true, cwd: project, dot: true, follow: false, }); const symlinks: Array<{ realpath: string; symlink: string; }> = []; for (const filename of filenames) { let stats; try { stats = await lstat(filename); } catch { continue; } if (stats.isSymbolicLink()) { let fileRealpath; try { fileRealpath = await realpath(filename); } catch { continue; } if (!symlinks.some((symlink) => symlink.symlink === fileRealpath)) { symlinks.push({ realpath: fileRealpath, symlink: filename, }); } } } return symlinks; }; export class FSWatcher extends FileWatchingBackend { private fsWatchers: NativeFSWatcher[] = []; private closed = false; public constructor(project: string) { super(); // eslint-disable-next-line unicorn/consistent-function-scoping const watchPath = (target: string) => { return watch( target, { encoding: 'utf8', persistent: true, recursive: true, }, (eventType, filename) => { this.emit('change', { filename: path.resolve(target, filename) }); }, ); }; this.fsWatchers.push(watchPath(project)); // TODO detect when a new symlink is added to the project // eslint-disable-next-line promise/prefer-await-to-then findSymlinks(project).then((symlinks) => { if (this.closed) { return; } for (const symlink of symlinks) { this.fsWatchers.push( watch( symlink.realpath, { encoding: 'utf8', persistent: true, recursive: true, }, (eventType, filename) => { const absolutePath = path.resolve(symlink.realpath, filename); this.emit('change', { filename: path.join( symlink.symlink, path.relative(symlink.realpath, absolutePath), ), }); }, ), ); } this.emit('ready'); }); } public async close() { this.closed = true; for (const fsWatcher of this.fsWatchers) { fsWatcher.close(); } } }