All files / bin/watch FSWatcherUtils.ts

97.06% Statements 33/34
50% Branches 1/2
100% Functions 10/10
96.97% Lines 32/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 871x         1x   1x       1x       1x 1x                   1x     1x   1x 1x 1x       1x       1x     1x         1x   2x     2x     1x 2x 2x 2x 2x     1x   2x     2x         1x 2x   2x 2x       1x 1x 3x    
import {
    FSWatcher,
    WatchOptions
} from 'chokidar';
 
export async function createReadyFSWatcher(options?: WatchOptions): Promise<FSWatcher> {
    // Create the watcher instance
    const watcher = new FSWatcher(options);
 
    // Flag to stop keep-alive timer after watcher is ready.
    // Otherwise, Node.JS would never exit.
    let isReady = false;
 
    // Keep-alive timer to stop Node.JS from shutting down before watcher is ready.
    // Apparently the "persistent" property does not kick in until *after* the initial scan / ready event.
    const keepAlive = setTimeout(() => {
        Iif (!isReady) {
            // Refresh the timer if the watcher isn't ready
            keepAlive.refresh();
        }
    }, 10);
 
    // Watch a single file to force the initial scan.
    // If we don't do this here, then the ready event will never fire.
    // But if we don't wait for ready, then the first set of watchers may not bind correctly.
    // This especially causes problems on Mac OS.
    watcher.add(__filename);
 
    // Create a promise bound to the ready event, then wait for it
    await new Promise<void>((resolve, reject) => {
        // Resolve on "ready" event
       watcher.on('ready', () => {
           isReady = true;
           resolve();
       });
 
       // Reject/throw on errors
       watcher.on('error', reject);
    });
 
    // Remove the file we watched earlier to guarantee a clean state for the caller.
    await watcher.unwatch(__filename);
 
    // Return the watcher, which is now ready to use.
    return watcher;
}
 
export function watchPaths(watcher: FSWatcher, paths: readonly string[]): void;
export function watchPaths(watcher: FSWatcher, ...paths: readonly string[]): void;
export function watchPaths(watcher: FSWatcher, ...paths: ReadonlyArray<string | readonly string[]>): void {
    // Flatten paths array, as TS overload syntax allows to pass in a mix of strings and string arrays
    const flatPaths = paths.flat();
 
    // Add to the watcher
    watcher.add(flatPaths);
}
 
export function getAllWatched(watcher: FSWatcher): string[] {
    const watchedObj = watcher.getWatched();
    const watchedDirs = Object.keys(watchedObj);
    const watchedFiles = Object.values(watchedObj).flat();
    return watchedDirs.concat(watchedFiles);
}
 
export function clearAllWatched(watcher: FSWatcher): void {
    // Get all paths to stop watching
    const watched = getAllWatched(watcher);
 
    // Stop watching
    watcher.unwatch(watched);
}
 
export function setWatched(watcher: FSWatcher, paths: readonly string[]): void;
export function setWatched(watcher: FSWatcher, ...paths: readonly string[]): void;
export function setWatched(watcher: FSWatcher, ...paths: ReadonlyArray<string | readonly string[]>): void {
    const allPaths = paths.flat();
 
    clearAllWatched(watcher);
    watchPaths(watcher, allPaths);
}
 
export type MultiWatchEvent = 'add'|'addDir'|'change'|'unlink'|'unlinkDir';
export function onAny(watcher: FSWatcher, events: readonly MultiWatchEvent[], callback: (event: MultiWatchEvent, path: string) => void): void {
    for (const event of events) {
        watcher.on(event, (path) => callback(event, path));
}
}