diff --git a/lib/DirectoryWatcher.js b/lib/DirectoryWatcher.js index f18dbef..f5861af 100644 --- a/lib/DirectoryWatcher.js +++ b/lib/DirectoryWatcher.js @@ -39,7 +39,6 @@ class Watcher extends EventEmitter { this.directoryWatcher = directoryWatcher; this.path = filePath; this.startTime = startTime && +startTime; - this._cachedTimeInfoEntries = undefined; } checkStartTime(mtime, initial) { @@ -136,8 +135,6 @@ class DirectoryWatcher extends EventEmitter { } setMissing(itemPath, initial, type) { - this._cachedTimeInfoEntries = undefined; - if (this.initialScan) { this.initialScanRemoved.add(itemPath); } @@ -206,7 +203,6 @@ class DirectoryWatcher extends EventEmitter { accuracy, timestamp: mtime }); - this._cachedTimeInfoEntries = undefined; if (!old) { const key = withoutCase(filePath); @@ -249,7 +245,6 @@ class DirectoryWatcher extends EventEmitter { if (!old) { const now = Date.now(); - this._cachedTimeInfoEntries = undefined; if (this.nestedWatching) { this.createNestedWatcher(directoryPath); } else { @@ -280,7 +275,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); @@ -293,7 +287,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); @@ -431,7 +424,6 @@ class DirectoryWatcher extends EventEmitter { } } this.lastWatchEvent = Date.now(); - this._cachedTimeInfoEntries = undefined; if (!stats) { this.setMissing(filePath, false, eventType); } else if (stats.isDirectory()) { @@ -714,48 +706,41 @@ class DirectoryWatcher extends EventEmitter { return obj; } - getTimeInfoEntries() { - if (this._cachedTimeInfoEntries !== undefined) - return this._cachedTimeInfoEntries; - const map = new Map(); - let safeTime = this.lastWatchEvent; + getTimeInfoEntries(fileTimestamps, directoryTimestamps, safeTime) { + safeTime.value = Math.max(safeTime.value, this.lastWatchEvent); for (const [file, entry] of this.files) { fixupEntryAccuracy(entry); - safeTime = Math.max(safeTime, entry.safeTime); - map.set(file, entry); + safeTime.value = Math.max(safeTime.value, entry.safeTime); + 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); - } + w.directoryWatcher.getTimeInfoEntries( + fileTimestamps, + directoryTimestamps, + safeTime + ); } - map.set(this.path, { - safeTime + directoryTimestamps.set(this.path, { + safeTime: safeTime.value }); } 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); + 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; } close() { diff --git a/lib/watchpack.js b/lib/watchpack.js index 9f1dca1..bc502fa 100644 --- a/lib/watchpack.js +++ b/lib/watchpack.js @@ -275,29 +275,18 @@ class Watchpack extends EventEmitter { return obj; } - getTimeInfoEntries() { - if (EXISTANCE_ONLY_TIME_ENTRY === undefined) { - EXISTANCE_ONLY_TIME_ENTRY = require("./DirectoryWatcher") - .EXISTANCE_ONLY_TIME_ENTRY; - } - const directoryWatchers = new Set(); - addWatchersToSet(this.fileWatchers.values(), directoryWatchers); - addWatchersToSet(this.directoryWatchers.values(), directoryWatchers); + getTimeInfoEntries(fileTimestamps, directoryTimestamps) { + const allWatchers = new Set(); + addWatchersToSet(this.fileWatchers.values(), allWatchers); + addWatchersToSet(this.directoryWatchers.values(), allWatchers); 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); - } + // if timestamp maps are passed in, populate them, otherwise return a new map with both file and directory timestamps. + if (!fileTimestamps && !directoryTimestamps) { + fileTimestamps = directoryTimestamps = map; + } + const safeTime = { value: 0 }; + for (const w of allWatchers) { + w.getTimeInfoEntries(fileTimestamps, directoryTimestamps, safeTime); } return map; } diff --git a/test/DirectoryWatcher.test.js b/test/DirectoryWatcher.js similarity index 100% rename from test/DirectoryWatcher.test.js rename to test/DirectoryWatcher.js diff --git a/test/Watchpack.js b/test/Watchpack.js index ade8021..48f5438 100644 --- a/test/Watchpack.js +++ b/test/Watchpack.js @@ -524,6 +524,44 @@ 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(); + const times = w.getTimeInfoEntries(); + w.getTimeInfoEntries(files, directories); + // aggregated results should be the same as seperated maps + Array.from(times).sort().should.be.eql([...Array.from(files), ...Array.from(directories)].sort()) + const dir = directories.get(path.join(fixtures, "dir")); + const sub = directories.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"); + 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.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