Skip to content

Commit

Permalink
Merge pull request #205 from markjm/markjm/split
Browse files Browse the repository at this point in the history
Support splitting of files and directories in getTimeInfoEntries
  • Loading branch information
sokra committed Nov 24, 2021
2 parents f1f3586 + 8d14e94 commit 89d5f48
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 74 deletions.
11 changes: 8 additions & 3 deletions README.md
Expand Up @@ -114,14 +114,19 @@ const { changes, removals } = wp.getAggregated();
// when futher changes happen
// Can also be used when paused.

// Watchpack.prototype.getTimeInfoEntries()
var fileTimes = wp.getTimeInfoEntries();
// returns a Map with all known time info objects for files and directories
// Watchpack.prototype.collectTimeInfoEntries(fileInfoEntries: Map<string, Entry>, directoryInfoEntries: Map<string, Entry>)
wp.collectTimeInfoEntries(fileInfoEntries, directoryInfoEntries);
// collects time info objects for all known files and directories
// this include info from files not directly watched
// key: absolute path, value: object with { safeTime, timestamp }
// safeTime: a point in time at which it is safe to say all changes happened before that
// timestamp: only for files, the mtime timestamp of the file

// Watchpack.prototype.getTimeInfoEntries()
var fileTimes = wp.getTimeInfoEntries();
// returns a Map with all known time info objects for files and directories
// similar to collectTimeInfoEntries but returns a single map with all entries

// (deprecated)
// Watchpack.prototype.getTimes()
var fileTimes = wp.getTimes();
Expand Down
45 changes: 18 additions & 27 deletions lib/DirectoryWatcher.js
Expand Up @@ -39,7 +39,6 @@ class Watcher extends EventEmitter {
this.directoryWatcher = directoryWatcher;
this.path = filePath;
this.startTime = startTime && +startTime;
this._cachedTimeInfoEntries = undefined;
}

checkStartTime(mtime, initial) {
Expand Down Expand Up @@ -130,8 +129,6 @@ class DirectoryWatcher extends EventEmitter {
}

setMissing(itemPath, initial, type) {
this._cachedTimeInfoEntries = undefined;

if (this.initialScan) {
this.initialScanRemoved.add(itemPath);
}
Expand Down Expand Up @@ -200,7 +197,6 @@ class DirectoryWatcher extends EventEmitter {
accuracy,
timestamp: mtime
});
this._cachedTimeInfoEntries = undefined;

if (!old) {
const key = withoutCase(filePath);
Expand Down Expand Up @@ -243,7 +239,6 @@ class DirectoryWatcher extends EventEmitter {
if (!old) {
const now = Date.now();

this._cachedTimeInfoEntries = undefined;
if (this.nestedWatching) {
this.createNestedWatcher(directoryPath);
} else {
Expand Down Expand Up @@ -274,7 +269,6 @@ class DirectoryWatcher extends EventEmitter {
createNestedWatcher(directoryPath) {
const watcher = this.watcherManager.watchDirectory(directoryPath, 1);
watcher.on("change", (filePath, mtime, type, initial) => {
this._cachedTimeInfoEntries = undefined;
this.forEachWatcher(this.path, w => {
if (!initial || w.checkStartTime(mtime, initial)) {
w.emit("change", filePath, mtime, type, initial);
Expand All @@ -287,7 +281,6 @@ class DirectoryWatcher extends EventEmitter {
setNestedWatching(flag) {
if (this.nestedWatching !== !!flag) {
this.nestedWatching = !!flag;
this._cachedTimeInfoEntries = undefined;
if (this.nestedWatching) {
for (const directory of this.directories.keys()) {
this.createNestedWatcher(directory);
Expand Down Expand Up @@ -425,7 +418,6 @@ class DirectoryWatcher extends EventEmitter {
}
}
this.lastWatchEvent = Date.now();
this._cachedTimeInfoEntries = undefined;
if (!stats) {
this.setMissing(filePath, false, eventType);
} else if (stats.isDirectory()) {
Expand Down Expand Up @@ -709,48 +701,47 @@ class DirectoryWatcher extends EventEmitter {
return obj;
}

getTimeInfoEntries() {
if (this._cachedTimeInfoEntries !== undefined)
return this._cachedTimeInfoEntries;
const map = new Map();
collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
let safeTime = this.lastWatchEvent;
for (const [file, entry] of this.files) {
fixupEntryAccuracy(entry);
safeTime = Math.max(safeTime, entry.safeTime);
map.set(file, entry);
fileTimestamps.set(file, entry);
}
if (this.nestedWatching) {
for (const w of this.directories.values()) {
const timeInfoEntries = w.directoryWatcher.getTimeInfoEntries();
for (const [file, entry] of timeInfoEntries) {
if (entry) {
safeTime = Math.max(safeTime, entry.safeTime);
}
map.set(file, entry);
}
safeTime = Math.max(
safeTime,
w.directoryWatcher.collectTimeInfoEntries(
fileTimestamps,
directoryTimestamps,
safeTime
)
);
}
map.set(this.path, {
fileTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
directoryTimestamps.set(this.path, {
safeTime
});
} else {
for (const dir of this.directories.keys()) {
// No additional info about this directory
map.set(dir, EXISTANCE_ONLY_TIME_ENTRY);
directoryTimestamps.set(dir, EXISTANCE_ONLY_TIME_ENTRY);
}
map.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
fileTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
directoryTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
}
if (!this.initialScan) {
for (const watchers of this.watchers.values()) {
for (const watcher of watchers) {
const path = watcher.path;
if (!map.has(path)) {
map.set(path, null);
if (!directoryTimestamps.has(path)) {
directoryTimestamps.set(path, null);
}
}
}
this._cachedTimeInfoEntries = map;
}
return map;
return safeTime;
}

close() {
Expand Down
57 changes: 14 additions & 43 deletions lib/watchpack.js
Expand Up @@ -10,26 +10,14 @@ const EventEmitter = require("events").EventEmitter;
const globToRegExp = require("glob-to-regexp");
const watchEventSource = require("./watchEventSource");

let EXISTANCE_ONLY_TIME_ENTRY; // lazy required

const EMPTY_ARRAY = [];
const EMPTY_OPTIONS = {};

function addWatchpackWatchersToSet(watchers, set) {
function addWatchersToSet(watchers, set) {
for (const ww of watchers) {
const w = ww.watcher;
if (!set.has(w.directoryWatcher)) {
set.add(w.directoryWatcher);
addWatchersToSet(w.directoryWatcher.directories.values(), set);
}
}
}

function addWatchersToSet(watchers, set) {
for (const w of watchers) {
if (w !== true && !set.has(w.directoryWatcher)) {
set.add(w.directoryWatcher);
addWatchersToSet(w.directoryWatcher.directories.values(), set);
}
}
}
Expand Down Expand Up @@ -324,11 +312,8 @@ class Watchpack extends EventEmitter {

getTimes() {
const directoryWatchers = new Set();
addWatchpackWatchersToSet(this.fileWatchers.values(), directoryWatchers);
addWatchpackWatchersToSet(
this.directoryWatchers.values(),
directoryWatchers
);
addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
const obj = Object.create(null);
for (const w of directoryWatchers) {
const times = w.getTimes();
Expand All @@ -338,35 +323,21 @@ class Watchpack extends EventEmitter {
}

getTimeInfoEntries() {
if (EXISTANCE_ONLY_TIME_ENTRY === undefined) {
EXISTANCE_ONLY_TIME_ENTRY = require("./DirectoryWatcher")
.EXISTANCE_ONLY_TIME_ENTRY;
}
const directoryWatchers = new Set();
addWatchpackWatchersToSet(this.fileWatchers.values(), directoryWatchers);
addWatchpackWatchersToSet(
this.directoryWatchers.values(),
directoryWatchers
);
const map = new Map();
for (const w of directoryWatchers) {
const times = w.getTimeInfoEntries();
for (const [path, entry] of times) {
if (map.has(path)) {
if (entry === EXISTANCE_ONLY_TIME_ENTRY) continue;
const value = map.get(path);
if (value === entry) continue;
if (value !== EXISTANCE_ONLY_TIME_ENTRY) {
map.set(path, Object.assign({}, value, entry));
continue;
}
}
map.set(path, entry);
}
}
this.collectTimeInfoEntries(map, map);
return map;
}

collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
const allWatchers = new Set();
addWatchersToSet(this.fileWatchers.values(), allWatchers);
addWatchersToSet(this.directoryWatchers.values(), allWatchers);
const safeTime = { value: 0 };
for (const w of allWatchers) {
w.collectTimeInfoEntries(fileTimestamps, directoryTimestamps, safeTime);
}
}

getAggregated() {
if (this.aggregateTimer) {
clearTimeout(this.aggregateTimer);
Expand Down
File renamed without changes.
49 changes: 48 additions & 1 deletion test/Watchpack.js
Expand Up @@ -121,7 +121,7 @@ describe("Watchpack", function() {
it("should not watch a single ignored file (function)", function(done) {
var w = new Watchpack({
aggregateTimeout: 300,
ignored: (entry) => entry.includes("a")
ignored: entry => entry.includes("a")
});
var changeEvents = 0;
var aggregatedEvents = 0;
Expand Down Expand Up @@ -550,6 +550,53 @@ describe("Watchpack", function() {
});
});

it("should watch file in a sub directory (passed in maps)", function(done) {
var w = new Watchpack({
aggregateTimeout: 1000
});
var changeEvents = [];
w.on("change", function(file) {
if (changeEvents[changeEvents.length - 1] === file) return;
changeEvents.push(file);
});
w.on("aggregated", function(changes) {
Array.from(changes).should.be.eql([path.join(fixtures, "dir")]);
changeEvents.should.be.eql([path.join(fixtures, "dir", "sub", "a")]);
const files = new Map();
const directories = new Map();
w.collectTimeInfoEntries(files, directories);
const dir = directories.get(path.join(fixtures, "dir"));
const dirAsFile = files.get(path.join(fixtures, "dir"));
const sub = directories.get(path.join(fixtures, "dir", "sub"));
const subAsFile = files.get(path.join(fixtures, "dir", "sub"));
const a = files.get(path.join(fixtures, "dir", "sub", "a"));
dir.should.be.type("object");
dir.should.have.property("safeTime");
dirAsFile.should.be.type("object");
dirAsFile.should.not.have.property("safeTime");
sub.should.be.type("object");
sub.should.have.property("safeTime");
subAsFile.should.be.type("object");
subAsFile.should.not.have.property("safeTime");
a.should.be.type("object");
a.should.have.property("safeTime");
a.should.have.property("timestamp");
sub.safeTime.should.be.aboveOrEqual(a.safeTime);
dir.safeTime.should.be.aboveOrEqual(sub.safeTime);
w.close();
done();
});
testHelper.dir("dir");
testHelper.dir(path.join("dir", "sub"));
testHelper.dir(path.join("dir", "sub2"));
testHelper.tick(function() {
w.watch([], [path.join(fixtures, "dir")]);
testHelper.tick(function() {
testHelper.file(path.join("dir", "sub", "a"));
});
});
});

it("should watch 2 files in a not-existing directory", function(done) {
var w = new Watchpack({
aggregateTimeout: 1000
Expand Down

0 comments on commit 89d5f48

Please sign in to comment.