summaryrefslogtreecommitdiff
path: root/node_modules/watchpack/lib
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
committerlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
commitefbc2ffe6a1ee5ec38a4396305561d6515d0943e (patch)
tree2b4c94f18f2c792dfda813050755d5c0273fae22 /node_modules/watchpack/lib
parent43acceb75957c880a5ba5a31aae221394a36d5f6 (diff)
node fixes, updates
Diffstat (limited to 'node_modules/watchpack/lib')
-rw-r--r--node_modules/watchpack/lib/DirectoryWatcher.js787
-rw-r--r--node_modules/watchpack/lib/LinkResolver.js107
-rw-r--r--node_modules/watchpack/lib/getWatcherManager.js52
-rw-r--r--node_modules/watchpack/lib/reducePlan.js138
-rw-r--r--node_modules/watchpack/lib/watchEventSource.js333
-rw-r--r--node_modules/watchpack/lib/watchpack.js393
6 files changed, 1810 insertions, 0 deletions
diff --git a/node_modules/watchpack/lib/DirectoryWatcher.js b/node_modules/watchpack/lib/DirectoryWatcher.js
new file mode 100644
index 00000000..034447de
--- /dev/null
+++ b/node_modules/watchpack/lib/DirectoryWatcher.js
@@ -0,0 +1,787 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const EventEmitter = require("events").EventEmitter;
+const fs = require("graceful-fs");
+const path = require("path");
+
+const watchEventSource = require("./watchEventSource");
+
+const EXISTANCE_ONLY_TIME_ENTRY = Object.freeze({});
+
+let FS_ACCURACY = 2000;
+
+const IS_OSX = require("os").platform() === "darwin";
+const WATCHPACK_POLLING = process.env.WATCHPACK_POLLING;
+const FORCE_POLLING =
+ `${+WATCHPACK_POLLING}` === WATCHPACK_POLLING
+ ? +WATCHPACK_POLLING
+ : !!WATCHPACK_POLLING && WATCHPACK_POLLING !== "false";
+
+function withoutCase(str) {
+ return str.toLowerCase();
+}
+
+function needCalls(times, callback) {
+ return function() {
+ if (--times === 0) {
+ return callback();
+ }
+ };
+}
+
+class Watcher extends EventEmitter {
+ constructor(directoryWatcher, filePath, startTime) {
+ super();
+ this.directoryWatcher = directoryWatcher;
+ this.path = filePath;
+ this.startTime = startTime && +startTime;
+ }
+
+ checkStartTime(mtime, initial) {
+ const startTime = this.startTime;
+ if (typeof startTime !== "number") return !initial;
+ return startTime <= mtime;
+ }
+
+ close() {
+ this.emit("closed");
+ }
+}
+
+class DirectoryWatcher extends EventEmitter {
+ constructor(watcherManager, directoryPath, options) {
+ super();
+ if (FORCE_POLLING) {
+ options.poll = FORCE_POLLING;
+ }
+ this.watcherManager = watcherManager;
+ this.options = options;
+ this.path = directoryPath;
+ // safeTime is the point in time after which reading is safe to be unchanged
+ // timestamp is a value that should be compared with another timestamp (mtime)
+ /** @type {Map<string, { safeTime: number, timestamp: number }} */
+ this.files = new Map();
+ /** @type {Map<string, number>} */
+ this.filesWithoutCase = new Map();
+ this.directories = new Map();
+ this.lastWatchEvent = 0;
+ this.initialScan = true;
+ this.ignored = options.ignored || (() => false);
+ this.nestedWatching = false;
+ this.polledWatching =
+ typeof options.poll === "number"
+ ? options.poll
+ : options.poll
+ ? 5007
+ : false;
+ this.timeout = undefined;
+ this.initialScanRemoved = new Set();
+ this.initialScanFinished = undefined;
+ /** @type {Map<string, Set<Watcher>>} */
+ this.watchers = new Map();
+ this.parentWatcher = null;
+ this.refs = 0;
+ this._activeEvents = new Map();
+ this.closed = false;
+ this.scanning = false;
+ this.scanAgain = false;
+ this.scanAgainInitial = false;
+
+ this.createWatcher();
+ this.doScan(true);
+ }
+
+ createWatcher() {
+ try {
+ if (this.polledWatching) {
+ this.watcher = {
+ close: () => {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ this.timeout = undefined;
+ }
+ }
+ };
+ } else {
+ if (IS_OSX) {
+ this.watchInParentDirectory();
+ }
+ this.watcher = watchEventSource.watch(this.path);
+ this.watcher.on("change", this.onWatchEvent.bind(this));
+ this.watcher.on("error", this.onWatcherError.bind(this));
+ }
+ } catch (err) {
+ this.onWatcherError(err);
+ }
+ }
+
+ forEachWatcher(path, fn) {
+ const watchers = this.watchers.get(withoutCase(path));
+ if (watchers !== undefined) {
+ for (const w of watchers) {
+ fn(w);
+ }
+ }
+ }
+
+ setMissing(itemPath, initial, type) {
+ if (this.initialScan) {
+ this.initialScanRemoved.add(itemPath);
+ }
+
+ const oldDirectory = this.directories.get(itemPath);
+ if (oldDirectory) {
+ if (this.nestedWatching) oldDirectory.close();
+ this.directories.delete(itemPath);
+
+ this.forEachWatcher(itemPath, w => w.emit("remove", type));
+ if (!initial) {
+ this.forEachWatcher(this.path, w =>
+ w.emit("change", itemPath, null, type, initial)
+ );
+ }
+ }
+
+ const oldFile = this.files.get(itemPath);
+ if (oldFile) {
+ this.files.delete(itemPath);
+ const key = withoutCase(itemPath);
+ const count = this.filesWithoutCase.get(key) - 1;
+ if (count <= 0) {
+ this.filesWithoutCase.delete(key);
+ this.forEachWatcher(itemPath, w => w.emit("remove", type));
+ } else {
+ this.filesWithoutCase.set(key, count);
+ }
+
+ if (!initial) {
+ this.forEachWatcher(this.path, w =>
+ w.emit("change", itemPath, null, type, initial)
+ );
+ }
+ }
+ }
+
+ setFileTime(filePath, mtime, initial, ignoreWhenEqual, type) {
+ const now = Date.now();
+
+ if (this.ignored(filePath)) return;
+
+ const old = this.files.get(filePath);
+
+ let safeTime, accuracy;
+ if (initial) {
+ safeTime = Math.min(now, mtime) + FS_ACCURACY;
+ accuracy = FS_ACCURACY;
+ } else {
+ safeTime = now;
+ accuracy = 0;
+
+ if (old && old.timestamp === mtime && mtime + FS_ACCURACY < now) {
+ // We are sure that mtime is untouched
+ // This can be caused by some file attribute change
+ // e. g. when access time has been changed
+ // but the file content is untouched
+ return;
+ }
+ }
+
+ if (ignoreWhenEqual && old && old.timestamp === mtime) return;
+
+ this.files.set(filePath, {
+ safeTime,
+ accuracy,
+ timestamp: mtime
+ });
+
+ if (!old) {
+ const key = withoutCase(filePath);
+ const count = this.filesWithoutCase.get(key);
+ this.filesWithoutCase.set(key, (count || 0) + 1);
+ if (count !== undefined) {
+ // 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.
+ // To be sure that our information is correct
+ // we trigger a rescan here
+ this.doScan(false);
+ }
+
+ this.forEachWatcher(filePath, w => {
+ if (!initial || w.checkStartTime(safeTime, initial)) {
+ w.emit("change", mtime, type);
+ }
+ });
+ } else if (!initial) {
+ this.forEachWatcher(filePath, w => w.emit("change", mtime, type));
+ }
+ this.forEachWatcher(this.path, w => {
+ if (!initial || w.checkStartTime(safeTime, initial)) {
+ w.emit("change", filePath, safeTime, type, initial);
+ }
+ });
+ }
+
+ setDirectory(directoryPath, birthtime, initial, type) {
+ if (this.ignored(directoryPath)) return;
+ if (directoryPath === this.path) {
+ if (!initial) {
+ this.forEachWatcher(this.path, w =>
+ w.emit("change", directoryPath, birthtime, type, initial)
+ );
+ }
+ } else {
+ const old = this.directories.get(directoryPath);
+ if (!old) {
+ const now = Date.now();
+
+ if (this.nestedWatching) {
+ this.createNestedWatcher(directoryPath);
+ } else {
+ this.directories.set(directoryPath, true);
+ }
+
+ let safeTime;
+ if (initial) {
+ safeTime = Math.min(now, birthtime) + FS_ACCURACY;
+ } else {
+ safeTime = now;
+ }
+
+ this.forEachWatcher(directoryPath, w => {
+ if (!initial || w.checkStartTime(safeTime, false)) {
+ w.emit("change", birthtime, type);
+ }
+ });
+ this.forEachWatcher(this.path, w => {
+ if (!initial || w.checkStartTime(safeTime, initial)) {
+ w.emit("change", directoryPath, safeTime, type, initial);
+ }
+ });
+ }
+ }
+ }
+
+ createNestedWatcher(directoryPath) {
+ const watcher = this.watcherManager.watchDirectory(directoryPath, 1);
+ watcher.on("change", (filePath, mtime, type, initial) => {
+ this.forEachWatcher(this.path, w => {
+ if (!initial || w.checkStartTime(mtime, initial)) {
+ w.emit("change", filePath, mtime, type, initial);
+ }
+ });
+ });
+ this.directories.set(directoryPath, watcher);
+ }
+
+ setNestedWatching(flag) {
+ if (this.nestedWatching !== !!flag) {
+ this.nestedWatching = !!flag;
+ if (this.nestedWatching) {
+ for (const directory of this.directories.keys()) {
+ this.createNestedWatcher(directory);
+ }
+ } else {
+ for (const [directory, watcher] of this.directories) {
+ watcher.close();
+ this.directories.set(directory, true);
+ }
+ }
+ }
+ }
+
+ watch(filePath, startTime) {
+ const key = withoutCase(filePath);
+ let watchers = this.watchers.get(key);
+ if (watchers === undefined) {
+ watchers = new Set();
+ this.watchers.set(key, watchers);
+ }
+ this.refs++;
+ const watcher = new Watcher(this, filePath, startTime);
+ watcher.on("closed", () => {
+ if (--this.refs <= 0) {
+ this.close();
+ return;
+ }
+ watchers.delete(watcher);
+ if (watchers.size === 0) {
+ this.watchers.delete(key);
+ if (this.path === filePath) this.setNestedWatching(false);
+ }
+ });
+ watchers.add(watcher);
+ let safeTime;
+ if (filePath === this.path) {
+ this.setNestedWatching(true);
+ safeTime = this.lastWatchEvent;
+ for (const entry of this.files.values()) {
+ fixupEntryAccuracy(entry);
+ safeTime = Math.max(safeTime, entry.safeTime);
+ }
+ } else {
+ const entry = this.files.get(filePath);
+ if (entry) {
+ fixupEntryAccuracy(entry);
+ safeTime = entry.safeTime;
+ } else {
+ safeTime = 0;
+ }
+ }
+ if (safeTime) {
+ if (safeTime >= startTime) {
+ process.nextTick(() => {
+ if (this.closed) return;
+ if (filePath === this.path) {
+ watcher.emit(
+ "change",
+ filePath,
+ safeTime,
+ "watch (outdated on attach)",
+ true
+ );
+ } else {
+ watcher.emit(
+ "change",
+ safeTime,
+ "watch (outdated on attach)",
+ true
+ );
+ }
+ });
+ }
+ } else if (this.initialScan) {
+ if (this.initialScanRemoved.has(filePath)) {
+ process.nextTick(() => {
+ if (this.closed) return;
+ watcher.emit("remove");
+ });
+ }
+ } else if (
+ filePath !== this.path &&
+ !this.directories.has(filePath) &&
+ watcher.checkStartTime(this.initialScanFinished, false)
+ ) {
+ process.nextTick(() => {
+ if (this.closed) return;
+ watcher.emit("initial-missing", "watch (missing on attach)");
+ });
+ }
+ return watcher;
+ }
+
+ onWatchEvent(eventType, filename) {
+ if (this.closed) return;
+ if (!filename) {
+ // In some cases no filename is provided
+ // This seem to happen on windows
+ // So some event happened but we don't know which file is affected
+ // We have to do a full scan of the directory
+ this.doScan(false);
+ return;
+ }
+
+ const filePath = path.join(this.path, filename);
+ if (this.ignored(filePath)) return;
+
+ if (this._activeEvents.get(filename) === undefined) {
+ this._activeEvents.set(filename, false);
+ const checkStats = () => {
+ if (this.closed) return;
+ this._activeEvents.set(filename, false);
+ fs.lstat(filePath, (err, stats) => {
+ if (this.closed) return;
+ if (this._activeEvents.get(filename) === true) {
+ process.nextTick(checkStats);
+ return;
+ }
+ this._activeEvents.delete(filename);
+ // ENOENT happens when the file/directory doesn't exist
+ // EPERM happens when the containing directory doesn't exist
+ if (err) {
+ if (
+ err.code !== "ENOENT" &&
+ err.code !== "EPERM" &&
+ err.code !== "EBUSY"
+ ) {
+ this.onStatsError(err);
+ } else {
+ if (filename === path.basename(this.path)) {
+ // This may indicate that the directory itself was removed
+ if (!fs.existsSync(this.path)) {
+ this.onDirectoryRemoved("stat failed");
+ }
+ }
+ }
+ }
+ this.lastWatchEvent = Date.now();
+ if (!stats) {
+ this.setMissing(filePath, false, eventType);
+ } else if (stats.isDirectory()) {
+ this.setDirectory(
+ filePath,
+ +stats.birthtime || 1,
+ false,
+ eventType
+ );
+ } else if (stats.isFile() || stats.isSymbolicLink()) {
+ if (stats.mtime) {
+ ensureFsAccuracy(stats.mtime);
+ }
+ this.setFileTime(
+ filePath,
+ +stats.mtime || +stats.ctime || 1,
+ false,
+ false,
+ eventType
+ );
+ }
+ });
+ };
+ process.nextTick(checkStats);
+ } else {
+ this._activeEvents.set(filename, true);
+ }
+ }
+
+ onWatcherError(err) {
+ if (this.closed) return;
+ if (err) {
+ if (err.code !== "EPERM" && err.code !== "ENOENT") {
+ console.error("Watchpack Error (watcher): " + err);
+ }
+ this.onDirectoryRemoved("watch error");
+ }
+ }
+
+ onStatsError(err) {
+ if (err) {
+ console.error("Watchpack Error (stats): " + err);
+ }
+ }
+
+ onScanError(err) {
+ if (err) {
+ console.error("Watchpack Error (initial scan): " + err);
+ }
+ this.onScanFinished();
+ }
+
+ onScanFinished() {
+ if (this.polledWatching) {
+ this.timeout = setTimeout(() => {
+ if (this.closed) return;
+ this.doScan(false);
+ }, this.polledWatching);
+ }
+ }
+
+ onDirectoryRemoved(reason) {
+ if (this.watcher) {
+ this.watcher.close();
+ this.watcher = null;
+ }
+ this.watchInParentDirectory();
+ const type = `directory-removed (${reason})`;
+ for (const directory of this.directories.keys()) {
+ this.setMissing(directory, null, type);
+ }
+ for (const file of this.files.keys()) {
+ this.setMissing(file, null, type);
+ }
+ }
+
+ watchInParentDirectory() {
+ if (!this.parentWatcher) {
+ const parentDir = path.dirname(this.path);
+ // avoid watching in the root directory
+ // removing directories in the root directory is not supported
+ if (path.dirname(parentDir) === parentDir) return;
+
+ this.parentWatcher = this.watcherManager.watchFile(this.path, 1);
+ this.parentWatcher.on("change", (mtime, type) => {
+ if (this.closed) return;
+
+ // On non-osx platforms we don't need this watcher to detect
+ // directory removal, as an EPERM error indicates that
+ if ((!IS_OSX || this.polledWatching) && this.parentWatcher) {
+ this.parentWatcher.close();
+ this.parentWatcher = null;
+ }
+ // Try to create the watcher when parent directory is found
+ if (!this.watcher) {
+ this.createWatcher();
+ this.doScan(false);
+
+ // directory was created so we emit an event
+ this.forEachWatcher(this.path, w =>
+ w.emit("change", this.path, mtime, type, false)
+ );
+ }
+ });
+ this.parentWatcher.on("remove", () => {
+ this.onDirectoryRemoved("parent directory removed");
+ });
+ }
+ }
+
+ doScan(initial) {
+ if (this.scanning) {
+ if (this.scanAgain) {
+ if (!initial) this.scanAgainInitial = false;
+ } else {
+ this.scanAgain = true;
+ this.scanAgainInitial = initial;
+ }
+ return;
+ }
+ this.scanning = true;
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ this.timeout = undefined;
+ }
+ process.nextTick(() => {
+ if (this.closed) return;
+ 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;
+ }
+ 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 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) {
+ 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 === "EACCES" ||
+ err2.code === "EBUSY"
+ ) {
+ this.setMissing(itemPath, initial, "scan (" + err2.code + ")");
+ } else {
+ this.onScanError(err2);
+ }
+ itemFinished();
+ return;
+ }
+ if (stats.isFile() || stats.isSymbolicLink()) {
+ if (stats.mtime) {
+ ensureFsAccuracy(stats.mtime);
+ }
+ this.setFileTime(
+ itemPath,
+ +stats.mtime || +stats.ctime || 1,
+ initial,
+ true,
+ "scan (file)"
+ );
+ } else if (stats.isDirectory()) {
+ if (!initial || !this.directories.has(itemPath))
+ this.setDirectory(
+ itemPath,
+ +stats.birthtime || 1,
+ initial,
+ "scan (dir)"
+ );
+ }
+ itemFinished();
+ });
+ }
+ itemFinished();
+ });
+ });
+ }
+
+ getTimes() {
+ const obj = Object.create(null);
+ let safeTime = this.lastWatchEvent;
+ for (const [file, entry] of this.files) {
+ fixupEntryAccuracy(entry);
+ safeTime = Math.max(safeTime, entry.safeTime);
+ obj[file] = Math.max(entry.safeTime, entry.timestamp);
+ }
+ if (this.nestedWatching) {
+ for (const w of this.directories.values()) {
+ const times = w.directoryWatcher.getTimes();
+ for (const file of Object.keys(times)) {
+ const time = times[file];
+ safeTime = Math.max(safeTime, time);
+ obj[file] = time;
+ }
+ }
+ obj[this.path] = safeTime;
+ }
+ if (!this.initialScan) {
+ for (const watchers of this.watchers.values()) {
+ for (const watcher of watchers) {
+ const path = watcher.path;
+ if (!Object.prototype.hasOwnProperty.call(obj, path)) {
+ obj[path] = null;
+ }
+ }
+ }
+ }
+ return obj;
+ }
+
+ collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
+ let safeTime = this.lastWatchEvent;
+ for (const [file, entry] of this.files) {
+ fixupEntryAccuracy(entry);
+ safeTime = Math.max(safeTime, entry.safeTime);
+ fileTimestamps.set(file, entry);
+ }
+ if (this.nestedWatching) {
+ for (const w of this.directories.values()) {
+ safeTime = Math.max(
+ safeTime,
+ w.directoryWatcher.collectTimeInfoEntries(
+ fileTimestamps,
+ directoryTimestamps
+ )
+ );
+ }
+ 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
+ // but maybe another DirectoryWatcher has info
+ fileTimestamps.set(dir, EXISTANCE_ONLY_TIME_ENTRY);
+ if (!directoryTimestamps.has(dir))
+ directoryTimestamps.set(dir, 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 (!fileTimestamps.has(path)) {
+ fileTimestamps.set(path, null);
+ }
+ }
+ }
+ }
+ return safeTime;
+ }
+
+ close() {
+ this.closed = true;
+ this.initialScan = false;
+ if (this.watcher) {
+ this.watcher.close();
+ this.watcher = null;
+ }
+ if (this.nestedWatching) {
+ for (const w of this.directories.values()) {
+ w.close();
+ }
+ this.directories.clear();
+ }
+ if (this.parentWatcher) {
+ this.parentWatcher.close();
+ this.parentWatcher = null;
+ }
+ this.emit("closed");
+ }
+}
+
+module.exports = DirectoryWatcher;
+module.exports.EXISTANCE_ONLY_TIME_ENTRY = EXISTANCE_ONLY_TIME_ENTRY;
+
+function fixupEntryAccuracy(entry) {
+ if (entry.accuracy > FS_ACCURACY) {
+ entry.safeTime = entry.safeTime - entry.accuracy + FS_ACCURACY;
+ entry.accuracy = FS_ACCURACY;
+ }
+}
+
+function ensureFsAccuracy(mtime) {
+ if (!mtime) return;
+ if (FS_ACCURACY > 1 && mtime % 1 !== 0) FS_ACCURACY = 1;
+ else if (FS_ACCURACY > 10 && mtime % 10 !== 0) FS_ACCURACY = 10;
+ else if (FS_ACCURACY > 100 && mtime % 100 !== 0) FS_ACCURACY = 100;
+ else if (FS_ACCURACY > 1000 && mtime % 1000 !== 0) FS_ACCURACY = 1000;
+}
diff --git a/node_modules/watchpack/lib/LinkResolver.js b/node_modules/watchpack/lib/LinkResolver.js
new file mode 100644
index 00000000..daea90bf
--- /dev/null
+++ b/node_modules/watchpack/lib/LinkResolver.js
@@ -0,0 +1,107 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+
+// macOS, Linux, and Windows all rely on these errors
+const EXPECTED_ERRORS = new Set(["EINVAL", "ENOENT"]);
+
+// On Windows there is also this error in some cases
+if (process.platform === "win32") EXPECTED_ERRORS.add("UNKNOWN");
+
+class LinkResolver {
+ constructor() {
+ this.cache = new Map();
+ }
+
+ /**
+ * @param {string} file path to file or directory
+ * @returns {string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file)
+ */
+ resolve(file) {
+ const cacheEntry = this.cache.get(file);
+ if (cacheEntry !== undefined) {
+ return cacheEntry;
+ }
+ const parent = path.dirname(file);
+ if (parent === file) {
+ // At root of filesystem there can't be a link
+ const result = Object.freeze([file]);
+ this.cache.set(file, result);
+ return result;
+ }
+ // resolve the parent directory to find links there and get the real path
+ const parentResolved = this.resolve(parent);
+ let realFile = file;
+
+ // is the parent directory really somewhere else?
+ if (parentResolved[0] !== parent) {
+ // get the real location of file
+ const basename = path.basename(file);
+ realFile = path.resolve(parentResolved[0], basename);
+ }
+ // try to read the link content
+ try {
+ const linkContent = fs.readlinkSync(realFile);
+
+ // resolve the link content relative to the parent directory
+ const resolvedLink = path.resolve(parentResolved[0], linkContent);
+
+ // recursive resolve the link content for more links in the structure
+ const linkResolved = this.resolve(resolvedLink);
+
+ // merge parent and link resolve results
+ let result;
+ if (linkResolved.length > 1 && parentResolved.length > 1) {
+ // when both contain links we need to duplicate them with a Set
+ const resultSet = new Set(linkResolved);
+ // add the link
+ resultSet.add(realFile);
+ // add all symlinks of the parent
+ for (let i = 1; i < parentResolved.length; i++) {
+ resultSet.add(parentResolved[i]);
+ }
+ result = Object.freeze(Array.from(resultSet));
+ } else if (parentResolved.length > 1) {
+ // we have links in the parent but not for the link content location
+ result = parentResolved.slice();
+ result[0] = linkResolved[0];
+ // add the link
+ result.push(realFile);
+ Object.freeze(result);
+ } else if (linkResolved.length > 1) {
+ // we can return the link content location result
+ result = linkResolved.slice();
+ // add the link
+ result.push(realFile);
+ Object.freeze(result);
+ } else {
+ // neither link content location nor parent have links
+ // this link is the only link here
+ result = Object.freeze([
+ // the resolve real location
+ linkResolved[0],
+ // add the link
+ realFile
+ ]);
+ }
+ this.cache.set(file, result);
+ return result;
+ } catch (e) {
+ if (!EXPECTED_ERRORS.has(e.code)) {
+ throw e;
+ }
+ // no link
+ const result = parentResolved.slice();
+ result[0] = realFile;
+ Object.freeze(result);
+ this.cache.set(file, result);
+ return result;
+ }
+ }
+}
+module.exports = LinkResolver;
diff --git a/node_modules/watchpack/lib/getWatcherManager.js b/node_modules/watchpack/lib/getWatcherManager.js
new file mode 100644
index 00000000..e0dcd6ac
--- /dev/null
+++ b/node_modules/watchpack/lib/getWatcherManager.js
@@ -0,0 +1,52 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const path = require("path");
+const DirectoryWatcher = require("./DirectoryWatcher");
+
+class WatcherManager {
+ constructor(options) {
+ this.options = options;
+ this.directoryWatchers = new Map();
+ }
+
+ getDirectoryWatcher(directory) {
+ const watcher = this.directoryWatchers.get(directory);
+ if (watcher === undefined) {
+ const newWatcher = new DirectoryWatcher(this, directory, this.options);
+ this.directoryWatchers.set(directory, newWatcher);
+ newWatcher.on("closed", () => {
+ this.directoryWatchers.delete(directory);
+ });
+ return newWatcher;
+ }
+ return watcher;
+ }
+
+ watchFile(p, startTime) {
+ const directory = path.dirname(p);
+ if (directory === p) return null;
+ return this.getDirectoryWatcher(directory).watch(p, startTime);
+ }
+
+ watchDirectory(directory, startTime) {
+ return this.getDirectoryWatcher(directory).watch(directory, startTime);
+ }
+}
+
+const watcherManagers = new WeakMap();
+/**
+ * @param {object} options options
+ * @returns {WatcherManager} the watcher manager
+ */
+module.exports = options => {
+ const watcherManager = watcherManagers.get(options);
+ if (watcherManager !== undefined) return watcherManager;
+ const newWatcherManager = new WatcherManager(options);
+ watcherManagers.set(options, newWatcherManager);
+ return newWatcherManager;
+};
+module.exports.WatcherManager = WatcherManager;
diff --git a/node_modules/watchpack/lib/reducePlan.js b/node_modules/watchpack/lib/reducePlan.js
new file mode 100644
index 00000000..b281ed69
--- /dev/null
+++ b/node_modules/watchpack/lib/reducePlan.js
@@ -0,0 +1,138 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const path = require("path");
+
+/**
+ * @template T
+ * @typedef {Object} TreeNode
+ * @property {string} filePath
+ * @property {TreeNode} parent
+ * @property {TreeNode[]} children
+ * @property {number} entries
+ * @property {boolean} active
+ * @property {T[] | T | undefined} value
+ */
+
+/**
+ * @template T
+ * @param {Map<string, T[] | T} plan
+ * @param {number} limit
+ * @returns {Map<string, Map<T, string>>} the new plan
+ */
+module.exports = (plan, limit) => {
+ const treeMap = new Map();
+ // Convert to tree
+ for (const [filePath, value] of plan) {
+ treeMap.set(filePath, {
+ filePath,
+ parent: undefined,
+ children: undefined,
+ entries: 1,
+ active: true,
+ value
+ });
+ }
+ let currentCount = treeMap.size;
+ // Create parents and calculate sum of entries
+ for (const node of treeMap.values()) {
+ const parentPath = path.dirname(node.filePath);
+ if (parentPath !== node.filePath) {
+ let parent = treeMap.get(parentPath);
+ if (parent === undefined) {
+ parent = {
+ filePath: parentPath,
+ parent: undefined,
+ children: [node],
+ entries: node.entries,
+ active: false,
+ value: undefined
+ };
+ treeMap.set(parentPath, parent);
+ node.parent = parent;
+ } else {
+ node.parent = parent;
+ if (parent.children === undefined) {
+ parent.children = [node];
+ } else {
+ parent.children.push(node);
+ }
+ do {
+ parent.entries += node.entries;
+ parent = parent.parent;
+ } while (parent);
+ }
+ }
+ }
+ // Reduce until limit reached
+ while (currentCount > limit) {
+ // Select node that helps reaching the limit most effectively without overmerging
+ const overLimit = currentCount - limit;
+ let bestNode = undefined;
+ let bestCost = Infinity;
+ for (const node of treeMap.values()) {
+ if (node.entries <= 1 || !node.children || !node.parent) continue;
+ if (node.children.length === 0) continue;
+ if (node.children.length === 1 && !node.value) continue;
+ // Try to select the node with has just a bit more entries than we need to reduce
+ // When just a bit more is over 30% over the limit,
+ // also consider just a bit less entries then we need to reduce
+ const cost =
+ node.entries - 1 >= overLimit
+ ? node.entries - 1 - overLimit
+ : overLimit - node.entries + 1 + limit * 0.3;
+ if (cost < bestCost) {
+ bestNode = node;
+ bestCost = cost;
+ }
+ }
+ if (!bestNode) break;
+ // Merge all children
+ const reduction = bestNode.entries - 1;
+ bestNode.active = true;
+ bestNode.entries = 1;
+ currentCount -= reduction;
+ let parent = bestNode.parent;
+ while (parent) {
+ parent.entries -= reduction;
+ parent = parent.parent;
+ }
+ const queue = new Set(bestNode.children);
+ for (const node of queue) {
+ node.active = false;
+ node.entries = 0;
+ if (node.children) {
+ for (const child of node.children) queue.add(child);
+ }
+ }
+ }
+ // Write down new plan
+ const newPlan = new Map();
+ for (const rootNode of treeMap.values()) {
+ if (!rootNode.active) continue;
+ const map = new Map();
+ const queue = new Set([rootNode]);
+ for (const node of queue) {
+ if (node.active && node !== rootNode) continue;
+ if (node.value) {
+ if (Array.isArray(node.value)) {
+ for (const item of node.value) {
+ map.set(item, node.filePath);
+ }
+ } else {
+ map.set(node.value, node.filePath);
+ }
+ }
+ if (node.children) {
+ for (const child of node.children) {
+ queue.add(child);
+ }
+ }
+ }
+ newPlan.set(rootNode.filePath, map);
+ }
+ return newPlan;
+};
diff --git a/node_modules/watchpack/lib/watchEventSource.js b/node_modules/watchpack/lib/watchEventSource.js
new file mode 100644
index 00000000..6df9ab0d
--- /dev/null
+++ b/node_modules/watchpack/lib/watchEventSource.js
@@ -0,0 +1,333 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+const { EventEmitter } = require("events");
+const reducePlan = require("./reducePlan");
+
+const IS_OSX = require("os").platform() === "darwin";
+const IS_WIN = require("os").platform() === "win32";
+const SUPPORTS_RECURSIVE_WATCHING = IS_OSX || IS_WIN;
+
+const watcherLimit =
+ +process.env.WATCHPACK_WATCHER_LIMIT || (IS_OSX ? 2000 : 10000);
+
+const recursiveWatcherLogging = !!process.env
+ .WATCHPACK_RECURSIVE_WATCHER_LOGGING;
+
+let isBatch = false;
+let watcherCount = 0;
+
+/** @type {Map<Watcher, string>} */
+const pendingWatchers = new Map();
+
+/** @type {Map<string, RecursiveWatcher>} */
+const recursiveWatchers = new Map();
+
+/** @type {Map<string, DirectWatcher>} */
+const directWatchers = new Map();
+
+/** @type {Map<Watcher, RecursiveWatcher | DirectWatcher>} */
+const underlyingWatcher = new Map();
+
+class DirectWatcher {
+ constructor(filePath) {
+ this.filePath = filePath;
+ this.watchers = new Set();
+ this.watcher = undefined;
+ try {
+ const watcher = fs.watch(filePath);
+ this.watcher = watcher;
+ watcher.on("change", (type, filename) => {
+ for (const w of this.watchers) {
+ w.emit("change", type, filename);
+ }
+ });
+ watcher.on("error", error => {
+ for (const w of this.watchers) {
+ w.emit("error", error);
+ }
+ });
+ } catch (err) {
+ process.nextTick(() => {
+ for (const w of this.watchers) {
+ w.emit("error", err);
+ }
+ });
+ }
+ watcherCount++;
+ }
+
+ add(watcher) {
+ underlyingWatcher.set(watcher, this);
+ this.watchers.add(watcher);
+ }
+
+ remove(watcher) {
+ this.watchers.delete(watcher);
+ if (this.watchers.size === 0) {
+ directWatchers.delete(this.filePath);
+ watcherCount--;
+ if (this.watcher) this.watcher.close();
+ }
+ }
+
+ getWatchers() {
+ return this.watchers;
+ }
+}
+
+class RecursiveWatcher {
+ constructor(rootPath) {
+ this.rootPath = rootPath;
+ /** @type {Map<Watcher, string>} */
+ this.mapWatcherToPath = new Map();
+ /** @type {Map<string, Set<Watcher>>} */
+ this.mapPathToWatchers = new Map();
+ this.watcher = undefined;
+ try {
+ const watcher = fs.watch(rootPath, {
+ recursive: true
+ });
+ this.watcher = watcher;
+ watcher.on("change", (type, filename) => {
+ if (!filename) {
+ if (recursiveWatcherLogging) {
+ process.stderr.write(
+ `[watchpack] dispatch ${type} event in recursive watcher (${this.rootPath}) to all watchers\n`
+ );
+ }
+ for (const w of this.mapWatcherToPath.keys()) {
+ w.emit("change", type);
+ }
+ } else {
+ const dir = path.dirname(filename);
+ const watchers = this.mapPathToWatchers.get(dir);
+ if (recursiveWatcherLogging) {
+ process.stderr.write(
+ `[watchpack] dispatch ${type} event in recursive watcher (${
+ this.rootPath
+ }) for '${filename}' to ${
+ watchers ? watchers.size : 0
+ } watchers\n`
+ );
+ }
+ if (watchers === undefined) return;
+ for (const w of watchers) {
+ w.emit("change", type, path.basename(filename));
+ }
+ }
+ });
+ watcher.on("error", error => {
+ for (const w of this.mapWatcherToPath.keys()) {
+ w.emit("error", error);
+ }
+ });
+ } catch (err) {
+ process.nextTick(() => {
+ for (const w of this.mapWatcherToPath.keys()) {
+ w.emit("error", err);
+ }
+ });
+ }
+ watcherCount++;
+ if (recursiveWatcherLogging) {
+ process.stderr.write(
+ `[watchpack] created recursive watcher at ${rootPath}\n`
+ );
+ }
+ }
+
+ add(filePath, watcher) {
+ underlyingWatcher.set(watcher, this);
+ const subpath = filePath.slice(this.rootPath.length + 1) || ".";
+ this.mapWatcherToPath.set(watcher, subpath);
+ const set = this.mapPathToWatchers.get(subpath);
+ if (set === undefined) {
+ const newSet = new Set();
+ newSet.add(watcher);
+ this.mapPathToWatchers.set(subpath, newSet);
+ } else {
+ set.add(watcher);
+ }
+ }
+
+ remove(watcher) {
+ const subpath = this.mapWatcherToPath.get(watcher);
+ if (!subpath) return;
+ this.mapWatcherToPath.delete(watcher);
+ const set = this.mapPathToWatchers.get(subpath);
+ set.delete(watcher);
+ if (set.size === 0) {
+ this.mapPathToWatchers.delete(subpath);
+ }
+ if (this.mapWatcherToPath.size === 0) {
+ recursiveWatchers.delete(this.rootPath);
+ watcherCount--;
+ if (this.watcher) this.watcher.close();
+ if (recursiveWatcherLogging) {
+ process.stderr.write(
+ `[watchpack] closed recursive watcher at ${this.rootPath}\n`
+ );
+ }
+ }
+ }
+
+ getWatchers() {
+ return this.mapWatcherToPath;
+ }
+}
+
+class Watcher extends EventEmitter {
+ close() {
+ if (pendingWatchers.has(this)) {
+ pendingWatchers.delete(this);
+ return;
+ }
+ const watcher = underlyingWatcher.get(this);
+ watcher.remove(this);
+ underlyingWatcher.delete(this);
+ }
+}
+
+const createDirectWatcher = filePath => {
+ const existing = directWatchers.get(filePath);
+ if (existing !== undefined) return existing;
+ const w = new DirectWatcher(filePath);
+ directWatchers.set(filePath, w);
+ return w;
+};
+
+const createRecursiveWatcher = rootPath => {
+ const existing = recursiveWatchers.get(rootPath);
+ if (existing !== undefined) return existing;
+ const w = new RecursiveWatcher(rootPath);
+ recursiveWatchers.set(rootPath, w);
+ return w;
+};
+
+const execute = () => {
+ /** @type {Map<string, Watcher[] | Watcher>} */
+ const map = new Map();
+ const addWatcher = (watcher, filePath) => {
+ const entry = map.get(filePath);
+ if (entry === undefined) {
+ map.set(filePath, watcher);
+ } else if (Array.isArray(entry)) {
+ entry.push(watcher);
+ } else {
+ map.set(filePath, [entry, watcher]);
+ }
+ };
+ for (const [watcher, filePath] of pendingWatchers) {
+ addWatcher(watcher, filePath);
+ }
+ pendingWatchers.clear();
+
+ // Fast case when we are not reaching the limit
+ if (!SUPPORTS_RECURSIVE_WATCHING || watcherLimit - watcherCount >= map.size) {
+ // Create watchers for all entries in the map
+ for (const [filePath, entry] of map) {
+ const w = createDirectWatcher(filePath);
+ if (Array.isArray(entry)) {
+ for (const item of entry) w.add(item);
+ } else {
+ w.add(entry);
+ }
+ }
+ return;
+ }
+
+ // Reconsider existing watchers to improving watch plan
+ for (const watcher of recursiveWatchers.values()) {
+ for (const [w, subpath] of watcher.getWatchers()) {
+ addWatcher(w, path.join(watcher.rootPath, subpath));
+ }
+ }
+ for (const watcher of directWatchers.values()) {
+ for (const w of watcher.getWatchers()) {
+ addWatcher(w, watcher.filePath);
+ }
+ }
+
+ // Merge map entries to keep watcher limit
+ // Create a 10% buffer to be able to enter fast case more often
+ const plan = reducePlan(map, watcherLimit * 0.9);
+
+ // Update watchers for all entries in the map
+ for (const [filePath, entry] of plan) {
+ if (entry.size === 1) {
+ for (const [watcher, filePath] of entry) {
+ const w = createDirectWatcher(filePath);
+ const old = underlyingWatcher.get(watcher);
+ if (old === w) continue;
+ w.add(watcher);
+ if (old !== undefined) old.remove(watcher);
+ }
+ } else {
+ const filePaths = new Set(entry.values());
+ if (filePaths.size > 1) {
+ const w = createRecursiveWatcher(filePath);
+ for (const [watcher, watcherPath] of entry) {
+ const old = underlyingWatcher.get(watcher);
+ if (old === w) continue;
+ w.add(watcherPath, watcher);
+ if (old !== undefined) old.remove(watcher);
+ }
+ } else {
+ for (const filePath of filePaths) {
+ const w = createDirectWatcher(filePath);
+ for (const watcher of entry.keys()) {
+ const old = underlyingWatcher.get(watcher);
+ if (old === w) continue;
+ w.add(watcher);
+ if (old !== undefined) old.remove(watcher);
+ }
+ }
+ }
+ }
+ }
+};
+
+exports.watch = filePath => {
+ const watcher = new Watcher();
+ // Find an existing watcher
+ const directWatcher = directWatchers.get(filePath);
+ if (directWatcher !== undefined) {
+ directWatcher.add(watcher);
+ return watcher;
+ }
+ let current = filePath;
+ for (;;) {
+ const recursiveWatcher = recursiveWatchers.get(current);
+ if (recursiveWatcher !== undefined) {
+ recursiveWatcher.add(filePath, watcher);
+ return watcher;
+ }
+ const parent = path.dirname(current);
+ if (parent === current) break;
+ current = parent;
+ }
+ // Queue up watcher for creation
+ pendingWatchers.set(watcher, filePath);
+ if (!isBatch) execute();
+ return watcher;
+};
+
+exports.batch = fn => {
+ isBatch = true;
+ try {
+ fn();
+ } finally {
+ isBatch = false;
+ execute();
+ }
+};
+
+exports.getNumberOfWatchers = () => {
+ return watcherCount;
+};
diff --git a/node_modules/watchpack/lib/watchpack.js b/node_modules/watchpack/lib/watchpack.js
new file mode 100644
index 00000000..0b567fce
--- /dev/null
+++ b/node_modules/watchpack/lib/watchpack.js
@@ -0,0 +1,393 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+"use strict";
+
+const getWatcherManager = require("./getWatcherManager");
+const LinkResolver = require("./LinkResolver");
+const EventEmitter = require("events").EventEmitter;
+const globToRegExp = require("glob-to-regexp");
+const watchEventSource = require("./watchEventSource");
+
+const EMPTY_ARRAY = [];
+const EMPTY_OPTIONS = {};
+
+function addWatchersToSet(watchers, set) {
+ for (const ww of watchers) {
+ const w = ww.watcher;
+ if (!set.has(w.directoryWatcher)) {
+ set.add(w.directoryWatcher);
+ }
+ }
+}
+
+const stringToRegexp = ignored => {
+ if (ignored.length === 0) {
+ return;
+ }
+ const source = globToRegExp(ignored, { globstar: true, extended: true })
+ .source;
+ return source.slice(0, source.length - 1) + "(?:$|\\/)";
+};
+
+const ignoredToFunction = ignored => {
+ if (Array.isArray(ignored)) {
+ const stringRegexps = ignored.map(i => stringToRegexp(i)).filter(Boolean);
+ if (stringRegexps.length === 0) {
+ return () => false;
+ }
+ const regexp = new RegExp(stringRegexps.join("|"));
+ return x => regexp.test(x.replace(/\\/g, "/"));
+ } else if (typeof ignored === "string") {
+ const stringRegexp = stringToRegexp(ignored);
+ if (!stringRegexp) {
+ return () => false;
+ }
+ const regexp = new RegExp(stringRegexp);
+ 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;
+ } else if (ignored) {
+ throw new Error(`Invalid option for 'ignored': ${ignored}`);
+ } else {
+ return () => false;
+ }
+};
+
+const normalizeOptions = options => {
+ return {
+ followSymlinks: !!options.followSymlinks,
+ ignored: ignoredToFunction(options.ignored),
+ poll: options.poll
+ };
+};
+
+const normalizeCache = new WeakMap();
+const cachedNormalizeOptions = options => {
+ const cacheEntry = normalizeCache.get(options);
+ if (cacheEntry !== undefined) return cacheEntry;
+ const normalized = normalizeOptions(options);
+ normalizeCache.set(options, normalized);
+ return normalized;
+};
+
+class WatchpackFileWatcher {
+ constructor(watchpack, watcher, files) {
+ 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) {
+ 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 {
+ constructor(options) {
+ super();
+ 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.fileWatchers = new Map();
+ this.directoryWatchers = new Map();
+ this._missing = new Set();
+ this.startTime = undefined;
+ this.paused = false;
+ this.aggregatedChanges = new Set();
+ this.aggregatedRemovals = new Set();
+ this.aggregateTimer = undefined;
+ this._onTimeout = this._onTimeout.bind(this);
+ }
+
+ watch(arg1, arg2, arg3) {
+ let files, directories, missing, startTime;
+ if (!arg2) {
+ ({
+ files = EMPTY_ARRAY,
+ directories = EMPTY_ARRAY,
+ missing = EMPTY_ARRAY,
+ startTime
+ } = arg1);
+ } else {
+ files = arg1;
+ directories = arg2;
+ missing = EMPTY_ARRAY;
+ startTime = arg3;
+ }
+ this.paused = false;
+ const fileWatchers = this.fileWatchers;
+ const directoryWatchers = this.directoryWatchers;
+ const ignored = this.watcherOptions.ignored;
+ const filter = path => !ignored(path);
+ const addToMap = (map, key, item) => {
+ const list = map.get(key);
+ if (list === undefined) {
+ map.set(key, item);
+ } else if (Array.isArray(list)) {
+ list.push(item);
+ } else {
+ map.set(key, [list, item]);
+ }
+ };
+ const fileWatchersNeeded = new Map();
+ const directoryWatchersNeeded = new Map();
+ const missingFiles = new Set();
+ if (this.watcherOptions.followSymlinks) {
+ const resolver = new LinkResolver();
+ for (const file of files) {
+ if (filter(file)) {
+ for (const innerFile of resolver.resolve(file)) {
+ if (file === innerFile || filter(innerFile)) {
+ addToMap(fileWatchersNeeded, innerFile, file);
+ }
+ }
+ }
+ }
+ for (const file of missing) {
+ if (filter(file)) {
+ for (const innerFile of resolver.resolve(file)) {
+ if (file === innerFile || filter(innerFile)) {
+ missingFiles.add(file);
+ addToMap(fileWatchersNeeded, innerFile, file);
+ }
+ }
+ }
+ }
+ for (const dir of directories) {
+ if (filter(dir)) {
+ let first = true;
+ for (const innerItem of resolver.resolve(dir)) {
+ if (filter(innerItem)) {
+ addToMap(
+ first ? directoryWatchersNeeded : fileWatchersNeeded,
+ innerItem,
+ dir
+ );
+ }
+ first = false;
+ }
+ }
+ }
+ } else {
+ for (const file of files) {
+ if (filter(file)) {
+ addToMap(fileWatchersNeeded, file, file);
+ }
+ }
+ for (const file of missing) {
+ if (filter(file)) {
+ missingFiles.add(file);
+ addToMap(fileWatchersNeeded, file, file);
+ }
+ }
+ for (const dir of directories) {
+ if (filter(dir)) {
+ addToMap(directoryWatchersNeeded, dir, dir);
+ }
+ }
+ }
+ // Close unneeded old watchers
+ // and update existing watchers
+ for (const [key, w] of fileWatchers) {
+ const needed = fileWatchersNeeded.get(key);
+ if (needed === undefined) {
+ w.close();
+ fileWatchers.delete(key);
+ } else {
+ w.update(needed);
+ fileWatchersNeeded.delete(key);
+ }
+ }
+ for (const [key, w] of directoryWatchers) {
+ const needed = directoryWatchersNeeded.get(key);
+ if (needed === undefined) {
+ w.close();
+ directoryWatchers.delete(key);
+ } else {
+ w.update(needed);
+ directoryWatchersNeeded.delete(key);
+ }
+ }
+ // 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) {
+ fileWatchers.set(key, new WatchpackFileWatcher(this, watcher, files));
+ }
+ }
+ for (const [key, directories] of directoryWatchersNeeded) {
+ const watcher = this.watcherManager.watchDirectory(key, startTime);
+ if (watcher) {
+ directoryWatchers.set(
+ key,
+ new WatchpackDirectoryWatcher(this, watcher, directories)
+ );
+ }
+ }
+ });
+ this._missing = missingFiles;
+ this.startTime = startTime;
+ }
+
+ close() {
+ this.paused = true;
+ 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();
+ }
+
+ pause() {
+ this.paused = true;
+ if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
+ }
+
+ getTimes() {
+ const directoryWatchers = new Set();
+ addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
+ addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
+ const obj = Object.create(null);
+ for (const w of directoryWatchers) {
+ const times = w.getTimes();
+ for (const file of Object.keys(times)) obj[file] = times[file];
+ }
+ return obj;
+ }
+
+ getTimeInfoEntries() {
+ const map = new Map();
+ 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);
+ this.aggregateTimer = undefined;
+ }
+ const changes = this.aggregatedChanges;
+ const removals = this.aggregatedRemovals;
+ this.aggregatedChanges = new Set();
+ this.aggregatedRemovals = new Set();
+ return { changes, removals };
+ }
+
+ _onChange(item, mtime, file, type) {
+ file = file || item;
+ if (!this.paused) {
+ this.emit("change", file, mtime, type);
+ if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
+ this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
+ }
+ this.aggregatedRemovals.delete(item);
+ this.aggregatedChanges.add(item);
+ }
+
+ _onRemove(item, file, type) {
+ file = file || item;
+ if (!this.paused) {
+ this.emit("remove", file, type);
+ if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
+ this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
+ }
+ this.aggregatedChanges.delete(item);
+ this.aggregatedRemovals.add(item);
+ }
+
+ _onTimeout() {
+ this.aggregateTimer = undefined;
+ const changes = this.aggregatedChanges;
+ const removals = this.aggregatedRemovals;
+ this.aggregatedChanges = new Set();
+ this.aggregatedRemovals = new Set();
+ this.emit("aggregated", changes, removals);
+ }
+}
+
+module.exports = Watchpack;