Merge branch 'release-2.5' into feature/recursive-file-watchers

This commit is contained in:
Jan Dvorak
2021-10-15 20:32:11 +02:00
2 changed files with 37 additions and 46 deletions

View File

@@ -1820,7 +1820,7 @@ export const watchFile = wrapFsFunc("watchFile", (
export const unwatchFile = wrapFsFunc("unwatchFile", (
filename: string,
listener: StatListener,
listener?: StatListener,
) => {
return fs.unwatchFile(filename, listener);
}, [0]);

View File

@@ -2,7 +2,6 @@ import { FSWatcher, Stats, BigIntStats } from "fs";
import { Profile } from "../tool-env/profile";
import {
statOrNull,
pathResolve,
convertToOSPath,
watchFile,
unwatchFile,
@@ -307,63 +306,55 @@ function statWatch(
interval: number,
callback: (current: Stats, previous: Stats) => void,
) {
const oldWatcher = statWatchers[absPath];
let statWatcher = statWatchers[absPath];
while (oldWatcher) {
// Make sure this callback no longer appears among the listeners for
// this StatWatcher.
const countBefore = oldWatcher.stat.listenerCount("change");
// This removes at most one occurrence of the callback from the
// listeners list...
oldWatcher.stat.removeListener("change", callback);
// ... so we have to keep calling it until the first time
// it removes nothing.
if (oldWatcher.stat.listenerCount("change") === countBefore) {
break;
}
if (!statWatcher) {
statWatcher = {
interval,
changeListeners: [],
stat: null
};
statWatchers[absPath] = statWatcher;
}
// This doesn't actually call newStat.start again if there's already a
// watcher for this file, so it won't change any interval previously
// specified. In the rare event that the interval needs to change, we
// manually stop and restart the StatWatcher below.
const newStat = watchFile(absPath, {
persistent: false, // never persistent
interval,
}, callback);
// If the interval needs to be changed, replace the watcher.
// Node will only recreate the watcher with the new interval if all old
// watchers are stopped (which unwatchFile does when not passed a
// specific listener)
if (statWatcher.interval !== interval && statWatcher.stat) {
// This stops all stat watchers for the file, not just those created by
// statWatch
unwatchFile(absPath);
statWatcher.stat = null;
statWatcher.interval = interval;
}
if (! oldWatcher) {
const newWatcher = {
stat: newStat,
if (!statWatcher.changeListeners.includes(callback)) {
statWatcher.changeListeners.push(callback);
}
if (!statWatcher.stat) {
const newStat = watchFile(absPath, {
persistent: false, // never persistent
interval,
};
}, (newStat, oldStat) => {
statWatcher.changeListeners.forEach((
listener: (newStat: Stats, oldStat: Stats) => void
) => {
listener(newStat, oldStat);
});
});
newStat.on("stop", () => {
if (statWatchers[absPath] === newWatcher) {
if (statWatchers[absPath] === statWatch) {
delete statWatchers[absPath];
}
});
return statWatchers[absPath] = newWatcher;
statWatcher.stat = newStat;
}
// These should be identical at this point, but just in case.
oldWatcher.stat = newStat;
// If the interval needs to be changed, manually stop and restart the
// StatWatcher using lower-level methods than unwatchFile and watchFile.
if (oldWatcher.interval !== interval) {
oldWatcher.stat.stop();
oldWatcher.stat.start(
convertToOSPath(pathResolve(absPath)),
false, // never persistent
oldWatcher.interval = interval,
);
}
return oldWatcher;
return statWatcher;
}
function watchLibraryWatch(absPath: string, callback: EntryCallback) {