watchpack
Advanced tools
Comparing version 2.0.0-beta.13 to 2.0.0-beta.14
@@ -11,2 +11,4 @@ /* | ||
const watchEventSource = require("./watchEventSource"); | ||
const EXISTANCE_ONLY_TIME_ENTRY = Object.freeze({}); | ||
@@ -81,2 +83,3 @@ | ||
: false; | ||
this.timeout = undefined; | ||
this.initialScanRemoved = new Set(); | ||
@@ -107,9 +110,8 @@ this.initialScanFinished = undefined; | ||
if (this.polledWatching) { | ||
// Poll for changes | ||
const interval = setInterval(() => { | ||
this.doScan(false); | ||
}, this.polledWatching); | ||
this.watcher = { | ||
close: () => { | ||
clearInterval(interval); | ||
if (this.timeout) { | ||
clearTimeout(this.timeout); | ||
this.timeout = undefined; | ||
} | ||
} | ||
@@ -121,3 +123,3 @@ }; | ||
} | ||
this.watcher = fs.watch(this.path); | ||
this.watcher = watchEventSource.watch(this.path); | ||
this.watcher.on("change", this.onWatchEvent.bind(this)); | ||
@@ -155,3 +157,3 @@ this.watcher.on("error", this.onWatcherError.bind(this)); | ||
this.forEachWatcher(this.path, w => | ||
w.emit("change", itemPath, null, type) | ||
w.emit("change", itemPath, null, type, initial) | ||
); | ||
@@ -175,3 +177,3 @@ } | ||
this.forEachWatcher(this.path, w => | ||
w.emit("change", itemPath, null, type) | ||
w.emit("change", itemPath, null, type, initial) | ||
); | ||
@@ -220,4 +222,4 @@ } | ||
if (count !== undefined) { | ||
// There is already a file with case-insenstive-equal name | ||
// On a case-insenstive filesystem we may miss the renaming | ||
// There is already a file with case-insensitive-equal name | ||
// On a case-insensitive filesystem we may miss the renaming | ||
// when only casing is changed. | ||
@@ -239,3 +241,3 @@ // To be sure that our information is correct | ||
if (!initial || w.checkStartTime(safeTime, initial)) { | ||
w.emit("change", filePath, safeTime, type); | ||
w.emit("change", filePath, safeTime, type, initial); | ||
} | ||
@@ -250,3 +252,3 @@ }); | ||
this.forEachWatcher(this.path, w => | ||
w.emit("change", directoryPath, mtime, type) | ||
w.emit("change", directoryPath, mtime, type, initial) | ||
); | ||
@@ -280,3 +282,3 @@ } | ||
if (!initial || w.checkStartTime(safeTime, initial)) { | ||
w.emit("change", directoryPath, safeTime, type); | ||
w.emit("change", directoryPath, safeTime, type, initial); | ||
} | ||
@@ -290,7 +292,7 @@ }); | ||
const watcher = this.watcherManager.watchDirectory(directoryPath, 1); | ||
watcher.on("change", (filePath, mtime, type) => { | ||
watcher.on("change", (filePath, mtime, type, initial) => { | ||
this._cachedTimeInfoEntries = undefined; | ||
this.forEachWatcher(this.path, w => { | ||
if (w.checkStartTime(mtime, false)) { | ||
w.emit("change", filePath, mtime, type); | ||
if (!initial || w.checkStartTime(mtime, initial)) { | ||
w.emit("change", filePath, mtime, type, initial); | ||
} | ||
@@ -478,4 +480,14 @@ }); | ||
} | ||
this.onScanFinished(); | ||
} | ||
onScanFinished() { | ||
if (this.polledWatching) { | ||
this.timeout = setTimeout(() => { | ||
if (this.closed) return; | ||
this.doScan(false); | ||
}, this.polledWatching); | ||
} | ||
} | ||
onDirectoryRemoved(reason) { | ||
@@ -520,3 +532,3 @@ if (this.watcher) { | ||
this.forEachWatcher(this.path, w => | ||
w.emit("change", this.path, mtime, type) | ||
w.emit("change", this.path, mtime, type, false) | ||
); | ||
@@ -542,120 +554,128 @@ } | ||
this.scanning = true; | ||
fs.readdir(this.path, (err, items) => { | ||
if (this.timeout) { | ||
clearTimeout(this.timeout); | ||
this.timeout = undefined; | ||
} | ||
process.nextTick(() => { | ||
if (this.closed) return; | ||
if (err) { | ||
if (err.code === "ENOENT" || err.code === "EPERM") { | ||
this.onDirectoryRemoved("scan readdir failed"); | ||
} else { | ||
this.onScanError(err); | ||
} | ||
this.initialScan = false; | ||
this.initialScanFinished = Date.now(); | ||
if (initial) { | ||
for (const watchers of this.watchers.values()) { | ||
for (const watcher of watchers) { | ||
if (watcher.checkStartTime(this.initialScanFinished, false)) { | ||
watcher.emit( | ||
"initial-missing", | ||
"scan (parent directory missing in initial scan)" | ||
); | ||
fs.readdir(this.path, (err, items) => { | ||
if (this.closed) return; | ||
if (err) { | ||
if (err.code === "ENOENT" || err.code === "EPERM") { | ||
this.onDirectoryRemoved("scan readdir failed"); | ||
} else { | ||
this.onScanError(err); | ||
} | ||
this.initialScan = false; | ||
this.initialScanFinished = Date.now(); | ||
if (initial) { | ||
for (const watchers of this.watchers.values()) { | ||
for (const watcher of watchers) { | ||
if (watcher.checkStartTime(this.initialScanFinished, false)) { | ||
watcher.emit( | ||
"initial-missing", | ||
"scan (parent directory missing in initial scan)" | ||
); | ||
} | ||
} | ||
} | ||
} | ||
if (this.scanAgain) { | ||
this.scanAgain = false; | ||
this.doScan(this.scanAgainInitial); | ||
} else { | ||
this.scanning = false; | ||
} | ||
return; | ||
} | ||
if (this.scanAgain) { | ||
this.scanAgain = false; | ||
this.doScan(this.scanAgainInitial); | ||
} else { | ||
this.scanning = false; | ||
const itemPaths = new Set( | ||
items.map(item => path.join(this.path, item.normalize("NFC"))) | ||
); | ||
for (const file of this.files.keys()) { | ||
if (!itemPaths.has(file)) { | ||
this.setMissing(file, initial, "scan (missing)"); | ||
} | ||
} | ||
return; | ||
} | ||
const itemPaths = new Set( | ||
items.map(item => path.join(this.path, item.normalize("NFC"))) | ||
); | ||
for (const file of this.files.keys()) { | ||
if (!itemPaths.has(file)) { | ||
this.setMissing(file, initial, "scan (missing)"); | ||
} | ||
} | ||
for (const directory of this.directories.keys()) { | ||
if (!itemPaths.has(directory)) { | ||
this.setMissing(directory, initial, "scan (missing)"); | ||
} | ||
} | ||
if (this.scanAgain) { | ||
// Early repeat of scan | ||
this.scanAgain = false; | ||
this.doScan(initial); | ||
return; | ||
} | ||
const itemFinished = needCalls(itemPaths.size + 1, () => { | ||
if (this.closed) return; | ||
this.initialScan = false; | ||
this.initialScanRemoved = null; | ||
this.initialScanFinished = Date.now(); | ||
if (initial) { | ||
const missingWatchers = new Map(this.watchers); | ||
missingWatchers.delete(withoutCase(this.path)); | ||
for (const item of itemPaths) { | ||
missingWatchers.delete(withoutCase(item)); | ||
for (const directory of this.directories.keys()) { | ||
if (!itemPaths.has(directory)) { | ||
this.setMissing(directory, initial, "scan (missing)"); | ||
} | ||
for (const watchers of missingWatchers.values()) { | ||
for (const watcher of watchers) { | ||
if (watcher.checkStartTime(this.initialScanFinished, false)) { | ||
watcher.emit( | ||
"initial-missing", | ||
"scan (missing in initial scan)" | ||
); | ||
} | ||
} | ||
} | ||
} | ||
if (this.scanAgain) { | ||
// Early repeat of scan | ||
this.scanAgain = false; | ||
this.doScan(this.scanAgainInitial); | ||
} else { | ||
this.scanning = false; | ||
this.doScan(initial); | ||
return; | ||
} | ||
}); | ||
for (const itemPath of itemPaths) { | ||
fs.lstat(itemPath, (err2, stats) => { | ||
const itemFinished = needCalls(itemPaths.size + 1, () => { | ||
if (this.closed) return; | ||
if (err2) { | ||
if ( | ||
err2.code === "ENOENT" || | ||
err2.code === "EPERM" || | ||
err2.code === "EBUSY" | ||
) { | ||
this.setMissing(itemPath, initial, "scan (" + err2.code + ")"); | ||
} else { | ||
this.onScanError(err2); | ||
this.initialScan = false; | ||
this.initialScanRemoved = null; | ||
this.initialScanFinished = Date.now(); | ||
if (initial) { | ||
const missingWatchers = new Map(this.watchers); | ||
missingWatchers.delete(withoutCase(this.path)); | ||
for (const item of itemPaths) { | ||
missingWatchers.delete(withoutCase(item)); | ||
} | ||
itemFinished(); | ||
return; | ||
for (const watchers of missingWatchers.values()) { | ||
for (const watcher of watchers) { | ||
if (watcher.checkStartTime(this.initialScanFinished, false)) { | ||
watcher.emit( | ||
"initial-missing", | ||
"scan (missing in initial scan)" | ||
); | ||
} | ||
} | ||
} | ||
} | ||
if (stats.isFile() || stats.isSymbolicLink()) { | ||
if (stats.mtime) { | ||
ensureFsAccuracy(stats.mtime); | ||
if (this.scanAgain) { | ||
this.scanAgain = false; | ||
this.doScan(this.scanAgainInitial); | ||
} else { | ||
this.scanning = false; | ||
this.onScanFinished(); | ||
} | ||
}); | ||
for (const itemPath of itemPaths) { | ||
fs.lstat(itemPath, (err2, stats) => { | ||
if (this.closed) return; | ||
if (err2) { | ||
if ( | ||
err2.code === "ENOENT" || | ||
err2.code === "EPERM" || | ||
err2.code === "EBUSY" | ||
) { | ||
this.setMissing(itemPath, initial, "scan (" + err2.code + ")"); | ||
} else { | ||
this.onScanError(err2); | ||
} | ||
itemFinished(); | ||
return; | ||
} | ||
this.setFileTime( | ||
itemPath, | ||
+stats.mtime || +stats.ctime || 1, | ||
initial, | ||
true, | ||
"scan (file)" | ||
); | ||
} else if (stats.isDirectory()) { | ||
if (!initial || !this.directories.has(itemPath)) | ||
this.setDirectory( | ||
if (stats.isFile() || stats.isSymbolicLink()) { | ||
if (stats.mtime) { | ||
ensureFsAccuracy(stats.mtime); | ||
} | ||
this.setFileTime( | ||
itemPath, | ||
+stats.mtime || +stats.ctime || 1, | ||
initial, | ||
"scan (dir)" | ||
true, | ||
"scan (file)" | ||
); | ||
} | ||
itemFinished(); | ||
}); | ||
} | ||
itemFinished(); | ||
} else if (stats.isDirectory()) { | ||
if (!initial || !this.directories.has(itemPath)) | ||
this.setDirectory( | ||
itemPath, | ||
+stats.mtime || +stats.ctime || 1, | ||
initial, | ||
"scan (dir)" | ||
); | ||
} | ||
itemFinished(); | ||
}); | ||
} | ||
itemFinished(); | ||
}); | ||
}); | ||
@@ -662,0 +682,0 @@ } |
@@ -41,2 +41,6 @@ /* | ||
const watcherManagers = new WeakMap(); | ||
/** | ||
* @param {object} options options | ||
* @returns {WatcherManager} the watcher manager | ||
*/ | ||
module.exports = options => { | ||
@@ -43,0 +47,0 @@ const watcherManager = watcherManagers.get(options); |
@@ -11,2 +11,3 @@ /* | ||
const globToRegExp = require("glob-to-regexp"); | ||
const watchEventSource = require("./watchEventSource"); | ||
@@ -16,2 +17,3 @@ let EXISTANCE_ONLY_TIME_ENTRY; // lazy required | ||
const EMPTY_ARRAY = []; | ||
const EMPTY_OPTIONS = {}; | ||
@@ -68,14 +70,17 @@ function addWatchersToSet(watchers, set) { | ||
super(); | ||
if (!options) options = {}; | ||
if (typeof options.aggregateTimeout !== "number") { | ||
options.aggregateTimeout = 200; | ||
} | ||
if (!options) options = EMPTY_OPTIONS; | ||
this.options = options; | ||
this.aggregateTimeout = | ||
typeof options.aggregateTimeout === "number" | ||
? options.aggregateTimeout | ||
: 200; | ||
this.watcherOptions = cachedNormalizeOptions(options); | ||
this.watcherManager = getWatcherManager(this.watcherOptions); | ||
this.watchers = []; | ||
this.fileWatchers = new Map(); | ||
this.directoryWatchers = new Map(); | ||
this.startTime = undefined; | ||
this.paused = false; | ||
this.aggregatedChanges = new Set(); | ||
this.aggregatedRemovals = new Set(); | ||
this.aggregateTimeout = 0; | ||
this.aggregateTimer = undefined; | ||
this._onTimeout = this._onTimeout.bind(this); | ||
@@ -100,3 +105,4 @@ } | ||
this.paused = false; | ||
const oldWatchers = this.watchers; | ||
const oldFileWatchers = this.fileWatchers; | ||
const oldDirectoryWatchers = this.directoryWatchers; | ||
const ignored = this.watcherOptions.ignored; | ||
@@ -106,3 +112,13 @@ const filter = ignored | ||
: () => true; | ||
this.watchers = []; | ||
const addToMap = (map, key, item) => { | ||
const list = map.get(key); | ||
if (list === undefined) { | ||
map.set(key, [item]); | ||
} else { | ||
list.push(item); | ||
} | ||
}; | ||
const fileWatchersNeeded = new Map(); | ||
const directoryWatchersNeeded = new Map(); | ||
const missingFiles = new Set(); | ||
if (this.watcherOptions.followSymlinks) { | ||
@@ -114,7 +130,3 @@ const resolver = new LinkResolver(); | ||
if (file === innerFile || filter(innerFile)) { | ||
const watcher = this._fileWatcher( | ||
file, | ||
this.watcherManager.watchFile(innerFile, startTime) | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
addToMap(fileWatchersNeeded, innerFile, file); | ||
} | ||
@@ -128,7 +140,4 @@ } | ||
if (file === innerFile || filter(innerFile)) { | ||
const watcher = this._missingWatcher( | ||
file, | ||
this.watcherManager.watchFile(innerFile, startTime) | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
missingFiles.add(file); | ||
addToMap(fileWatchersNeeded, innerFile, file); | ||
} | ||
@@ -143,9 +152,7 @@ } | ||
if (filter(innerItem)) { | ||
const watcher = this._dirWatcher( | ||
dir, | ||
first | ||
? this.watcherManager.watchDirectory(innerItem, startTime) | ||
: this.watcherManager.watchFile(innerItem, startTime) | ||
addToMap( | ||
first ? directoryWatchersNeeded : fileWatchersNeeded, | ||
innerItem, | ||
dir | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
} | ||
@@ -159,7 +166,3 @@ first = false; | ||
if (filter(file)) { | ||
const watcher = this._fileWatcher( | ||
file, | ||
this.watcherManager.watchFile(file, startTime) | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
addToMap(fileWatchersNeeded, file, file); | ||
} | ||
@@ -169,7 +172,4 @@ } | ||
if (filter(file)) { | ||
const watcher = this._missingWatcher( | ||
file, | ||
this.watcherManager.watchFile(file, startTime) | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
missingFiles.add(file); | ||
addToMap(fileWatchersNeeded, file, file); | ||
} | ||
@@ -179,11 +179,83 @@ } | ||
if (filter(dir)) { | ||
const watcher = this._dirWatcher( | ||
dir, | ||
this.watcherManager.watchDirectory(dir, startTime) | ||
); | ||
if (watcher) this.watchers.push(watcher); | ||
addToMap(directoryWatchersNeeded, dir, dir); | ||
} | ||
} | ||
} | ||
for (const w of oldWatchers) w.close(); | ||
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)) { | ||
w.close(); | ||
} else { | ||
fileWatchersToClose.push(w); | ||
} | ||
} | ||
for (const [key, w] of oldDirectoryWatchers) { | ||
if (!directoryWatchersNeeded.has(key)) { | ||
w.close(); | ||
} else { | ||
directoryWatchersToClose.push(w); | ||
} | ||
} | ||
// Create new watchers and install handlers on these watchers | ||
watchEventSource.batch(() => { | ||
for (const [key, files] of fileWatchersNeeded) { | ||
const watcher = this.watcherManager.watchFile(key, startTime); | ||
if (watcher) { | ||
setupFileWatcher(watcher, key, files); | ||
} | ||
} | ||
for (const [key, directories] of directoryWatchersNeeded) { | ||
const watcher = this.watcherManager.watchDirectory(key, startTime); | ||
if (watcher) { | ||
setupDirectoryWatcher(watcher, key, 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.startTime = startTime; | ||
} | ||
@@ -193,5 +265,7 @@ | ||
this.paused = true; | ||
if (this.aggregateTimeout) clearTimeout(this.aggregateTimeout); | ||
for (const w of this.watchers) w.close(); | ||
this.watchers.length = 0; | ||
if (this.aggregateTimer) clearTimeout(this.aggregateTimer); | ||
for (const w of this.fileWatchers.values()) w.close(); | ||
for (const w of this.directoryWatchers.values()) w.close(); | ||
this.fileWatchers.clear(); | ||
this.directoryWatchers.clear(); | ||
} | ||
@@ -201,3 +275,3 @@ | ||
this.paused = true; | ||
if (this.aggregateTimeout) clearTimeout(this.aggregateTimeout); | ||
if (this.aggregateTimer) clearTimeout(this.aggregateTimer); | ||
} | ||
@@ -207,3 +281,4 @@ | ||
const directoryWatchers = new Set(); | ||
addWatchersToSet(this.watchers, directoryWatchers); | ||
addWatchersToSet(this.fileWatchers.values(), directoryWatchers); | ||
addWatchersToSet(this.directoryWatchers.values(), directoryWatchers); | ||
const obj = Object.create(null); | ||
@@ -223,3 +298,4 @@ for (const w of directoryWatchers) { | ||
const directoryWatchers = new Set(); | ||
addWatchersToSet(this.watchers, directoryWatchers); | ||
addWatchersToSet(this.fileWatchers.values(), directoryWatchers); | ||
addWatchersToSet(this.directoryWatchers.values(), directoryWatchers); | ||
const map = new Map(); | ||
@@ -288,9 +364,6 @@ for (const w of directoryWatchers) { | ||
this.emit("change", file, mtime, type); | ||
if (this.aggregateTimeout) clearTimeout(this.aggregateTimeout); | ||
if (this.aggregateTimer) clearTimeout(this.aggregateTimer); | ||
this.aggregatedRemovals.delete(item); | ||
this.aggregatedChanges.add(item); | ||
this.aggregateTimeout = setTimeout( | ||
this._onTimeout, | ||
this.options.aggregateTimeout | ||
); | ||
this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout); | ||
} | ||
@@ -302,13 +375,10 @@ | ||
this.emit("remove", file, type); | ||
if (this.aggregateTimeout) clearTimeout(this.aggregateTimeout); | ||
if (this.aggregateTimer) clearTimeout(this.aggregateTimer); | ||
this.aggregatedChanges.delete(item); | ||
this.aggregatedRemovals.add(item); | ||
this.aggregateTimeout = setTimeout( | ||
this._onTimeout, | ||
this.options.aggregateTimeout | ||
); | ||
this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout); | ||
} | ||
_onTimeout() { | ||
this.aggregateTimeout = 0; | ||
this.aggregateTimer = undefined; | ||
const changes = this.aggregatedChanges; | ||
@@ -315,0 +385,0 @@ const removals = this.aggregatedRemovals; |
{ | ||
"name": "watchpack", | ||
"version": "2.0.0-beta.13", | ||
"version": "2.0.0-beta.14", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "./lib/watchpack.js", |
@@ -69,11 +69,11 @@ # watchpack | ||
// calling this again will override the files and directories | ||
// files: can be files or directories, for files: content and existance changes are tracked | ||
// for directories: only existance and timestamp changes are tracked | ||
// files: can be files or directories, for files: content and existence changes are tracked | ||
// for directories: only existence and timestamp changes are tracked | ||
// directories: only directories, directory content (and content of children, ...) and | ||
// existance changes are tracked. | ||
// assumed to exist, when directory is not found without futher information a remove event is emitted | ||
// missing: can be files or directores, | ||
// only existance changes are tracked | ||
// existence changes are tracked. | ||
// assumed to exist, when directory is not found without further information a remove event is emitted | ||
// missing: can be files or directorees, | ||
// only existence changes are tracked | ||
// expected to not exist, no remove event is emitted when not found initially | ||
// files and directories are assumed to exist, when they are not found without futher information a remove event is emitted | ||
// files and directories are assumed to exist, when they are not found without further information a remove event is emitted | ||
// missing is assumed to not exist and no remove event is emitted | ||
@@ -80,0 +80,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
54804
9
1631
4