Files
talk/scripts/watcher/ChokidarWatcher.ts
T
2018-07-13 11:26:19 -03:00

97 lines
2.6 KiB
TypeScript

import chokidar from "chokidar";
import path from "path";
import { Watcher, WatchOptions } from "./types";
function prependRootDir(
prepend: string,
paths: ReadonlyArray<string>
): string[] {
const prependFunc = (p: string) => path.resolve(prepend, p);
return paths.map(prependFunc);
}
export default class ChokidarWatcher implements Watcher {
public watch(
rootDir: string,
paths: ReadonlyArray<string>,
options: WatchOptions = {}
): AsyncIterable<string> {
const resolvedPaths = prependRootDir(rootDir, paths);
// An array to hold all changes, that has not yet been yield.
const queue: string[] = [];
let firstError: Error | null = null;
// If this is set, a pending promise is waiting for the next result.
let pending:
| ({ resolve: (result: string) => void; reject: (error: Error) => void })
| null = null;
// Only start client if we have something to watch.
if (paths.length) {
const client = chokidar.watch(resolvedPaths, {
ignored: options.ignore && prependRootDir(rootDir, options.ignore),
});
// Listen for errors
client.on("error", (error: Error) => {
// Resolve pending request.
if (pending) {
pending.reject(error);
pending = null;
return;
}
if (!firstError) {
firstError = error;
}
});
// Listen for changes
client.on("change", (pathFile: string) => {
// Resolve pending request.
if (pending) {
pending.resolve(pathFile);
pending = null;
return;
}
// There is no pending request, save it into the queue.
queue.unshift(pathFile);
});
}
return {
[Symbol.asyncIterator]() {
return {
next: () =>
new Promise<IteratorResult<string>>((resolve, reject) => {
const wrapped = {
resolve: (pathFile: string) =>
resolve({
done: false,
value: pathFile,
}),
reject: (error: Error) =>
reject({
done: true,
value: error,
}),
};
// We already have a change to return
if (firstError) {
wrapped.reject(firstError);
return;
}
if (queue.length) {
wrapped.resolve(queue.pop()!);
return;
}
// We need to wait for the next change event.
pending = wrapped;
}),
};
},
};
}
}