watchpack
Advanced tools
Comparing version 2.2.0 to 2.3.0
@@ -42,3 +42,2 @@ /* | ||
this.startTime = startTime && +startTime; | ||
this._cachedTimeInfoEntries = undefined; | ||
} | ||
@@ -75,3 +74,3 @@ | ||
this.initialScan = true; | ||
this.ignored = options.ignored; | ||
this.ignored = options.ignored || (() => false); | ||
this.nestedWatching = false; | ||
@@ -101,8 +100,2 @@ this.polledWatching = | ||
checkIgnore(path) { | ||
if (!this.ignored) return false; | ||
path = path.replace(/\\/g, "/"); | ||
return this.ignored.test(path); | ||
} | ||
createWatcher() { | ||
@@ -142,4 +135,2 @@ try { | ||
setMissing(itemPath, initial, type) { | ||
this._cachedTimeInfoEntries = undefined; | ||
if (this.initialScan) { | ||
@@ -185,3 +176,3 @@ this.initialScanRemoved.add(itemPath); | ||
if (this.checkIgnore(filePath)) return; | ||
if (this.ignored(filePath)) return; | ||
@@ -214,3 +205,2 @@ const old = this.files.get(filePath); | ||
}); | ||
this._cachedTimeInfoEntries = undefined; | ||
@@ -246,3 +236,3 @@ if (!old) { | ||
setDirectory(directoryPath, birthtime, initial, type) { | ||
if (this.checkIgnore(directoryPath)) return; | ||
if (this.ignored(directoryPath)) return; | ||
if (directoryPath === this.path) { | ||
@@ -259,3 +249,2 @@ if (!initial) { | ||
this._cachedTimeInfoEntries = undefined; | ||
if (this.nestedWatching) { | ||
@@ -291,3 +280,2 @@ this.createNestedWatcher(directoryPath); | ||
watcher.on("change", (filePath, mtime, type, initial) => { | ||
this._cachedTimeInfoEntries = undefined; | ||
this.forEachWatcher(this.path, w => { | ||
@@ -305,3 +293,2 @@ if (!initial || w.checkStartTime(mtime, initial)) { | ||
this.nestedWatching = !!flag; | ||
this._cachedTimeInfoEntries = undefined; | ||
if (this.nestedWatching) { | ||
@@ -411,3 +398,3 @@ for (const directory of this.directories.keys()) { | ||
const filePath = path.join(this.path, filename); | ||
if (this.checkIgnore(filePath)) return; | ||
if (this.ignored(filePath)) return; | ||
@@ -445,3 +432,2 @@ if (this._activeEvents.get(filename) === undefined) { | ||
this.lastWatchEvent = Date.now(); | ||
this._cachedTimeInfoEntries = undefined; | ||
if (!stats) { | ||
@@ -660,2 +646,3 @@ this.setMissing(filePath, false, eventType); | ||
err2.code === "EPERM" || | ||
err2.code === "EACCES" || | ||
err2.code === "EBUSY" | ||
@@ -730,6 +717,3 @@ ) { | ||
getTimeInfoEntries() { | ||
if (this._cachedTimeInfoEntries !== undefined) | ||
return this._cachedTimeInfoEntries; | ||
const map = new Map(); | ||
collectTimeInfoEntries(fileTimestamps, directoryTimestamps) { | ||
let safeTime = this.lastWatchEvent; | ||
@@ -739,15 +723,17 @@ for (const [file, entry] of this.files) { | ||
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 | ||
@@ -758,5 +744,6 @@ }); | ||
// 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); | ||
} | ||
@@ -767,10 +754,9 @@ if (!this.initialScan) { | ||
const path = watcher.path; | ||
if (!map.has(path)) { | ||
map.set(path, null); | ||
if (!fileTimestamps.has(path)) { | ||
fileTimestamps.set(path, null); | ||
} | ||
} | ||
} | ||
this._cachedTimeInfoEntries = map; | ||
} | ||
return map; | ||
return safeTime; | ||
} | ||
@@ -777,0 +763,0 @@ |
@@ -13,4 +13,2 @@ /* | ||
let EXISTANCE_ONLY_TIME_ENTRY; // lazy required | ||
const EMPTY_ARRAY = []; | ||
@@ -20,6 +18,6 @@ const EMPTY_OPTIONS = {}; | ||
function addWatchersToSet(watchers, set) { | ||
for (const w of watchers) { | ||
if (w !== true && !set.has(w.directoryWatcher)) { | ||
for (const ww of watchers) { | ||
const w = ww.watcher; | ||
if (!set.has(w.directoryWatcher)) { | ||
set.add(w.directoryWatcher); | ||
addWatchersToSet(w.directoryWatcher.directories.values(), set); | ||
} | ||
@@ -36,8 +34,12 @@ } | ||
const ignoredToRegexp = ignored => { | ||
const ignoredToFunction = ignored => { | ||
if (Array.isArray(ignored)) { | ||
return new RegExp(ignored.map(i => stringToRegexp(i)).join("|")); | ||
const regexp = new RegExp(ignored.map(i => stringToRegexp(i)).join("|")); | ||
return x => regexp.test(x.replace(/\\/g, "/")); | ||
} else if (typeof ignored === "string") { | ||
return new RegExp(stringToRegexp(ignored)); | ||
const regexp = new RegExp(stringToRegexp(ignored)); | ||
return x => regexp.test(x.replace(/\\/g, "/")); | ||
} else if (ignored instanceof RegExp) { | ||
return x => ignored.test(x.replace(/\\/g, "/")); | ||
} else if (ignored instanceof Function) { | ||
return ignored; | ||
@@ -47,3 +49,3 @@ } else if (ignored) { | ||
} else { | ||
return undefined; | ||
return () => false; | ||
} | ||
@@ -55,3 +57,3 @@ }; | ||
followSymlinks: !!options.followSymlinks, | ||
ignored: ignoredToRegexp(options.ignored), | ||
ignored: ignoredToFunction(options.ignored), | ||
poll: options.poll | ||
@@ -70,2 +72,81 @@ }; | ||
class WatchpackFileWatcher { | ||
constructor(watchpack, watcher, files) { | ||
if (!watcher) throw new Error(); | ||
this.files = Array.isArray(files) ? files : [files]; | ||
this.watcher = watcher; | ||
watcher.on("initial-missing", type => { | ||
for (const file of this.files) { | ||
if (!watchpack._missing.has(file)) | ||
watchpack._onRemove(file, file, type); | ||
} | ||
}); | ||
watcher.on("change", (mtime, type) => { | ||
for (const file of this.files) { | ||
watchpack._onChange(file, mtime, file, type); | ||
} | ||
}); | ||
watcher.on("remove", type => { | ||
for (const file of this.files) { | ||
watchpack._onRemove(file, file, type); | ||
} | ||
}); | ||
} | ||
update(files) { | ||
if (!Array.isArray(files)) { | ||
if (this.files.length !== 1) { | ||
this.files = [files]; | ||
} else if (this.files[0] !== files) { | ||
this.files[0] = files; | ||
} | ||
} else { | ||
this.files = files; | ||
} | ||
} | ||
close() { | ||
this.watcher.close(); | ||
} | ||
} | ||
class WatchpackDirectoryWatcher { | ||
constructor(watchpack, watcher, directories) { | ||
if (!watcher) throw new Error(); | ||
this.directories = Array.isArray(directories) ? directories : [directories]; | ||
this.watcher = watcher; | ||
watcher.on("initial-missing", type => { | ||
for (const item of this.directories) { | ||
watchpack._onRemove(item, item, type); | ||
} | ||
}); | ||
watcher.on("change", (file, mtime, type) => { | ||
for (const item of this.directories) { | ||
watchpack._onChange(item, mtime, file, type); | ||
} | ||
}); | ||
watcher.on("remove", type => { | ||
for (const item of this.directories) { | ||
watchpack._onRemove(item, item, type); | ||
} | ||
}); | ||
} | ||
update(directories) { | ||
if (!Array.isArray(directories)) { | ||
if (this.directories.length !== 1) { | ||
this.directories = [directories]; | ||
} else if (this.directories[0] !== directories) { | ||
this.directories[0] = directories; | ||
} | ||
} else { | ||
this.directories = directories; | ||
} | ||
} | ||
close() { | ||
this.watcher.close(); | ||
} | ||
} | ||
class Watchpack extends EventEmitter { | ||
@@ -84,2 +165,3 @@ constructor(options) { | ||
this.directoryWatchers = new Map(); | ||
this._missing = new Set(); | ||
this.startTime = undefined; | ||
@@ -109,14 +191,14 @@ this.paused = false; | ||
this.paused = false; | ||
const oldFileWatchers = this.fileWatchers; | ||
const oldDirectoryWatchers = this.directoryWatchers; | ||
const fileWatchers = this.fileWatchers; | ||
const directoryWatchers = this.directoryWatchers; | ||
const ignored = this.watcherOptions.ignored; | ||
const filter = ignored | ||
? path => !ignored.test(path.replace(/\\/g, "/")) | ||
: () => true; | ||
const filter = path => !ignored(path); | ||
const addToMap = (map, key, item) => { | ||
const list = map.get(key); | ||
if (list === undefined) { | ||
map.set(key, [item]); | ||
map.set(key, item); | ||
} else if (Array.isArray(list)) { | ||
list.push(item); | ||
} else { | ||
list.push(item); | ||
map.set(key, [list, item]); | ||
} | ||
@@ -181,55 +263,22 @@ }; | ||
} | ||
const newFileWatchers = new Map(); | ||
const newDirectoryWatchers = new Map(); | ||
const setupFileWatcher = (watcher, key, files) => { | ||
watcher.on("initial-missing", type => { | ||
for (const file of files) { | ||
if (!missingFiles.has(file)) this._onRemove(file, file, type); | ||
} | ||
}); | ||
watcher.on("change", (mtime, type) => { | ||
for (const file of files) { | ||
this._onChange(file, mtime, file, type); | ||
} | ||
}); | ||
watcher.on("remove", type => { | ||
for (const file of files) { | ||
this._onRemove(file, file, type); | ||
} | ||
}); | ||
newFileWatchers.set(key, watcher); | ||
}; | ||
const setupDirectoryWatcher = (watcher, key, directories) => { | ||
watcher.on("initial-missing", type => { | ||
for (const item of directories) { | ||
this._onRemove(item, item, type); | ||
} | ||
}); | ||
watcher.on("change", (file, mtime, type) => { | ||
for (const item of directories) { | ||
this._onChange(item, mtime, file, type); | ||
} | ||
}); | ||
watcher.on("remove", type => { | ||
for (const item of directories) { | ||
this._onRemove(item, item, type); | ||
} | ||
}); | ||
newDirectoryWatchers.set(key, watcher); | ||
}; | ||
// Close unneeded old watchers | ||
const fileWatchersToClose = []; | ||
const directoryWatchersToClose = []; | ||
for (const [key, w] of oldFileWatchers) { | ||
if (!fileWatchersNeeded.has(key)) { | ||
// and update existing watchers | ||
for (const [key, w] of fileWatchers) { | ||
const needed = fileWatchersNeeded.get(key); | ||
if (needed === undefined) { | ||
w.close(); | ||
fileWatchers.delete(key); | ||
} else { | ||
fileWatchersToClose.push(w); | ||
w.update(needed); | ||
fileWatchersNeeded.delete(key); | ||
} | ||
} | ||
for (const [key, w] of oldDirectoryWatchers) { | ||
if (!directoryWatchersNeeded.has(key)) { | ||
for (const [key, w] of directoryWatchers) { | ||
const needed = directoryWatchersNeeded.get(key); | ||
if (needed === undefined) { | ||
w.close(); | ||
directoryWatchers.delete(key); | ||
} else { | ||
directoryWatchersToClose.push(w); | ||
w.update(needed); | ||
directoryWatchersNeeded.delete(key); | ||
} | ||
@@ -242,3 +291,3 @@ } | ||
if (watcher) { | ||
setupFileWatcher(watcher, key, files); | ||
fileWatchers.set(key, new WatchpackFileWatcher(this, watcher, files)); | ||
} | ||
@@ -249,12 +298,10 @@ } | ||
if (watcher) { | ||
setupDirectoryWatcher(watcher, key, directories); | ||
directoryWatchers.set( | ||
key, | ||
new WatchpackDirectoryWatcher(this, watcher, directories) | ||
); | ||
} | ||
} | ||
}); | ||
// Close old watchers | ||
for (const w of fileWatchersToClose) w.close(); | ||
for (const w of directoryWatchersToClose) w.close(); | ||
// Store watchers | ||
this.fileWatchers = newFileWatchers; | ||
this.directoryWatchers = newDirectoryWatchers; | ||
this._missing = missingFiles; | ||
this.startTime = startTime; | ||
@@ -290,28 +337,17 @@ } | ||
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); | ||
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() { | ||
@@ -318,0 +354,0 @@ if (this.aggregateTimer) { |
{ | ||
"name": "watchpack", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "./lib/watchpack.js", |
@@ -52,2 +52,4 @@ # watchpack | ||
// ignored: /regexp/ - a regular expression for files or folders that should not be watched | ||
// ignored: (entry) => boolean - an arbitrary function which must return truthy to ignore an entry | ||
// For all cases expect the arbitrary function the path will have path separator normalized to '/'. | ||
// All subdirectories are ignored too | ||
@@ -116,5 +118,5 @@ }); | ||
// 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 | ||
@@ -125,2 +127,7 @@ // key: absolute path, value: object with { safeTime, timestamp } | ||
// 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) | ||
@@ -134,9 +141,9 @@ // Watchpack.prototype.getTimes() | ||
[build-status]: https://travis-ci.org/webpack/watchpack.svg?branch=master | ||
[build-status]: https://travis-ci.org/webpack/watchpack.svg?branch=main | ||
[build-status-url]: https://travis-ci.org/webpack/watchpack | ||
[build-status-veyor]: https://ci.appveyor.com/api/projects/status/e5u2qvmugtv0r647/branch/master?svg=true | ||
[build-status-veyor-url]: https://ci.appveyor.com/project/sokra/watchpack/branch/master | ||
[build-status-veyor]: https://ci.appveyor.com/api/projects/status/e5u2qvmugtv0r647/branch/main?svg=true | ||
[build-status-veyor-url]: https://ci.appveyor.com/project/sokra/watchpack/branch/main | ||
[coveralls-url]: https://coveralls.io/r/webpack/watchpack/ | ||
[coveralls-image]: https://img.shields.io/coveralls/webpack/watchpack.svg | ||
[codecov]: https://codecov.io/gh/webpack/watchpack/branch/master/graph/badge.svg | ||
[codecov]: https://codecov.io/gh/webpack/watchpack/branch/main/graph/badge.svg | ||
[codecov-url]: https://codecov.io/gh/webpack/watchpack | ||
@@ -143,0 +150,0 @@ [downloads]: https://img.shields.io/npm/dm/watchpack.svg |
56812
1675
150