Feat: Multiplayer sessions added using CRUD database.

This commit is contained in:
2026-02-10 11:49:38 +00:00
parent bbbd21d4ad
commit fa81fddbd4
6850 changed files with 808827 additions and 8 deletions

20
node_modules/watchpack/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

133
node_modules/watchpack/README.md generated vendored Normal file
View File

@@ -0,0 +1,133 @@
# watchpack
Wrapper library for directory and file watching.
[![Test](https://github.com/webpack/watchpack/actions/workflows/test.yml/badge.svg)](https://github.com/webpack/watchpack/actions/workflows/test.yml)
[![Codecov](https://codecov.io/gh/webpack/watchpack/graph/badge.svg?token=8xk2OrrxWm)](https://codecov.io/gh/webpack/watchpack)
[![Downloads](https://img.shields.io/npm/dm/watchpack.svg)](https://www.npmjs.com/package/watchpack)
## Concept
watchpack high level API doesn't map directly to watchers. Instead a three level architecture ensures that for each directory only a single watcher exists.
- The high level API requests `DirectoryWatchers` from a `WatcherManager`, which ensures that only a single `DirectoryWatcher` per directory is created.
- A user-faced `Watcher` can be obtained from a `DirectoryWatcher` and provides a filtered view on the `DirectoryWatcher`.
- Reference-counting is used on the `DirectoryWatcher` and `Watcher` to decide when to close them.
- The real watchers are created by the `DirectoryWatcher`.
- Files are never watched directly. This should keep the watcher count low.
- Watching can be started in the past. This way watching can start after file reading.
- Symlinks are not followed, instead the symlink is watched.
## API
```javascript
const Watchpack = require("watchpack");
const wp = new Watchpack({
// options:
aggregateTimeout: 1000,
// fire "aggregated" event when after a change for 1000ms no additional change occurred
// aggregated defaults to undefined, which doesn't fire an "aggregated" event
poll: true,
// poll: true - use polling with the default interval
// poll: 10000 - use polling with an interval of 10s
// poll defaults to undefined, which prefer native watching methods
// Note: enable polling when watching on a network path
// When WATCHPACK_POLLING environment variable is set it will override this option
followSymlinks: true,
// true: follows symlinks and watches symlinks and real files
// (This makes sense when symlinks has not been resolved yet, comes with a performance hit)
// false (default): watches only specified item they may be real files or symlinks
// (This makes sense when symlinks has already been resolved)
ignored: "**/.git",
// ignored: "string" - a glob pattern for files or folders that should not be watched
// ignored: ["string", "string"] - multiple glob patterns that should be ignored
// 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
});
// Watchpack.prototype.watch({
// files: Iterable<string>,
// directories: Iterable<string>,
// missing: Iterable<string>,
// startTime?: number
// })
wp.watch({
files: listOfFiles,
directories: listOfDirectories,
missing: listOfNotExistingItems,
startTime: Date.now() - 10000,
});
// starts watching these files and directories
// calling this again will override the files and directories
// 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
// 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 directories,
// 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 further information a remove event is emitted
// missing is assumed to not exist and no remove event is emitted
wp.on("change", (filePath, mtime, explanation) => {
// filePath: the changed file
// mtime: last modified time for the changed file
// explanation: textual information how this change was detected
});
wp.on("remove", (filePath, explanation) => {
// filePath: the removed file or directory
// explanation: textual information how this change was detected
});
wp.on("aggregated", (changes, removals) => {
// changes: a Set of all changed files
// removals: a Set of all removed files
// watchpack gives up ownership on these Sets.
});
// Watchpack.prototype.pause()
wp.pause();
// stops emitting events, but keeps watchers open
// next "watch" call can reuse the watchers
// The watcher will keep aggregating events
// which can be received with getAggregated()
// Watchpack.prototype.close()
wp.close();
// stops emitting events and closes all watchers
// Watchpack.prototype.getAggregated(): { changes: Set<string>, removals: Set<string> }
const { changes, removals } = wp.getAggregated();
// returns the current aggregated info and removes that from the watcher
// The next aggregated event won't include that info and will only emitted
// when futher changes happen
// Can also be used when paused.
// 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()
const 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()
const fileTimesOld = wp.getTimes();
// returns an object with all known change times for files
// this include timestamps from files not directly watched
// key: absolute path, value: timestamp as number
```

965
node_modules/watchpack/lib/DirectoryWatcher.js generated vendored Normal file
View File

@@ -0,0 +1,965 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { EventEmitter } = require("events");
const path = require("path");
const fs = require("graceful-fs");
const watchEventSource = require("./watchEventSource");
/** @typedef {import("./index").IgnoredFunction} IgnoredFunction */
/** @typedef {import("./index").EventType} EventType */
/** @typedef {import("./index").TimeInfoEntries} TimeInfoEntries */
/** @typedef {import("./index").Entry} Entry */
/** @typedef {import("./index").ExistenceOnlyTimeEntry} ExistenceOnlyTimeEntry */
/** @typedef {import("./index").OnlySafeTimeEntry} OnlySafeTimeEntry */
/** @typedef {import("./index").EventMap} EventMap */
/** @typedef {import("./getWatcherManager").WatcherManager} WatcherManager */
/** @typedef {import("./watchEventSource").Watcher} EventSourceWatcher */
/** @type {ExistenceOnlyTimeEntry} */
const EXISTANCE_ONLY_TIME_ENTRY = Object.freeze({});
let FS_ACCURACY = 2000;
const IS_OSX = require("os").platform() === "darwin";
const IS_WIN = require("os").platform() === "win32";
const { WATCHPACK_POLLING } = process.env;
const FORCE_POLLING =
// @ts-expect-error avoid additional checks
`${+WATCHPACK_POLLING}` === WATCHPACK_POLLING
? +WATCHPACK_POLLING
: Boolean(WATCHPACK_POLLING) && WATCHPACK_POLLING !== "false";
/**
* @param {string} str string
* @returns {string} lower cased string
*/
function withoutCase(str) {
return str.toLowerCase();
}
/**
* @param {number} times times
* @param {() => void} callback callback
* @returns {() => void} result
*/
function needCalls(times, callback) {
return function needCallsCallback() {
if (--times === 0) {
return callback();
}
};
}
/**
* @param {Entry} entry entry
*/
function fixupEntryAccuracy(entry) {
if (entry.accuracy > FS_ACCURACY) {
entry.safeTime = entry.safeTime - entry.accuracy + FS_ACCURACY;
entry.accuracy = FS_ACCURACY;
}
}
/**
* @param {number=} mtime mtime
*/
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;
}
/**
* @typedef {object} FileWatcherEvents
* @property {(type: EventType) => void} initial-missing initial missing event
* @property {(mtime: number, type: EventType, initial: boolean) => void} change change event
* @property {(type: EventType) => void} remove remove event
* @property {() => void} closed closed event
*/
/**
* @typedef {object} DirectoryWatcherEvents
* @property {(type: EventType) => void} initial-missing initial missing event
* @property {((file: string, mtime: number, type: EventType, initial: boolean) => void)} change change event
* @property {(type: EventType) => void} remove remove event
* @property {() => void} closed closed event
*/
/**
* @template {EventMap} T
* @extends {EventEmitter<{ [K in keyof T]: Parameters<T[K]> }>}
*/
class Watcher extends EventEmitter {
/**
* @param {DirectoryWatcher} directoryWatcher a directory watcher
* @param {string} target a target to watch
* @param {number=} startTime start time
*/
constructor(directoryWatcher, target, startTime) {
super();
this.directoryWatcher = directoryWatcher;
this.path = target;
this.startTime = startTime && +startTime;
}
/**
* @param {number} mtime mtime
* @param {boolean} initial true when initial, otherwise false
* @returns {boolean} true of start time less than mtile, otherwise false
*/
checkStartTime(mtime, initial) {
const { startTime } = this;
if (typeof startTime !== "number") return !initial;
return startTime <= mtime;
}
close() {
// @ts-expect-error bad typing in EventEmitter
this.emit("closed");
}
}
/** @typedef {Set<string>} InitialScanRemoved */
/**
* @typedef {object} WatchpackEvents
* @property {(target: string, mtime: string, type: EventType, initial: boolean) => void} change change event
* @property {() => void} closed closed event
*/
/**
* @typedef {object} DirectoryWatcherOptions
* @property {boolean=} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
* @property {IgnoredFunction=} ignored ignore some files from watching (glob pattern or regexp)
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
*/
/**
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
*/
class DirectoryWatcher extends EventEmitter {
/**
* @param {WatcherManager} watcherManager a watcher manager
* @param {string} directoryPath directory path
* @param {DirectoryWatcherOptions=} options options
*/
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, Entry>} */
this.files = new Map();
/** @type {Map<string, number>} */
this.filesWithoutCase = new Map();
/** @type {Map<string, Watcher<DirectoryWatcherEvents> | boolean>} */
this.directories = new Map();
this.lastWatchEvent = 0;
this.initialScan = true;
this.ignored = options.ignored || (() => false);
this.nestedWatching = false;
/** @type {number | false} */
this.polledWatching =
typeof options.poll === "number"
? options.poll
: options.poll
? 5007
: false;
/** @type {undefined | NodeJS.Timeout} */
this.timeout = undefined;
/** @type {null | InitialScanRemoved} */
this.initialScanRemoved = new Set();
/** @type {undefined | number} */
this.initialScanFinished = undefined;
/** @type {Map<string, Set<Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>>>} */
this.watchers = new Map();
/** @type {Watcher<FileWatcherEvents> | null} */
this.parentWatcher = null;
this.refs = 0;
/** @type {Map<string, boolean>} */
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) {
/** @type {EventSourceWatcher} */
(this.watcher) = /** @type {EventSourceWatcher} */ ({
close: () => {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = undefined;
}
},
});
} else {
if (IS_OSX) {
this.watchInParentDirectory();
}
this.watcher =
/** @type {EventSourceWatcher} */
(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);
}
}
/**
* @template {(watcher: Watcher<EventMap>) => void} T
* @param {string} path path
* @param {T} fn function
*/
forEachWatcher(path, fn) {
const watchers = this.watchers.get(withoutCase(path));
if (watchers !== undefined) {
for (const w of watchers) {
fn(w);
}
}
}
/**
* @param {string} itemPath an item path
* @param {boolean} initial true when initial, otherwise false
* @param {EventType} type even type
*/
setMissing(itemPath, initial, type) {
if (this.initialScan) {
/** @type {InitialScanRemoved} */
(this.initialScanRemoved).add(itemPath);
}
const oldDirectory = this.directories.get(itemPath);
if (oldDirectory) {
if (this.nestedWatching) {
/** @type {Watcher<DirectoryWatcherEvents>} */
(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 = /** @type {number} */ (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),
);
}
}
}
/**
* @param {string} target a target to set file time
* @param {number} mtime mtime
* @param {boolean} initial true when initial, otherwise false
* @param {boolean} ignoreWhenEqual true to ignore when equal, otherwise false
* @param {EventType} type type
*/
setFileTime(target, mtime, initial, ignoreWhenEqual, type) {
const now = Date.now();
if (this.ignored(target)) return;
const old = this.files.get(target);
let safeTime;
let 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(target, {
safeTime,
accuracy,
timestamp: mtime,
});
if (!old) {
const key = withoutCase(target);
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(target, (w) => {
if (!initial || w.checkStartTime(safeTime, initial)) {
w.emit("change", mtime, type);
}
});
} else if (!initial) {
this.forEachWatcher(target, (w) => w.emit("change", mtime, type));
}
this.forEachWatcher(this.path, (w) => {
if (!initial || w.checkStartTime(safeTime, initial)) {
w.emit("change", target, safeTime, type, initial);
}
});
}
/**
* @param {string} directoryPath directory path
* @param {number} birthtime birthtime
* @param {boolean} initial true when initial, otherwise false
* @param {EventType} type even type
*/
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);
}
const safeTime = initial ? Math.min(now, birthtime) + FS_ACCURACY : 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);
}
});
}
}
}
/**
* @param {string} directoryPath directory path
*/
createNestedWatcher(directoryPath) {
const watcher = this.watcherManager.watchDirectory(directoryPath, 1);
watcher.on("change", (target, mtime, type, initial) => {
this.forEachWatcher(this.path, (w) => {
if (!initial || w.checkStartTime(mtime, initial)) {
w.emit("change", target, mtime, type, initial);
}
});
});
this.directories.set(directoryPath, watcher);
}
/**
* @param {boolean} flag true when nested, otherwise false
*/
setNestedWatching(flag) {
if (this.nestedWatching !== Boolean(flag)) {
this.nestedWatching = Boolean(flag);
if (this.nestedWatching) {
for (const directory of this.directories.keys()) {
this.createNestedWatcher(directory);
}
} else {
for (const [directory, watcher] of this.directories) {
/** @type {Watcher<DirectoryWatcherEvents>} */
(watcher).close();
this.directories.set(directory, true);
}
}
}
}
/**
* @param {string} target a target to watch
* @param {number=} startTime start time
* @returns {Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>} watcher
*/
watch(target, startTime) {
const key = withoutCase(target);
let watchers = this.watchers.get(key);
if (watchers === undefined) {
watchers = new Set();
this.watchers.set(key, watchers);
}
this.refs++;
const watcher =
/** @type {Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>} */
(new Watcher(this, target, 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 === target) this.setNestedWatching(false);
}
});
watchers.add(watcher);
let safeTime;
if (target === 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(target);
if (entry) {
fixupEntryAccuracy(entry);
safeTime = entry.safeTime;
} else {
safeTime = 0;
}
}
if (safeTime) {
if (startTime && safeTime >= startTime) {
process.nextTick(() => {
if (this.closed) return;
if (target === this.path) {
/** @type {Watcher<DirectoryWatcherEvents>} */
(watcher).emit(
"change",
target,
safeTime,
"watch (outdated on attach)",
true,
);
} else {
/** @type {Watcher<FileWatcherEvents>} */
(watcher).emit(
"change",
safeTime,
"watch (outdated on attach)",
true,
);
}
});
}
} else if (this.initialScan) {
if (
/** @type {InitialScanRemoved} */
(this.initialScanRemoved).has(target)
) {
process.nextTick(() => {
if (this.closed) return;
watcher.emit("remove");
});
}
} else if (
target !== this.path &&
!this.directories.has(target) &&
watcher.checkStartTime(
/** @type {number} */
(this.initialScanFinished),
false,
)
) {
process.nextTick(() => {
if (this.closed) return;
watcher.emit("initial-missing", "watch (missing on attach)");
});
}
return watcher;
}
/**
* @param {EventType} eventType event type
* @param {string=} filename filename
*/
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 target = path.join(this.path, filename);
if (this.ignored(target)) 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(target, (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
!fs.existsSync(this.path)
) {
this.onDirectoryRemoved("stat failed");
}
}
this.lastWatchEvent = Date.now();
if (!stats) {
this.setMissing(target, false, eventType);
} else if (stats.isDirectory()) {
this.setDirectory(target, +stats.birthtime || 1, false, eventType);
} else if (stats.isFile() || stats.isSymbolicLink()) {
if (stats.mtime) {
ensureFsAccuracy(+stats.mtime);
}
this.setFileTime(
target,
+stats.mtime || +stats.ctime || 1,
false,
false,
eventType,
);
}
});
};
process.nextTick(checkStats);
} else {
this._activeEvents.set(filename, true);
}
}
/**
* @param {unknown=} err error
*/
onWatcherError(err) {
if (this.closed) return;
if (err) {
if (
/** @type {NodeJS.ErrnoException} */
(err).code !== "EPERM" &&
/** @type {NodeJS.ErrnoException} */
(err).code !== "ENOENT"
) {
// eslint-disable-next-line no-console
console.error(`Watchpack Error (watcher): ${err}`);
}
this.onDirectoryRemoved("watch error");
}
}
/**
* @param {Error | NodeJS.ErrnoException=} err error
*/
onStatsError(err) {
if (err) {
// eslint-disable-next-line no-console
console.error(`Watchpack Error (stats): ${err}`);
}
}
/**
* @param {Error | NodeJS.ErrnoException=} err error
*/
onScanError(err) {
if (err) {
// eslint-disable-next-line no-console
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);
}
}
/**
* @param {string} reason a reason
*/
onDirectoryRemoved(reason) {
if (this.watcher) {
this.watcher.close();
this.watcher = null;
}
this.watchInParentDirectory();
const type = /** @type {EventType} */ (`directory-removed (${reason})`);
for (const directory of this.directories.keys()) {
this.setMissing(directory, false, type);
}
for (const file of this.files.keys()) {
this.setMissing(file, false, 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);
/** @type {Watcher<FileWatcherEvents>} */
(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),
);
}
});
/** @type {Watcher<FileWatcherEvents>} */
(this.parentWatcher).on("remove", () => {
this.onDirectoryRemoved("parent directory removed");
});
}
}
/**
* @param {boolean} initial true when initial, otherwise false
*/
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" ||
// TODO https://github.com/libuv/libuv/pull/4566
(err2.code === "EINVAL" && IS_WIN)
) {
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() &&
(!initial || !this.directories.has(itemPath))
) {
this.setDirectory(
itemPath,
+stats.birthtime || 1,
initial,
"scan (dir)",
);
}
itemFinished();
});
}
itemFinished();
});
});
}
/**
* @returns {Record<string, number>} times
*/
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 =
/** @type {Watcher<DirectoryWatcherEvents>} */
(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;
if (!Object.prototype.hasOwnProperty.call(obj, path)) {
obj[path] = null;
}
}
}
}
return obj;
}
/**
* @param {TimeInfoEntries} fileTimestamps file timestamps
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
* @returns {number} safe time
*/
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,
/** @type {Watcher<DirectoryWatcherEvents>} */
(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;
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()) {
/** @type {Watcher<DirectoryWatcherEvents>} */
(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;
module.exports.Watcher = Watcher;

114
node_modules/watchpack/lib/LinkResolver.js generated vendored Normal file
View File

@@ -0,0 +1,114 @@
/*
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() {
/** @type {Map<string, readonly string[]>} */
this.cache = new Map();
}
/**
* @param {string} file path to file or directory
* @returns {readonly 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([...resultSet]);
} else if (parentResolved.length > 1) {
// we have links in the parent but not for the link content location
result = [...parentResolved];
// eslint-disable-next-line prefer-destructuring
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];
// 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 (err) {
if (
/** @type {NodeJS.ErrnoException} */
(err).code &&
!EXPECTED_ERRORS.has(/** @type {NodeJS.ErrnoException} */ (err).code)
) {
throw err;
}
// no link
const result = [...parentResolved];
result[0] = realFile;
Object.freeze(result);
this.cache.set(file, result);
return result;
}
}
}
module.exports = LinkResolver;

80
node_modules/watchpack/lib/getWatcherManager.js generated vendored Normal file
View File

@@ -0,0 +1,80 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const path = require("path");
const DirectoryWatcher = require("./DirectoryWatcher");
/** @typedef {import("./index").EventMap} EventMap */
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherOptions} DirectoryWatcherOptions */
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherEvents} DirectoryWatcherEvents */
/** @typedef {import("./DirectoryWatcher").FileWatcherEvents} FileWatcherEvents */
/**
* @template {EventMap} T
* @typedef {import("./DirectoryWatcher").Watcher<T>} Watcher
*/
class WatcherManager {
/**
* @param {DirectoryWatcherOptions=} options options
*/
constructor(options = {}) {
this.options = options;
/** @type {Map<string, DirectoryWatcher>} */
this.directoryWatchers = new Map();
}
/**
* @param {string} directory a directory
* @returns {DirectoryWatcher} a directory watcher
*/
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;
}
/**
* @param {string} file file
* @param {number=} startTime start time
* @returns {Watcher<FileWatcherEvents> | null} watcher or null if file has no directory
*/
watchFile(file, startTime) {
const directory = path.dirname(file);
if (directory === file) return null;
return this.getDirectoryWatcher(directory).watch(file, startTime);
}
/**
* @param {string} directory directory
* @param {number=} startTime start time
* @returns {Watcher<DirectoryWatcherEvents>} watcher
*/
watchDirectory(directory, startTime) {
return this.getDirectoryWatcher(directory).watch(directory, startTime);
}
}
const watcherManagers = new WeakMap();
/**
* @param {DirectoryWatcherOptions} 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;

554
node_modules/watchpack/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,554 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { EventEmitter } = require("events");
const globToRegExp = require("glob-to-regexp");
const LinkResolver = require("./LinkResolver");
const getWatcherManager = require("./getWatcherManager");
const watchEventSource = require("./watchEventSource");
/** @typedef {import("./getWatcherManager").WatcherManager} WatcherManager */
/** @typedef {import("./DirectoryWatcher")} DirectoryWatcher */
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherEvents} DirectoryWatcherEvents */
/** @typedef {import("./DirectoryWatcher").FileWatcherEvents} FileWatcherEvents */
// eslint-disable-next-line jsdoc/reject-any-type
/** @typedef {Record<string, (...args: any[]) => any>} EventMap */
/**
* @template {EventMap} T
* @typedef {import("./DirectoryWatcher").Watcher<T>} Watcher
*/
/** @typedef {(item: string) => boolean} IgnoredFunction */
/** @typedef {string[] | RegExp | string | IgnoredFunction} Ignored */
/**
* @typedef {object} WatcherOptions
* @property {boolean=} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
* @property {Ignored=} ignored ignore some files from watching (glob pattern or regexp)
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
*/
/** @typedef {WatcherOptions & { aggregateTimeout?: number }} WatchOptions */
/**
* @typedef {object} NormalizedWatchOptions
* @property {boolean} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
* @property {IgnoredFunction} ignored ignore some files from watching (glob pattern or regexp)
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
*/
/** @typedef {`scan (${string})` | "change" | "rename" | `watch ${string}` | `directory-removed ${string}`} EventType */
/** @typedef {{ safeTime: number, timestamp: number, accuracy: number }} Entry */
/** @typedef {{ safeTime: number }} OnlySafeTimeEntry */
// eslint-disable-next-line jsdoc/ts-no-empty-object-type
/** @typedef {{}} ExistenceOnlyTimeEntry */
/** @typedef {Map<string, Entry | OnlySafeTimeEntry | ExistenceOnlyTimeEntry | null>} TimeInfoEntries */
/** @typedef {Set<string>} Changes */
/** @typedef {Set<string>} Removals */
/** @typedef {{ changes: Changes, removals: Removals }} Aggregated */
/** @typedef {{ files?: Iterable<string>, directories?: Iterable<string>, missing?: Iterable<string>, startTime?: number }} WatchMethodOptions */
/** @typedef {Record<string, number>} Times */
/**
* @param {MapIterator<WatchpackFileWatcher> | MapIterator<WatchpackDirectoryWatcher>} watchers watchers
* @param {Set<DirectoryWatcher>} set set
*/
function addWatchersToSet(watchers, set) {
for (const ww of watchers) {
const w = ww.watcher;
if (!set.has(w.directoryWatcher)) {
set.add(w.directoryWatcher);
}
}
}
/**
* @param {string} ignored ignored
* @returns {string | undefined} resolved global to regexp
*/
const stringToRegexp = (ignored) => {
if (ignored.length === 0) {
return;
}
const { source } = globToRegExp(ignored, { globstar: true, extended: true });
return `${source.slice(0, -1)}(?:$|\\/)`;
};
/**
* @param {Ignored=} ignored ignored
* @returns {(item: string) => boolean} ignored to function
*/
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 (item) => regexp.test(item.replace(/\\/g, "/"));
} else if (typeof ignored === "string") {
const stringRegexp = stringToRegexp(ignored);
if (!stringRegexp) {
return () => false;
}
const regexp = new RegExp(stringRegexp);
return (item) => regexp.test(item.replace(/\\/g, "/"));
} else if (ignored instanceof RegExp) {
return (item) => ignored.test(item.replace(/\\/g, "/"));
} else if (typeof ignored === "function") {
return ignored;
} else if (ignored) {
throw new Error(`Invalid option for 'ignored': ${ignored}`);
} else {
return () => false;
}
};
/**
* @param {WatchOptions} options options
* @returns {NormalizedWatchOptions} normalized options
*/
const normalizeOptions = (options) => ({
followSymlinks: Boolean(options.followSymlinks),
ignored: ignoredToFunction(options.ignored),
poll: options.poll,
});
const normalizeCache = new WeakMap();
/**
* @param {WatchOptions} options options
* @returns {NormalizedWatchOptions} normalized options
*/
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 {
/**
* @param {Watchpack} watchpack watchpack
* @param {Watcher<FileWatcherEvents>} watcher watcher
* @param {string | string[]} files files
*/
constructor(watchpack, watcher, files) {
/** @type {string[]} */
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, _initial) => {
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);
}
});
}
/**
* @param {string | string[]} files files
*/
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 {
/**
* @param {Watchpack} watchpack watchpack
* @param {Watcher<DirectoryWatcherEvents>} watcher watcher
* @param {string} directories directories
*/
constructor(watchpack, watcher, directories) {
/** @type {string[]} */
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, _initial) => {
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);
}
});
}
/**
* @param {string | string[]} directories directories
*/
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();
}
}
/**
* @typedef {object} WatchpackEvents
* @property {(file: string, mtime: number, type: EventType) => void} change change event
* @property {(file: string, type: EventType) => void} remove remove event
* @property {(changes: Changes, removals: Removals) => void} aggregated aggregated event
*/
/**
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
*/
class Watchpack extends EventEmitter {
/**
* @param {WatchOptions=} options options
*/
constructor(options = {}) {
super();
if (!options) options = {};
/** @type {WatchOptions} */
this.options = options;
this.aggregateTimeout =
typeof options.aggregateTimeout === "number"
? options.aggregateTimeout
: 200;
/** @type {NormalizedWatchOptions} */
this.watcherOptions = cachedNormalizeOptions(options);
/** @type {WatcherManager} */
this.watcherManager = getWatcherManager(this.watcherOptions);
/** @type {Map<string, WatchpackFileWatcher>} */
this.fileWatchers = new Map();
/** @type {Map<string, WatchpackDirectoryWatcher>} */
this.directoryWatchers = new Map();
/** @type {Set<string>} */
this._missing = new Set();
this.startTime = undefined;
this.paused = false;
/** @type {Changes} */
this.aggregatedChanges = new Set();
/** @type {Removals} */
this.aggregatedRemovals = new Set();
/** @type {undefined | NodeJS.Timeout} */
this.aggregateTimer = undefined;
this._onTimeout = this._onTimeout.bind(this);
}
/**
* @overload
* @param {Iterable<string>} arg1 files
* @param {Iterable<string>} arg2 directories
* @param {number=} arg3 startTime
* @returns {void}
*/
/**
* @overload
* @param {WatchMethodOptions} arg1 watch options
* @returns {void}
*/
/**
* @param {Iterable<string> | WatchMethodOptions} arg1 files
* @param {Iterable<string>=} arg2 directories
* @param {number=} arg3 startTime
* @returns {void}
*/
watch(arg1, arg2, arg3) {
/** @type {Iterable<string> | undefined} */
let files;
/** @type {Iterable<string> | undefined} */
let directories;
/** @type {Iterable<string> | undefined} */
let missing;
/** @type {number | undefined} */
let startTime;
if (!arg2) {
({
files = [],
directories = [],
missing = [],
startTime,
} = /** @type {WatchMethodOptions} */ (arg1));
} else {
files = /** @type {Iterable<string>} */ (arg1);
directories = /** @type {Iterable<string>} */ (arg2);
missing = [];
startTime = /** @type {number} */ (arg3);
}
this.paused = false;
const { fileWatchers, directoryWatchers } = this;
const { ignored } = this.watcherOptions;
/**
* @param {string} path path
* @returns {boolean} true when need to filter, otherwise false
*/
const filter = (path) => !ignored(path);
/**
* @template K, V
* @param {Map<K, V | V[]>} map map
* @param {K} key key
* @param {V} item item
*/
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();
/** @type {Set<string>} */
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);
}
/**
* @returns {Record<string, number>} times
*/
getTimes() {
/** @type {Set<DirectoryWatcher>} */
const directoryWatchers = new Set();
addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
/** @type {Record<string, number>} */
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;
}
/**
* @returns {TimeInfoEntries} time info entries
*/
getTimeInfoEntries() {
/** @type {TimeInfoEntries} */
const map = new Map();
this.collectTimeInfoEntries(map, map);
return map;
}
/**
* @param {TimeInfoEntries} fileTimestamps file timestamps
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
*/
collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
/** @type {Set<DirectoryWatcher>} */
const allWatchers = new Set();
addWatchersToSet(this.fileWatchers.values(), allWatchers);
addWatchersToSet(this.directoryWatchers.values(), allWatchers);
for (const w of allWatchers) {
w.collectTimeInfoEntries(fileTimestamps, directoryTimestamps);
}
}
/**
* @returns {Aggregated} aggregated info
*/
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 };
}
/**
* @param {string} item item
* @param {number} mtime mtime
* @param {string} file file
* @param {EventType} type type
*/
_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);
}
/**
* @param {string} item item
* @param {string} file file
* @param {EventType} type type
*/
_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;

138
node_modules/watchpack/lib/reducePlan.js generated vendored Normal file
View File

@@ -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} target target
* @property {TreeNode<T>} parent parent
* @property {TreeNode<T>[]} children children
* @property {number} entries number of entries
* @property {boolean} active true when active, otherwise false
* @property {T[] | T | undefined} value value
*/
/**
* @template T
* @param {Map<string, T[] | T>} plan plan
* @param {number} limit limit
* @returns {Map<string, Map<T, string>>} the new plan
*/
module.exports = (plan, limit) => {
const treeMap = new Map();
// Convert to tree
for (const [target, value] of plan) {
treeMap.set(target, {
target,
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.target);
if (parentPath !== node.target) {
let parent = treeMap.get(parentPath);
if (parent === undefined) {
parent = {
target: 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;
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;
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.target);
}
} else {
map.set(node.value, node.target);
}
}
if (node.children) {
for (const child of node.children) {
queue.add(child);
}
}
}
newPlan.set(rootNode.target, map);
}
return newPlan;
};

449
node_modules/watchpack/lib/watchEventSource.js generated vendored Normal file
View File

@@ -0,0 +1,449 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { EventEmitter } = require("events");
const fs = require("fs");
const path = require("path");
const reducePlan = require("./reducePlan");
/** @typedef {import("fs").FSWatcher} FSWatcher */
/** @typedef {import("./index").EventType} EventType */
const IS_OSX = require("os").platform() === "darwin";
const IS_WIN = require("os").platform() === "win32";
const SUPPORTS_RECURSIVE_WATCHING = IS_OSX || IS_WIN;
// Use 20 for OSX to make `FSWatcher.close` faster
// https://github.com/nodejs/node/issues/29949
const watcherLimit =
// @ts-expect-error avoid additional checks
+process.env.WATCHPACK_WATCHER_LIMIT || (IS_OSX ? 20 : 10000);
const recursiveWatcherLogging = Boolean(
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();
/**
* @param {string} filePath file path
* @returns {NodeJS.ErrnoException} new error with file path in the message
*/
function createEPERMError(filePath) {
const error =
/** @type {NodeJS.ErrnoException} */
(new Error(`Operation not permitted: ${filePath}`));
error.code = "EPERM";
return error;
}
/**
* @param {FSWatcher} watcher watcher
* @param {string} filePath a file path
* @param {(type: "rename" | "change", filename: string) => void} handleChangeEvent function to handle change
* @returns {(type: "rename" | "change", filename: string) => void} handler of change event
*/
function createHandleChangeEvent(watcher, filePath, handleChangeEvent) {
return (type, filename) => {
// TODO: After Node.js v22, fs.watch(dir) and deleting a dir will trigger the rename change event.
// Here we just ignore it and keep the same behavior as before v22
// https://github.com/libuv/libuv/pull/4376
if (
type === "rename" &&
path.isAbsolute(filename) &&
path.basename(filename) === path.basename(filePath)
) {
if (!IS_OSX) {
// Before v22, windows will throw EPERM error
watcher.emit("error", createEPERMError(filename));
}
// Before v22, macos nothing to do
return;
}
handleChangeEvent(type, filename);
};
}
class DirectWatcher {
/**
* @param {string} filePath file path
*/
constructor(filePath) {
this.filePath = filePath;
this.watchers = new Set();
/** @type {FSWatcher | undefined} */
this.watcher = undefined;
try {
const watcher = fs.watch(filePath);
this.watcher = watcher;
const handleChangeEvent = createHandleChangeEvent(
watcher,
filePath,
(type, filename) => {
for (const w of this.watchers) {
w.emit("change", type, filename);
}
},
);
watcher.on("change", handleChangeEvent);
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++;
}
/**
* @param {Watcher} watcher a watcher
*/
add(watcher) {
underlyingWatcher.set(watcher, this);
this.watchers.add(watcher);
}
/**
* @param {Watcher} watcher a 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;
}
}
/** @typedef {Set<Watcher>} WatcherSet */
class RecursiveWatcher {
/**
* @param {string} rootPath a root path
*/
constructor(rootPath) {
this.rootPath = rootPath;
/** @type {Map<Watcher, string>} */
this.mapWatcherToPath = new Map();
/** @type {Map<string, WatcherSet>} */
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 {EventType} */ (type));
}
} else {
const dir = path.dirname(/** @type {string} */ (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 {EventType} */ (type),
path.basename(/** @type {string} */ (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`,
);
}
}
/**
* @param {string} filePath a file path
* @param {Watcher} watcher a watcher
*/
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);
}
}
/**
* @param {Watcher} watcher a watcher
*/
remove(watcher) {
const subpath = this.mapWatcherToPath.get(watcher);
if (!subpath) return;
this.mapWatcherToPath.delete(watcher);
const set = /** @type {WatcherSet} */ (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;
}
}
/**
* @typedef {object} WatcherEvents
* @property {(eventType: EventType, filename?: string) => void} change change event
* @property {(err: unknown) => void} error error event
*/
/**
* @extends {EventEmitter<{ [K in keyof WatcherEvents]: Parameters<WatcherEvents[K]> }>}
*/
class Watcher extends EventEmitter {
constructor() {
super();
}
close() {
if (pendingWatchers.has(this)) {
pendingWatchers.delete(this);
return;
}
const watcher = underlyingWatcher.get(this);
/** @type {RecursiveWatcher | DirectWatcher} */
(watcher).remove(this);
underlyingWatcher.delete(this);
}
}
/**
* @param {string} filePath a file path
* @returns {DirectWatcher} a directory watcher
*/
const createDirectWatcher = (filePath) => {
const existing = directWatchers.get(filePath);
if (existing !== undefined) return existing;
const w = new DirectWatcher(filePath);
directWatchers.set(filePath, w);
return w;
};
/**
* @param {string} rootPath a root path
* @returns {RecursiveWatcher} a recursive watcher
*/
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();
/**
* @param {Watcher} watcher a watcher
* @param {string} filePath a file path
*/
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);
}
}
}
}
}
};
module.exports.Watcher = Watcher;
/**
* @param {() => void} fn a function
*/
module.exports.batch = (fn) => {
isBatch = true;
try {
fn();
} finally {
isBatch = false;
execute();
}
};
module.exports.createHandleChangeEvent = createHandleChangeEvent;
module.exports.getNumberOfWatchers = () => watcherCount;
/**
* @param {string} filePath a file path
* @returns {Watcher} watcher
*/
module.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;
};
module.exports.watcherLimit = watcherLimit;

8
node_modules/watchpack/lib/watchpack.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Alexander Akait @akexander-akait
*/
"use strict";
// TODO remove this file in the next major release
module.exports = require("./index");

74
node_modules/watchpack/package.json generated vendored Normal file
View File

@@ -0,0 +1,74 @@
{
"name": "watchpack",
"version": "2.5.1",
"description": "",
"homepage": "https://github.com/webpack/watchpack",
"bugs": {
"url": "https://github.com/webpack/watchpack/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/webpack/watchpack.git"
},
"license": "MIT",
"author": "Tobias Koppers @sokra",
"main": "lib/index.js",
"types": "types/index.js",
"directories": {
"test": "test"
},
"files": [
"lib/",
"types/"
],
"scripts": {
"lint": "npm run lint:code && npm run lint:types && npm run lint:types-test && npm run lint:declarations && npm run fmt:check",
"lint:code": "eslint --cache .",
"lint:types": "tsc",
"lint:types-test": "tsc -p tsconfig.types.test.json",
"lint:declarations": "npm run fix:declarations && git diff --exit-code ./types",
"fix": "npm run fix:code && npm run fix:declarations",
"fix:code": "npm run lint:code -- --fix",
"fix:declarations": "tsc --noEmit false --declaration --emitDeclarationOnly --outDir types && npm run fmt -- ./types",
"fmt": "npm run fmt:base -- --log-level warn --write",
"fmt:check": "npm run fmt:base -- --check",
"fmt:base": "prettier --cache --ignore-unknown .",
"pretest": "npm run lint",
"test": "npm run test:coverage",
"test:base": "jest --runInBand",
"test:only": "npm run test:base",
"test:watch": "npm run test:base -- --watch",
"test:coverage": "npm run test:base -- --collectCoverageFrom=\"lib/**/*.js\" --coverage"
},
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"@eslint/markdown": "^7.5.1",
"@stylistic/eslint-plugin": "^5.6.1",
"@types/glob-to-regexp": "^0.4.4",
"@types/graceful-fs": "^4.1.9",
"@types/jest": "^27.5.1",
"@types/node": "^24.10.4",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-config-webpack": "^4.7.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.5.0",
"eslint-plugin-jsdoc": "^61.5.0",
"eslint-plugin-n": "^17.23.1",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-unicorn": "^62.0.0",
"globals": "^16.5.0",
"jest": "^27.5.1",
"prettier": "^3.7.4",
"rimraf": "^2.6.2",
"typescript": "^5.9.3",
"write-file-atomic": "^3.0.1"
},
"engines": {
"node": ">=10.13.0"
}
}

333
node_modules/watchpack/types/DirectoryWatcher.d.ts generated vendored Normal file
View File

@@ -0,0 +1,333 @@
export = DirectoryWatcher;
/** @typedef {Set<string>} InitialScanRemoved */
/**
* @typedef {object} WatchpackEvents
* @property {(target: string, mtime: string, type: EventType, initial: boolean) => void} change change event
* @property {() => void} closed closed event
*/
/**
* @typedef {object} DirectoryWatcherOptions
* @property {boolean=} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
* @property {IgnoredFunction=} ignored ignore some files from watching (glob pattern or regexp)
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
*/
/**
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
*/
declare class DirectoryWatcher extends EventEmitter<{
/**
* change event
*/
change: [
target: string,
mtime: string,
type: import("./index").EventType,
initial: boolean,
];
/**
* closed event
*/
closed: [];
}> {
/**
* @param {WatcherManager} watcherManager a watcher manager
* @param {string} directoryPath directory path
* @param {DirectoryWatcherOptions=} options options
*/
constructor(
watcherManager: WatcherManager,
directoryPath: string,
options?: DirectoryWatcherOptions | undefined,
);
watcherManager: import("./getWatcherManager").WatcherManager;
options: DirectoryWatcherOptions;
path: string;
/** @type {Map<string, Entry>} */
files: Map<string, Entry>;
/** @type {Map<string, number>} */
filesWithoutCase: Map<string, number>;
/** @type {Map<string, Watcher<DirectoryWatcherEvents> | boolean>} */
directories: Map<string, Watcher<DirectoryWatcherEvents> | boolean>;
lastWatchEvent: number;
initialScan: boolean;
ignored: import("./index").IgnoredFunction;
nestedWatching: boolean;
/** @type {number | false} */
polledWatching: number | false;
/** @type {undefined | NodeJS.Timeout} */
timeout: undefined | NodeJS.Timeout;
/** @type {null | InitialScanRemoved} */
initialScanRemoved: null | InitialScanRemoved;
/** @type {undefined | number} */
initialScanFinished: undefined | number;
/** @type {Map<string, Set<Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>>>} */
watchers: Map<
string,
Set<Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>>
>;
/** @type {Watcher<FileWatcherEvents> | null} */
parentWatcher: Watcher<FileWatcherEvents> | null;
refs: number;
/** @type {Map<string, boolean>} */
_activeEvents: Map<string, boolean>;
closed: boolean;
scanning: boolean;
scanAgain: boolean;
scanAgainInitial: boolean;
createWatcher(): void;
watcher: watchEventSource.Watcher | null | undefined;
/**
* @template {(watcher: Watcher<EventMap>) => void} T
* @param {string} path path
* @param {T} fn function
*/
forEachWatcher<T extends (watcher: Watcher<EventMap>) => void>(
path: string,
fn: T,
): void;
/**
* @param {string} itemPath an item path
* @param {boolean} initial true when initial, otherwise false
* @param {EventType} type even type
*/
setMissing(itemPath: string, initial: boolean, type: EventType): void;
/**
* @param {string} target a target to set file time
* @param {number} mtime mtime
* @param {boolean} initial true when initial, otherwise false
* @param {boolean} ignoreWhenEqual true to ignore when equal, otherwise false
* @param {EventType} type type
*/
setFileTime(
target: string,
mtime: number,
initial: boolean,
ignoreWhenEqual: boolean,
type: EventType,
): void;
/**
* @param {string} directoryPath directory path
* @param {number} birthtime birthtime
* @param {boolean} initial true when initial, otherwise false
* @param {EventType} type even type
*/
setDirectory(
directoryPath: string,
birthtime: number,
initial: boolean,
type: EventType,
): void;
/**
* @param {string} directoryPath directory path
*/
createNestedWatcher(directoryPath: string): void;
/**
* @param {boolean} flag true when nested, otherwise false
*/
setNestedWatching(flag: boolean): void;
/**
* @param {string} target a target to watch
* @param {number=} startTime start time
* @returns {Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>} watcher
*/
watch(
target: string,
startTime?: number | undefined,
): Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>;
/**
* @param {EventType} eventType event type
* @param {string=} filename filename
*/
onWatchEvent(eventType: EventType, filename?: string | undefined): void;
/**
* @param {unknown=} err error
*/
onWatcherError(err?: unknown | undefined): void;
/**
* @param {Error | NodeJS.ErrnoException=} err error
*/
onStatsError(err?: (Error | NodeJS.ErrnoException) | undefined): void;
/**
* @param {Error | NodeJS.ErrnoException=} err error
*/
onScanError(err?: (Error | NodeJS.ErrnoException) | undefined): void;
onScanFinished(): void;
/**
* @param {string} reason a reason
*/
onDirectoryRemoved(reason: string): void;
watchInParentDirectory(): void;
/**
* @param {boolean} initial true when initial, otherwise false
*/
doScan(initial: boolean): void;
/**
* @returns {Record<string, number>} times
*/
getTimes(): Record<string, number>;
/**
* @param {TimeInfoEntries} fileTimestamps file timestamps
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
* @returns {number} safe time
*/
collectTimeInfoEntries(
fileTimestamps: TimeInfoEntries,
directoryTimestamps: TimeInfoEntries,
): number;
close(): void;
}
declare namespace DirectoryWatcher {
export {
EXISTANCE_ONLY_TIME_ENTRY,
Watcher,
IgnoredFunction,
EventType,
TimeInfoEntries,
Entry,
ExistenceOnlyTimeEntry,
OnlySafeTimeEntry,
EventMap,
WatcherManager,
EventSourceWatcher,
FileWatcherEvents,
DirectoryWatcherEvents,
InitialScanRemoved,
WatchpackEvents,
DirectoryWatcherOptions,
};
}
import { EventEmitter } from "events";
/**
* @typedef {object} FileWatcherEvents
* @property {(type: EventType) => void} initial-missing initial missing event
* @property {(mtime: number, type: EventType, initial: boolean) => void} change change event
* @property {(type: EventType) => void} remove remove event
* @property {() => void} closed closed event
*/
/**
* @typedef {object} DirectoryWatcherEvents
* @property {(type: EventType) => void} initial-missing initial missing event
* @property {((file: string, mtime: number, type: EventType, initial: boolean) => void)} change change event
* @property {(type: EventType) => void} remove remove event
* @property {() => void} closed closed event
*/
/**
* @template {EventMap} T
* @extends {EventEmitter<{ [K in keyof T]: Parameters<T[K]> }>}
*/
declare class Watcher<T extends EventMap> extends EventEmitter<{
[K in keyof T]: Parameters<T[K]>;
}> {
/**
* @param {DirectoryWatcher} directoryWatcher a directory watcher
* @param {string} target a target to watch
* @param {number=} startTime start time
*/
constructor(
directoryWatcher: DirectoryWatcher,
target: string,
startTime?: number | undefined,
);
directoryWatcher: DirectoryWatcher;
path: string;
startTime: number | undefined;
/**
* @param {number} mtime mtime
* @param {boolean} initial true when initial, otherwise false
* @returns {boolean} true of start time less than mtile, otherwise false
*/
checkStartTime(mtime: number, initial: boolean): boolean;
close(): void;
}
import watchEventSource = require("./watchEventSource");
/** @typedef {import("./index").IgnoredFunction} IgnoredFunction */
/** @typedef {import("./index").EventType} EventType */
/** @typedef {import("./index").TimeInfoEntries} TimeInfoEntries */
/** @typedef {import("./index").Entry} Entry */
/** @typedef {import("./index").ExistenceOnlyTimeEntry} ExistenceOnlyTimeEntry */
/** @typedef {import("./index").OnlySafeTimeEntry} OnlySafeTimeEntry */
/** @typedef {import("./index").EventMap} EventMap */
/** @typedef {import("./getWatcherManager").WatcherManager} WatcherManager */
/** @typedef {import("./watchEventSource").Watcher} EventSourceWatcher */
/** @type {ExistenceOnlyTimeEntry} */
declare const EXISTANCE_ONLY_TIME_ENTRY: ExistenceOnlyTimeEntry;
type IgnoredFunction = import("./index").IgnoredFunction;
type EventType = import("./index").EventType;
type TimeInfoEntries = import("./index").TimeInfoEntries;
type Entry = import("./index").Entry;
type ExistenceOnlyTimeEntry = import("./index").ExistenceOnlyTimeEntry;
type OnlySafeTimeEntry = import("./index").OnlySafeTimeEntry;
type EventMap = import("./index").EventMap;
type WatcherManager = import("./getWatcherManager").WatcherManager;
type EventSourceWatcher = import("./watchEventSource").Watcher;
type FileWatcherEvents = {
/**
* initial missing event
*/
"initial-missing": (type: EventType) => void;
/**
* change event
*/
change: (mtime: number, type: EventType, initial: boolean) => void;
/**
* remove event
*/
remove: (type: EventType) => void;
/**
* closed event
*/
closed: () => void;
};
type DirectoryWatcherEvents = {
/**
* initial missing event
*/
"initial-missing": (type: EventType) => void;
/**
* change event
*/
change: (
file: string,
mtime: number,
type: EventType,
initial: boolean,
) => void;
/**
* remove event
*/
remove: (type: EventType) => void;
/**
* closed event
*/
closed: () => void;
};
type InitialScanRemoved = Set<string>;
type WatchpackEvents = {
/**
* change event
*/
change: (
target: string,
mtime: string,
type: EventType,
initial: boolean,
) => void;
/**
* closed event
*/
closed: () => void;
};
type DirectoryWatcherOptions = {
/**
* true when need to resolve symlinks and watch symlink and real file, otherwise false
*/
followSymlinks?: boolean | undefined;
/**
* ignore some files from watching (glob pattern or regexp)
*/
ignored?: IgnoredFunction | undefined;
/**
* true when need to enable polling mode for watching, otherwise false
*/
poll?: (number | boolean) | undefined;
};

10
node_modules/watchpack/types/LinkResolver.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
export = LinkResolver;
declare class LinkResolver {
/** @type {Map<string, readonly string[]>} */
cache: Map<string, readonly string[]>;
/**
* @param {string} file path to file or directory
* @returns {readonly string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file)
*/
resolve(file: string): readonly string[];
}

62
node_modules/watchpack/types/getWatcherManager.d.ts generated vendored Normal file
View File

@@ -0,0 +1,62 @@
declare namespace _exports {
export {
EventMap,
DirectoryWatcherOptions,
DirectoryWatcherEvents,
FileWatcherEvents,
Watcher,
};
}
declare function _exports(options: DirectoryWatcherOptions): WatcherManager;
declare namespace _exports {
export { WatcherManager };
}
export = _exports;
type EventMap = import("./index").EventMap;
type DirectoryWatcherOptions =
import("./DirectoryWatcher").DirectoryWatcherOptions;
type DirectoryWatcherEvents =
import("./DirectoryWatcher").DirectoryWatcherEvents;
type FileWatcherEvents = import("./DirectoryWatcher").FileWatcherEvents;
type Watcher<T extends EventMap> = import("./DirectoryWatcher").Watcher<T>;
/** @typedef {import("./index").EventMap} EventMap */
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherOptions} DirectoryWatcherOptions */
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherEvents} DirectoryWatcherEvents */
/** @typedef {import("./DirectoryWatcher").FileWatcherEvents} FileWatcherEvents */
/**
* @template {EventMap} T
* @typedef {import("./DirectoryWatcher").Watcher<T>} Watcher
*/
declare class WatcherManager {
/**
* @param {DirectoryWatcherOptions=} options options
*/
constructor(options?: DirectoryWatcherOptions | undefined);
options: DirectoryWatcher.DirectoryWatcherOptions;
/** @type {Map<string, DirectoryWatcher>} */
directoryWatchers: Map<string, DirectoryWatcher>;
/**
* @param {string} directory a directory
* @returns {DirectoryWatcher} a directory watcher
*/
getDirectoryWatcher(directory: string): DirectoryWatcher;
/**
* @param {string} file file
* @param {number=} startTime start time
* @returns {Watcher<FileWatcherEvents> | null} watcher or null if file has no directory
*/
watchFile(
file: string,
startTime?: number | undefined,
): Watcher<FileWatcherEvents> | null;
/**
* @param {string} directory directory
* @param {number=} startTime start time
* @returns {Watcher<DirectoryWatcherEvents>} watcher
*/
watchDirectory(
directory: string,
startTime?: number | undefined,
): Watcher<DirectoryWatcherEvents>;
}
import DirectoryWatcher = require("./DirectoryWatcher");

261
node_modules/watchpack/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,261 @@
export = Watchpack;
/**
* @typedef {object} WatchpackEvents
* @property {(file: string, mtime: number, type: EventType) => void} change change event
* @property {(file: string, type: EventType) => void} remove remove event
* @property {(changes: Changes, removals: Removals) => void} aggregated aggregated event
*/
/**
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
*/
declare class Watchpack extends EventEmitter<{
/**
* change event
*/
change: [file: string, mtime: number, type: EventType];
/**
* remove event
*/
remove: [file: string, type: EventType];
/**
* aggregated event
*/
aggregated: [changes: Changes, removals: Removals];
}> {
/**
* @param {WatchOptions=} options options
*/
constructor(options?: WatchOptions | undefined);
/** @type {WatchOptions} */
options: WatchOptions;
aggregateTimeout: number;
/** @type {NormalizedWatchOptions} */
watcherOptions: NormalizedWatchOptions;
/** @type {WatcherManager} */
watcherManager: WatcherManager;
/** @type {Map<string, WatchpackFileWatcher>} */
fileWatchers: Map<string, WatchpackFileWatcher>;
/** @type {Map<string, WatchpackDirectoryWatcher>} */
directoryWatchers: Map<string, WatchpackDirectoryWatcher>;
/** @type {Set<string>} */
_missing: Set<string>;
startTime: number | undefined;
paused: boolean;
/** @type {Changes} */
aggregatedChanges: Changes;
/** @type {Removals} */
aggregatedRemovals: Removals;
/** @type {undefined | NodeJS.Timeout} */
aggregateTimer: undefined | NodeJS.Timeout;
_onTimeout(): void;
/**
* @overload
* @param {Iterable<string>} arg1 files
* @param {Iterable<string>} arg2 directories
* @param {number=} arg3 startTime
* @returns {void}
*/
watch(
arg1: Iterable<string>,
arg2: Iterable<string>,
arg3?: number | undefined,
): void;
/**
* @overload
* @param {WatchMethodOptions} arg1 watch options
* @returns {void}
*/
watch(arg1: WatchMethodOptions): void;
close(): void;
pause(): void;
/**
* @returns {Record<string, number>} times
*/
getTimes(): Record<string, number>;
/**
* @returns {TimeInfoEntries} time info entries
*/
getTimeInfoEntries(): TimeInfoEntries;
/**
* @param {TimeInfoEntries} fileTimestamps file timestamps
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
*/
collectTimeInfoEntries(
fileTimestamps: TimeInfoEntries,
directoryTimestamps: TimeInfoEntries,
): void;
/**
* @returns {Aggregated} aggregated info
*/
getAggregated(): Aggregated;
/**
* @param {string} item item
* @param {number} mtime mtime
* @param {string} file file
* @param {EventType} type type
*/
_onChange(item: string, mtime: number, file: string, type: EventType): void;
/**
* @param {string} item item
* @param {string} file file
* @param {EventType} type type
*/
_onRemove(item: string, file: string, type: EventType): void;
}
declare namespace Watchpack {
export {
WatcherManager,
DirectoryWatcher,
DirectoryWatcherEvents,
FileWatcherEvents,
EventMap,
Watcher,
IgnoredFunction,
Ignored,
WatcherOptions,
WatchOptions,
NormalizedWatchOptions,
EventType,
Entry,
OnlySafeTimeEntry,
ExistenceOnlyTimeEntry,
TimeInfoEntries,
Changes,
Removals,
Aggregated,
WatchMethodOptions,
Times,
WatchpackEvents,
};
}
import { EventEmitter } from "events";
declare class WatchpackFileWatcher {
/**
* @param {Watchpack} watchpack watchpack
* @param {Watcher<FileWatcherEvents>} watcher watcher
* @param {string | string[]} files files
*/
constructor(
watchpack: Watchpack,
watcher: Watcher<FileWatcherEvents>,
files: string | string[],
);
/** @type {string[]} */
files: string[];
watcher: import("./DirectoryWatcher").Watcher<
import("./DirectoryWatcher").FileWatcherEvents
>;
/**
* @param {string | string[]} files files
*/
update(files: string | string[]): void;
close(): void;
}
declare class WatchpackDirectoryWatcher {
/**
* @param {Watchpack} watchpack watchpack
* @param {Watcher<DirectoryWatcherEvents>} watcher watcher
* @param {string} directories directories
*/
constructor(
watchpack: Watchpack,
watcher: Watcher<DirectoryWatcherEvents>,
directories: string,
);
/** @type {string[]} */
directories: string[];
watcher: import("./DirectoryWatcher").Watcher<
import("./DirectoryWatcher").DirectoryWatcherEvents
>;
/**
* @param {string | string[]} directories directories
*/
update(directories: string | string[]): void;
close(): void;
}
type WatcherManager = import("./getWatcherManager").WatcherManager;
type DirectoryWatcher = import("./DirectoryWatcher");
type DirectoryWatcherEvents =
import("./DirectoryWatcher").DirectoryWatcherEvents;
type FileWatcherEvents = import("./DirectoryWatcher").FileWatcherEvents;
type EventMap = Record<string, (...args: any[]) => any>;
type Watcher<T extends EventMap> = import("./DirectoryWatcher").Watcher<T>;
type IgnoredFunction = (item: string) => boolean;
type Ignored = string[] | RegExp | string | IgnoredFunction;
type WatcherOptions = {
/**
* true when need to resolve symlinks and watch symlink and real file, otherwise false
*/
followSymlinks?: boolean | undefined;
/**
* ignore some files from watching (glob pattern or regexp)
*/
ignored?: Ignored | undefined;
/**
* true when need to enable polling mode for watching, otherwise false
*/
poll?: (number | boolean) | undefined;
};
type WatchOptions = WatcherOptions & {
aggregateTimeout?: number;
};
type NormalizedWatchOptions = {
/**
* true when need to resolve symlinks and watch symlink and real file, otherwise false
*/
followSymlinks: boolean;
/**
* ignore some files from watching (glob pattern or regexp)
*/
ignored: IgnoredFunction;
/**
* true when need to enable polling mode for watching, otherwise false
*/
poll?: (number | boolean) | undefined;
};
type EventType =
| `scan (${string})`
| "change"
| "rename"
| `watch ${string}`
| `directory-removed ${string}`;
type Entry = {
safeTime: number;
timestamp: number;
accuracy: number;
};
type OnlySafeTimeEntry = {
safeTime: number;
};
type ExistenceOnlyTimeEntry = {};
type TimeInfoEntries = Map<
string,
Entry | OnlySafeTimeEntry | ExistenceOnlyTimeEntry | null
>;
type Changes = Set<string>;
type Removals = Set<string>;
type Aggregated = {
changes: Changes;
removals: Removals;
};
type WatchMethodOptions = {
files?: Iterable<string>;
directories?: Iterable<string>;
missing?: Iterable<string>;
startTime?: number;
};
type Times = Record<string, number>;
type WatchpackEvents = {
/**
* change event
*/
change: (file: string, mtime: number, type: EventType) => void;
/**
* remove event
*/
remove: (file: string, type: EventType) => void;
/**
* aggregated event
*/
aggregated: (changes: Changes, removals: Removals) => void;
};

34
node_modules/watchpack/types/reducePlan.d.ts generated vendored Normal file
View File

@@ -0,0 +1,34 @@
declare namespace _exports {
export { TreeNode };
}
declare function _exports<T>(
plan: Map<string, T[] | T>,
limit: number,
): Map<string, Map<T, string>>;
export = _exports;
type TreeNode<T> = {
/**
* target
*/
target: string;
/**
* parent
*/
parent: TreeNode<T>;
/**
* children
*/
children: TreeNode<T>[];
/**
* number of entries
*/
entries: number;
/**
* true when active, otherwise false
*/
active: boolean;
/**
* value
*/
value: T[] | T | undefined;
};

53
node_modules/watchpack/types/watchEventSource.d.ts generated vendored Normal file
View File

@@ -0,0 +1,53 @@
export function batch(fn: () => void): void;
export function getNumberOfWatchers(): number;
export function watch(filePath: string): Watcher;
export type FSWatcher = import("fs").FSWatcher;
export type EventType = import("./index").EventType;
export type WatcherSet = Set<Watcher>;
export type WatcherEvents = {
/**
* change event
*/
change: (eventType: EventType, filename?: string) => void;
/**
* error event
*/
error: (err: unknown) => void;
};
/**
* @typedef {object} WatcherEvents
* @property {(eventType: EventType, filename?: string) => void} change change event
* @property {(err: unknown) => void} error error event
*/
/**
* @extends {EventEmitter<{ [K in keyof WatcherEvents]: Parameters<WatcherEvents[K]> }>}
*/
export class Watcher extends EventEmitter<{
/**
* change event
*/
change: [
eventType: import("./index").EventType,
filename?: string | undefined,
];
/**
* error event
*/
error: [err: unknown];
}> {
constructor();
close(): void;
}
/**
* @param {FSWatcher} watcher watcher
* @param {string} filePath a file path
* @param {(type: "rename" | "change", filename: string) => void} handleChangeEvent function to handle change
* @returns {(type: "rename" | "change", filename: string) => void} handler of change event
*/
export function createHandleChangeEvent(
watcher: FSWatcher,
filePath: string,
handleChangeEvent: (type: "rename" | "change", filename: string) => void,
): (type: "rename" | "change", filename: string) => void;
export const watcherLimit: number;
import { EventEmitter } from "events";

2
node_modules/watchpack/types/watchpack.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
declare const _exports: typeof import("./index");
export = _exports;