run npm install to generate a package lock
This commit is contained in:
13
node_modules/watcher/dist/constants.d.ts
generated
vendored
Normal file
13
node_modules/watcher/dist/constants.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/// <reference types="node" />
|
||||
declare const DEBOUNCE = 300;
|
||||
declare const DEPTH = 20;
|
||||
declare const LIMIT = 10000000;
|
||||
declare const PLATFORM: NodeJS.Platform;
|
||||
declare const IS_LINUX: boolean;
|
||||
declare const IS_MAC: boolean;
|
||||
declare const IS_WINDOWS: boolean;
|
||||
declare const HAS_NATIVE_RECURSION: boolean;
|
||||
declare const POLLING_INTERVAL = 3000;
|
||||
declare const POLLING_TIMEOUT = 20000;
|
||||
declare const RENAME_TIMEOUT = 1250;
|
||||
export { DEBOUNCE, DEPTH, LIMIT, HAS_NATIVE_RECURSION, IS_LINUX, IS_MAC, IS_WINDOWS, PLATFORM, POLLING_INTERVAL, POLLING_TIMEOUT, RENAME_TIMEOUT };
|
||||
16
node_modules/watcher/dist/constants.js
generated
vendored
Normal file
16
node_modules/watcher/dist/constants.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/* IMPORT */
|
||||
import os from 'node:os';
|
||||
/* MAIN */
|
||||
const DEBOUNCE = 300;
|
||||
const DEPTH = 20;
|
||||
const LIMIT = 10000000;
|
||||
const PLATFORM = os.platform();
|
||||
const IS_LINUX = (PLATFORM === 'linux');
|
||||
const IS_MAC = (PLATFORM === 'darwin');
|
||||
const IS_WINDOWS = (PLATFORM === 'win32');
|
||||
const HAS_NATIVE_RECURSION = IS_MAC || IS_WINDOWS;
|
||||
const POLLING_INTERVAL = 3000;
|
||||
const POLLING_TIMEOUT = 20000;
|
||||
const RENAME_TIMEOUT = 1250;
|
||||
/* EXPORT */
|
||||
export { DEBOUNCE, DEPTH, LIMIT, HAS_NATIVE_RECURSION, IS_LINUX, IS_MAC, IS_WINDOWS, PLATFORM, POLLING_INTERVAL, POLLING_TIMEOUT, RENAME_TIMEOUT };
|
||||
28
node_modules/watcher/dist/enums.d.ts
generated
vendored
Normal file
28
node_modules/watcher/dist/enums.d.ts
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
declare const enum FileType {
|
||||
DIR = 1,
|
||||
FILE = 2
|
||||
}
|
||||
declare const enum FSTargetEvent {
|
||||
CHANGE = "change",
|
||||
RENAME = "rename"
|
||||
}
|
||||
declare const enum FSWatcherEvent {
|
||||
CHANGE = "change",
|
||||
ERROR = "error"
|
||||
}
|
||||
declare const enum TargetEvent {
|
||||
ADD = "add",
|
||||
ADD_DIR = "addDir",
|
||||
CHANGE = "change",
|
||||
RENAME = "rename",
|
||||
RENAME_DIR = "renameDir",
|
||||
UNLINK = "unlink",
|
||||
UNLINK_DIR = "unlinkDir"
|
||||
}
|
||||
declare const enum WatcherEvent {
|
||||
ALL = "all",
|
||||
CLOSE = "close",
|
||||
ERROR = "error",
|
||||
READY = "ready"
|
||||
}
|
||||
export { FileType, FSTargetEvent, FSWatcherEvent, TargetEvent, WatcherEvent };
|
||||
35
node_modules/watcher/dist/enums.js
generated
vendored
Normal file
35
node_modules/watcher/dist/enums.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* MAIN */
|
||||
var FileType;
|
||||
(function (FileType) {
|
||||
FileType[FileType["DIR"] = 1] = "DIR";
|
||||
FileType[FileType["FILE"] = 2] = "FILE";
|
||||
})(FileType || (FileType = {}));
|
||||
var FSTargetEvent;
|
||||
(function (FSTargetEvent) {
|
||||
FSTargetEvent["CHANGE"] = "change";
|
||||
FSTargetEvent["RENAME"] = "rename";
|
||||
})(FSTargetEvent || (FSTargetEvent = {}));
|
||||
var FSWatcherEvent;
|
||||
(function (FSWatcherEvent) {
|
||||
FSWatcherEvent["CHANGE"] = "change";
|
||||
FSWatcherEvent["ERROR"] = "error";
|
||||
})(FSWatcherEvent || (FSWatcherEvent = {}));
|
||||
var TargetEvent;
|
||||
(function (TargetEvent) {
|
||||
TargetEvent["ADD"] = "add";
|
||||
TargetEvent["ADD_DIR"] = "addDir";
|
||||
TargetEvent["CHANGE"] = "change";
|
||||
TargetEvent["RENAME"] = "rename";
|
||||
TargetEvent["RENAME_DIR"] = "renameDir";
|
||||
TargetEvent["UNLINK"] = "unlink";
|
||||
TargetEvent["UNLINK_DIR"] = "unlinkDir";
|
||||
})(TargetEvent || (TargetEvent = {}));
|
||||
var WatcherEvent;
|
||||
(function (WatcherEvent) {
|
||||
WatcherEvent["ALL"] = "all";
|
||||
WatcherEvent["CLOSE"] = "close";
|
||||
WatcherEvent["ERROR"] = "error";
|
||||
WatcherEvent["READY"] = "ready";
|
||||
})(WatcherEvent || (WatcherEvent = {}));
|
||||
/* EXPORT */
|
||||
export { FileType, FSTargetEvent, FSWatcherEvent, TargetEvent, WatcherEvent };
|
||||
10
node_modules/watcher/dist/lazy_map_set.d.ts
generated
vendored
Normal file
10
node_modules/watcher/dist/lazy_map_set.d.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
declare class LazyMapSet<K, V> {
|
||||
private map;
|
||||
clear(): void;
|
||||
delete(key: K, value?: V): boolean;
|
||||
find(key: K, iterator: (value: V) => boolean): V | undefined;
|
||||
get(key: K): Set<V> | V | undefined;
|
||||
has(key: K, value?: V): boolean;
|
||||
set(key: K, value: V): this;
|
||||
}
|
||||
export default LazyMapSet;
|
||||
81
node_modules/watcher/dist/lazy_map_set.js
generated
vendored
Normal file
81
node_modules/watcher/dist/lazy_map_set.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/* IMPORT */
|
||||
import Utils from './utils.js';
|
||||
/* MAIN */
|
||||
//TODO: Maybe publish this as a standalone module
|
||||
class LazyMapSet {
|
||||
constructor() {
|
||||
/* VARIABLES */
|
||||
this.map = new Map();
|
||||
}
|
||||
/* API */
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
delete(key, value) {
|
||||
if (Utils.lang.isUndefined(value)) {
|
||||
return this.map.delete(key);
|
||||
}
|
||||
else if (this.map.has(key)) {
|
||||
const values = this.map.get(key);
|
||||
if (Utils.lang.isSet(values)) {
|
||||
const deleted = values.delete(value);
|
||||
if (!values.size) {
|
||||
this.map.delete(key);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
else if (values === value) {
|
||||
this.map.delete(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
find(key, iterator) {
|
||||
if (this.map.has(key)) {
|
||||
const values = this.map.get(key);
|
||||
if (Utils.lang.isSet(values)) {
|
||||
return Array.from(values).find(iterator);
|
||||
}
|
||||
else if (iterator(values)) { //TSC
|
||||
return values;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
get(key) {
|
||||
return this.map.get(key);
|
||||
}
|
||||
has(key, value) {
|
||||
if (Utils.lang.isUndefined(value)) {
|
||||
return this.map.has(key);
|
||||
}
|
||||
else if (this.map.has(key)) {
|
||||
const values = this.map.get(key);
|
||||
if (Utils.lang.isSet(values)) {
|
||||
return values.has(value);
|
||||
}
|
||||
else {
|
||||
return (values === value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
set(key, value) {
|
||||
if (this.map.has(key)) {
|
||||
const values = this.map.get(key);
|
||||
if (Utils.lang.isSet(values)) {
|
||||
values.add(value);
|
||||
}
|
||||
else if (values !== value) {
|
||||
this.map.set(key, new Set([values, value])); //TSC
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.map.set(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/* EXPORT */
|
||||
export default LazyMapSet;
|
||||
64
node_modules/watcher/dist/types.d.ts
generated
vendored
Normal file
64
node_modules/watcher/dist/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
/// <reference types="node" />
|
||||
import type { FSWatcher, BigIntStats } from 'node:fs';
|
||||
import type { ResultDirectories } from 'tiny-readdir';
|
||||
import type { FSTargetEvent, TargetEvent } from './enums';
|
||||
import type WatcherStats from './watcher_stats';
|
||||
type Callback = () => void;
|
||||
type Disposer = () => void;
|
||||
type Event = [TargetEvent, Path, Path?];
|
||||
type FSHandler = (event?: FSTargetEvent, targetName?: string) => void;
|
||||
type Handler = (event: TargetEvent, targetPath: Path, targetPathNext?: Path) => void;
|
||||
type HandlerBatched = (event?: FSTargetEvent, targetPath?: Path, isInitial?: boolean) => Promise<void>;
|
||||
type Ignore = ((targetPath: Path) => boolean) | RegExp;
|
||||
type INO = bigint | number;
|
||||
type Path = string;
|
||||
type ReaddirMap = ResultDirectories;
|
||||
type Stats = BigIntStats;
|
||||
type LocksAdd = Map<INO, () => void>;
|
||||
type LocksUnlink = Map<INO, () => Path>;
|
||||
type LocksPair = {
|
||||
add: LocksAdd;
|
||||
unlink: LocksUnlink;
|
||||
};
|
||||
type LockConfig = {
|
||||
ino?: INO;
|
||||
targetPath: Path;
|
||||
locks: LocksPair;
|
||||
events: {
|
||||
add: TargetEvent.ADD | TargetEvent.ADD_DIR;
|
||||
change?: TargetEvent.CHANGE;
|
||||
rename: TargetEvent.RENAME | TargetEvent.RENAME_DIR;
|
||||
unlink: TargetEvent.UNLINK | TargetEvent.UNLINK_DIR;
|
||||
};
|
||||
};
|
||||
type PollerConfig = {
|
||||
options: WatcherOptions;
|
||||
targetPath: Path;
|
||||
};
|
||||
type SubwatcherConfig = {
|
||||
options: WatcherOptions;
|
||||
targetPath: Path;
|
||||
};
|
||||
type WatcherConfig = {
|
||||
handler: Handler;
|
||||
watcher: FSWatcher;
|
||||
options: WatcherOptions;
|
||||
folderPath: Path;
|
||||
filePath?: Path;
|
||||
};
|
||||
type WatcherOptions = {
|
||||
debounce?: number;
|
||||
depth?: number;
|
||||
limit?: number;
|
||||
ignore?: Ignore;
|
||||
ignoreInitial?: boolean;
|
||||
native?: boolean;
|
||||
persistent?: boolean;
|
||||
pollingInterval?: number;
|
||||
pollingTimeout?: number;
|
||||
readdirMap?: ReaddirMap;
|
||||
recursive?: boolean;
|
||||
renameDetection?: boolean;
|
||||
renameTimeout?: number;
|
||||
};
|
||||
export type { Callback, Disposer, Event, FSHandler, FSWatcher, Handler, HandlerBatched, Ignore, INO, Path, ReaddirMap, Stats, LocksAdd, LocksUnlink, LocksPair, LockConfig, PollerConfig, SubwatcherConfig, WatcherConfig, WatcherOptions, WatcherStats };
|
||||
2
node_modules/watcher/dist/types.js
generated
vendored
Normal file
2
node_modules/watcher/dist/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/* IMPORT */
|
||||
export {};
|
||||
37
node_modules/watcher/dist/utils.d.ts
generated
vendored
Normal file
37
node_modules/watcher/dist/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import type { Callback, Ignore, ReaddirMap, Stats } from './types';
|
||||
declare const Utils: {
|
||||
lang: {
|
||||
debounce: <Args extends unknown[]>(fn: import("dettle/dist/types").FN<Args, unknown>, wait?: number | undefined, options?: import("dettle/dist/types").DebounceOptions | undefined) => import("dettle/dist/types").Debounced<Args>;
|
||||
attempt: <T>(fn: () => T) => T | Error;
|
||||
castArray: <T_1>(x: T_1 | T_1[]) => T_1[];
|
||||
castError: (exception: unknown) => Error;
|
||||
defer: (callback: Callback) => NodeJS.Timeout;
|
||||
isArray: (value: unknown) => value is unknown[];
|
||||
isError: (value: unknown) => value is Error;
|
||||
isFunction: (value: unknown) => value is Function;
|
||||
isNaN: (value: unknown) => value is number;
|
||||
isNumber: (value: unknown) => value is number;
|
||||
isPrimitive: (value: unknown) => value is string | number | bigint | boolean | symbol | null | undefined;
|
||||
isShallowEqual: (x: any, y: any) => boolean;
|
||||
isSet: (value: unknown) => value is Set<unknown>;
|
||||
isString: (value: unknown) => value is string;
|
||||
isUndefined: (value: unknown) => value is undefined;
|
||||
noop: () => undefined;
|
||||
uniq: <T_2>(arr: T_2[]) => T_2[];
|
||||
};
|
||||
fs: {
|
||||
getDepth: (targetPath: string) => number;
|
||||
getRealPath: (targetPath: string, native?: boolean) => string | undefined;
|
||||
isSubPath: (targetPath: string, subPath: string) => boolean;
|
||||
poll: (targetPath: string, timeout?: number) => Promise<Stats | undefined>;
|
||||
readdir: (rootPath: string, ignore?: Ignore, depth?: number, limit?: number, signal?: {
|
||||
aborted: boolean;
|
||||
}, readdirMap?: ReaddirMap) => Promise<[string[], string[]]>;
|
||||
};
|
||||
};
|
||||
export default Utils;
|
||||
120
node_modules/watcher/dist/utils.js
generated
vendored
Normal file
120
node_modules/watcher/dist/utils.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
/* IMPORT */
|
||||
import { debounce } from 'dettle';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import sfs from 'stubborn-fs';
|
||||
import readdir from 'tiny-readdir';
|
||||
import { POLLING_TIMEOUT } from './constants.js';
|
||||
/* MAIN */
|
||||
const Utils = {
|
||||
/* LANG API */
|
||||
lang: {
|
||||
debounce,
|
||||
attempt: (fn) => {
|
||||
try {
|
||||
return fn();
|
||||
}
|
||||
catch (error) {
|
||||
return Utils.lang.castError(error);
|
||||
}
|
||||
},
|
||||
castArray: (x) => {
|
||||
return Utils.lang.isArray(x) ? x : [x];
|
||||
},
|
||||
castError: (exception) => {
|
||||
if (Utils.lang.isError(exception))
|
||||
return exception;
|
||||
if (Utils.lang.isString(exception))
|
||||
return new Error(exception);
|
||||
return new Error('Unknown error');
|
||||
},
|
||||
defer: (callback) => {
|
||||
return setTimeout(callback, 0);
|
||||
},
|
||||
isArray: (value) => {
|
||||
return Array.isArray(value);
|
||||
},
|
||||
isError: (value) => {
|
||||
return value instanceof Error;
|
||||
},
|
||||
isFunction: (value) => {
|
||||
return typeof value === 'function';
|
||||
},
|
||||
isNaN: (value) => {
|
||||
return Number.isNaN(value);
|
||||
},
|
||||
isNumber: (value) => {
|
||||
return typeof value === 'number';
|
||||
},
|
||||
isPrimitive: (value) => {
|
||||
if (value === null)
|
||||
return true;
|
||||
const type = typeof value;
|
||||
return type !== 'object' && type !== 'function';
|
||||
},
|
||||
isShallowEqual: (x, y) => {
|
||||
if (x === y)
|
||||
return true;
|
||||
if (Utils.lang.isNaN(x))
|
||||
return Utils.lang.isNaN(y);
|
||||
if (Utils.lang.isPrimitive(x) || Utils.lang.isPrimitive(y))
|
||||
return x === y;
|
||||
for (const i in x)
|
||||
if (!(i in y))
|
||||
return false;
|
||||
for (const i in y)
|
||||
if (x[i] !== y[i])
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
isSet: (value) => {
|
||||
return value instanceof Set;
|
||||
},
|
||||
isString: (value) => {
|
||||
return typeof value === 'string';
|
||||
},
|
||||
isUndefined: (value) => {
|
||||
return value === undefined;
|
||||
},
|
||||
noop: () => {
|
||||
return;
|
||||
},
|
||||
uniq: (arr) => {
|
||||
if (arr.length < 2)
|
||||
return arr;
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
},
|
||||
/* FS API */
|
||||
fs: {
|
||||
getDepth: (targetPath) => {
|
||||
return Math.max(0, targetPath.split(path.sep).length - 1);
|
||||
},
|
||||
getRealPath: (targetPath, native) => {
|
||||
try {
|
||||
return native ? fs.realpathSync.native(targetPath) : fs.realpathSync(targetPath);
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
},
|
||||
isSubPath: (targetPath, subPath) => {
|
||||
return (subPath.startsWith(targetPath) && subPath[targetPath.length] === path.sep && (subPath.length - targetPath.length) > path.sep.length);
|
||||
},
|
||||
poll: (targetPath, timeout = POLLING_TIMEOUT) => {
|
||||
return sfs.retry.stat(timeout)(targetPath, { bigint: true }).catch(Utils.lang.noop);
|
||||
},
|
||||
readdir: async (rootPath, ignore, depth = Infinity, limit = Infinity, signal, readdirMap) => {
|
||||
if (readdirMap && depth === 1 && rootPath in readdirMap) { // Reusing cached data
|
||||
const result = readdirMap[rootPath];
|
||||
return [result.directories, result.files];
|
||||
}
|
||||
else { // Retrieving fresh data
|
||||
const result = await readdir(rootPath, { depth, limit, ignore, signal });
|
||||
return [result.directories, result.files];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* EXPORT */
|
||||
export default Utils;
|
||||
55
node_modules/watcher/dist/watcher.d.ts
generated
vendored
Normal file
55
node_modules/watcher/dist/watcher.d.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { TargetEvent } from './enums';
|
||||
import WatcherHandler from './watcher_handler';
|
||||
import WatcherLocker from './watcher_locker';
|
||||
import WatcherPoller from './watcher_poller';
|
||||
import type { Callback, Disposer, Handler, Ignore, Path, PollerConfig, SubwatcherConfig, WatcherOptions, WatcherConfig } from './types';
|
||||
declare class Watcher extends EventEmitter {
|
||||
_closed: boolean;
|
||||
_ready: boolean;
|
||||
_closeAborter: AbortController;
|
||||
_closeSignal: {
|
||||
aborted: boolean;
|
||||
};
|
||||
_closeWait: Promise<void>;
|
||||
_readyWait: Promise<void>;
|
||||
_locker: WatcherLocker;
|
||||
_roots: Set<Path>;
|
||||
_poller: WatcherPoller;
|
||||
_pollers: Set<PollerConfig>;
|
||||
_subwatchers: Set<SubwatcherConfig>;
|
||||
_watchers: Record<Path, WatcherConfig[]>;
|
||||
_watchersLock: Promise<void>;
|
||||
_watchersRestorable: Record<Path, WatcherConfig>;
|
||||
_watchersRestoreTimeout?: NodeJS.Timeout;
|
||||
constructor(target?: Path[] | Path | Handler, options?: WatcherOptions | Handler, handler?: Handler);
|
||||
isClosed(): boolean;
|
||||
isIgnored(targetPath: Path, ignore?: Ignore): boolean;
|
||||
isReady(): boolean;
|
||||
close(): boolean;
|
||||
error(exception: unknown): boolean;
|
||||
event(event: TargetEvent, targetPath: Path, targetPathNext?: Path): boolean;
|
||||
ready(): boolean;
|
||||
pollerExists(targetPath: Path, options: WatcherOptions): boolean;
|
||||
subwatcherExists(targetPath: Path, options: WatcherOptions): boolean;
|
||||
watchersClose(folderPath?: Path, filePath?: Path, recursive?: boolean): void;
|
||||
watchersLock(callback: Callback): Promise<void>;
|
||||
watchersRestore(): void;
|
||||
watcherAdd(config: WatcherConfig, baseWatcherHandler?: WatcherHandler): Promise<WatcherHandler>;
|
||||
watcherClose(config: WatcherConfig): void;
|
||||
watcherExists(folderPath: Path, options: WatcherOptions, handler: Handler, filePath?: Path): boolean;
|
||||
watchDirectories(foldersPaths: Path[], options: WatcherOptions, handler: Handler, filePath?: Path, baseWatcherHandler?: WatcherHandler): Promise<WatcherHandler | undefined>;
|
||||
watchDirectory(folderPath: Path, options: WatcherOptions, handler: Handler, filePath?: Path, baseWatcherHandler?: WatcherHandler): Promise<void>;
|
||||
watchFileOnce(filePath: Path, options: WatcherOptions, callback: Callback): Promise<void>;
|
||||
watchFile(filePath: Path, options: WatcherOptions, handler: Handler): Promise<void>;
|
||||
watchPollingOnce(targetPath: Path, options: WatcherOptions, callback: Callback): Promise<void>;
|
||||
watchPolling(targetPath: Path, options: WatcherOptions, callback: Callback): Promise<Disposer>;
|
||||
watchUnknownChild(targetPath: Path, options: WatcherOptions, handler: Handler): Promise<void>;
|
||||
watchUnknownTarget(targetPath: Path, options: WatcherOptions, handler: Handler): Promise<void>;
|
||||
watchPaths(targetPaths: Path[], options: WatcherOptions, handler: Handler): Promise<void>;
|
||||
watchPath(targetPath: Path, options: WatcherOptions, handler: Handler): Promise<void>;
|
||||
watch(target?: Path[] | Path | Handler, options?: WatcherOptions | Handler, handler?: Handler): Promise<void>;
|
||||
}
|
||||
export default Watcher;
|
||||
396
node_modules/watcher/dist/watcher.js
generated
vendored
Normal file
396
node_modules/watcher/dist/watcher.js
generated
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
/* IMPORT */
|
||||
import { EventEmitter } from 'node:events';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { DEPTH, LIMIT, HAS_NATIVE_RECURSION, POLLING_INTERVAL } from './constants.js';
|
||||
import { TargetEvent, WatcherEvent } from './enums.js';
|
||||
import Utils from './utils.js';
|
||||
import WatcherHandler from './watcher_handler.js';
|
||||
import WatcherLocker from './watcher_locker.js';
|
||||
import WatcherPoller from './watcher_poller.js';
|
||||
/* MAIN */
|
||||
class Watcher extends EventEmitter {
|
||||
/* CONSTRUCTOR */
|
||||
constructor(target, options, handler) {
|
||||
super();
|
||||
this._closed = false;
|
||||
this._ready = false;
|
||||
this._closeAborter = new AbortController();
|
||||
this._closeSignal = this._closeAborter.signal;
|
||||
this.on(WatcherEvent.CLOSE, () => this._closeAborter.abort());
|
||||
this._closeWait = new Promise(resolve => this.on(WatcherEvent.CLOSE, resolve));
|
||||
this._readyWait = new Promise(resolve => this.on(WatcherEvent.READY, resolve));
|
||||
this._locker = new WatcherLocker(this);
|
||||
this._roots = new Set();
|
||||
this._poller = new WatcherPoller();
|
||||
this._pollers = new Set();
|
||||
this._subwatchers = new Set();
|
||||
this._watchers = {};
|
||||
this._watchersLock = Promise.resolve();
|
||||
this._watchersRestorable = {};
|
||||
this.watch(target, options, handler);
|
||||
}
|
||||
/* API */
|
||||
isClosed() {
|
||||
return this._closed;
|
||||
}
|
||||
isIgnored(targetPath, ignore) {
|
||||
return !!ignore && (Utils.lang.isFunction(ignore) ? !!ignore(targetPath) : ignore.test(targetPath));
|
||||
}
|
||||
isReady() {
|
||||
return this._ready;
|
||||
}
|
||||
close() {
|
||||
this._locker.reset();
|
||||
this._poller.reset();
|
||||
this._roots.clear();
|
||||
this.watchersClose();
|
||||
if (this.isClosed())
|
||||
return false;
|
||||
this._closed = true;
|
||||
return this.emit(WatcherEvent.CLOSE);
|
||||
}
|
||||
error(exception) {
|
||||
if (this.isClosed())
|
||||
return false;
|
||||
const error = Utils.lang.castError(exception);
|
||||
return this.emit(WatcherEvent.ERROR, error);
|
||||
}
|
||||
event(event, targetPath, targetPathNext) {
|
||||
if (this.isClosed())
|
||||
return false;
|
||||
this.emit(WatcherEvent.ALL, event, targetPath, targetPathNext);
|
||||
return this.emit(event, targetPath, targetPathNext);
|
||||
}
|
||||
ready() {
|
||||
if (this.isClosed() || this.isReady())
|
||||
return false;
|
||||
this._ready = true;
|
||||
return this.emit(WatcherEvent.READY);
|
||||
}
|
||||
pollerExists(targetPath, options) {
|
||||
for (const poller of this._pollers) {
|
||||
if (poller.targetPath !== targetPath)
|
||||
continue;
|
||||
if (!Utils.lang.isShallowEqual(poller.options, options))
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
subwatcherExists(targetPath, options) {
|
||||
for (const subwatcher of this._subwatchers) {
|
||||
if (subwatcher.targetPath !== targetPath)
|
||||
continue;
|
||||
if (!Utils.lang.isShallowEqual(subwatcher.options, options))
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
watchersClose(folderPath, filePath, recursive = true) {
|
||||
if (!folderPath) {
|
||||
for (const folderPath in this._watchers) {
|
||||
this.watchersClose(folderPath, filePath, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const configs = this._watchers[folderPath];
|
||||
if (configs) {
|
||||
for (const config of [...configs]) { // It's important to clone the array, as items will be deleted from it also
|
||||
if (filePath && config.filePath !== filePath)
|
||||
continue;
|
||||
this.watcherClose(config);
|
||||
}
|
||||
}
|
||||
if (recursive) {
|
||||
for (const folderPathOther in this._watchers) {
|
||||
if (!Utils.fs.isSubPath(folderPath, folderPathOther))
|
||||
continue;
|
||||
this.watchersClose(folderPathOther, filePath, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
watchersLock(callback) {
|
||||
return this._watchersLock.then(() => {
|
||||
return this._watchersLock = new Promise(async (resolve) => {
|
||||
await callback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
watchersRestore() {
|
||||
delete this._watchersRestoreTimeout;
|
||||
const watchers = Object.entries(this._watchersRestorable);
|
||||
this._watchersRestorable = {};
|
||||
for (const [targetPath, config] of watchers) {
|
||||
this.watchPath(targetPath, config.options, config.handler);
|
||||
}
|
||||
}
|
||||
async watcherAdd(config, baseWatcherHandler) {
|
||||
const { folderPath } = config;
|
||||
const configs = this._watchers[folderPath] = (this._watchers[folderPath] || []);
|
||||
configs.push(config);
|
||||
const watcherHandler = new WatcherHandler(this, config, baseWatcherHandler);
|
||||
await watcherHandler.init();
|
||||
return watcherHandler;
|
||||
}
|
||||
watcherClose(config) {
|
||||
config.watcher.close();
|
||||
const configs = this._watchers[config.folderPath];
|
||||
if (configs) {
|
||||
const index = configs.indexOf(config);
|
||||
configs.splice(index, 1);
|
||||
if (!configs.length) {
|
||||
delete this._watchers[config.folderPath];
|
||||
}
|
||||
}
|
||||
const rootPath = config.filePath || config.folderPath;
|
||||
const isRoot = this._roots.has(rootPath);
|
||||
if (isRoot) {
|
||||
this._watchersRestorable[rootPath] = config;
|
||||
if (!this._watchersRestoreTimeout) {
|
||||
this._watchersRestoreTimeout = Utils.lang.defer(() => this.watchersRestore());
|
||||
}
|
||||
}
|
||||
}
|
||||
watcherExists(folderPath, options, handler, filePath) {
|
||||
const configsSibling = this._watchers[folderPath];
|
||||
if (!!configsSibling?.find(config => config.handler === handler && (!config.filePath || config.filePath === filePath) && config.options.ignore === options.ignore && !!config.options.native === !!options.native && (!options.recursive || config.options.recursive)))
|
||||
return true;
|
||||
let folderAncestorPath = path.dirname(folderPath);
|
||||
for (let depth = 1; depth < Infinity; depth++) {
|
||||
const configsAncestor = this._watchers[folderAncestorPath];
|
||||
if (!!configsAncestor?.find(config => (depth === 1 || (config.options.recursive && depth <= (config.options.depth ?? DEPTH))) && config.handler === handler && (!config.filePath || config.filePath === filePath) && config.options.ignore === options.ignore && !!config.options.native === !!options.native && (!options.recursive || (config.options.recursive && (HAS_NATIVE_RECURSION && config.options.native !== false)))))
|
||||
return true;
|
||||
if (!HAS_NATIVE_RECURSION)
|
||||
break; // No other ancestor will possibly be found
|
||||
const folderAncestorPathNext = path.dirname(folderPath);
|
||||
if (folderAncestorPath === folderAncestorPathNext)
|
||||
break;
|
||||
folderAncestorPath = folderAncestorPathNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async watchDirectories(foldersPaths, options, handler, filePath, baseWatcherHandler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
foldersPaths = Utils.lang.uniq(foldersPaths).sort();
|
||||
let watcherHandlerLast;
|
||||
for (const folderPath of foldersPaths) {
|
||||
if (this.isIgnored(folderPath, options.ignore))
|
||||
continue;
|
||||
if (this.watcherExists(folderPath, options, handler, filePath))
|
||||
continue;
|
||||
try {
|
||||
const watcherOptions = (!options.recursive || (HAS_NATIVE_RECURSION && options.native !== false)) ? options : { ...options, recursive: false }; // Ensuring recursion is explicitly disabled if not available
|
||||
const watcher = fs.watch(folderPath, watcherOptions);
|
||||
const watcherConfig = { watcher, handler, options, folderPath, filePath };
|
||||
const watcherHandler = watcherHandlerLast = await this.watcherAdd(watcherConfig, baseWatcherHandler);
|
||||
const isRoot = this._roots.has(filePath || folderPath);
|
||||
if (isRoot) {
|
||||
const parentOptions = { ...options, ignoreInitial: true, recursive: false }; // Ensuring only the parent folder is being watched
|
||||
const parentFolderPath = path.dirname(folderPath);
|
||||
const parentFilePath = folderPath;
|
||||
await this.watchDirectories([parentFolderPath], parentOptions, handler, parentFilePath, watcherHandler);
|
||||
//TODO: Watch parents recursively with the following code, which requires other things to be changed too though
|
||||
// while ( true ) {
|
||||
// await this.watchDirectories ( [parentFolderPath], parentOptions, handler, parentFilePath, watcherHandler );
|
||||
// const parentFolderPathNext = path.dirname ( parentFolderPath );
|
||||
// if ( parentFolderPath === parentFolderPathNext ) break;
|
||||
// parentFilePath = parentFolderPath;
|
||||
// parentFolderPath = parentFolderPathNext;
|
||||
// }
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
this.error(error);
|
||||
}
|
||||
}
|
||||
return watcherHandlerLast;
|
||||
}
|
||||
async watchDirectory(folderPath, options, handler, filePath, baseWatcherHandler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
if (this.isIgnored(folderPath, options.ignore))
|
||||
return;
|
||||
if (!options.recursive || (HAS_NATIVE_RECURSION && options.native !== false)) {
|
||||
return this.watchersLock(() => {
|
||||
return this.watchDirectories([folderPath], options, handler, filePath, baseWatcherHandler);
|
||||
});
|
||||
}
|
||||
else {
|
||||
options = { ...options, recursive: true }; // Ensuring recursion is explicitly enabled
|
||||
const depth = options.depth ?? DEPTH;
|
||||
const limit = options.limit ?? LIMIT;
|
||||
const [folderSubPaths] = await Utils.fs.readdir(folderPath, options.ignore, depth, limit, this._closeSignal, options.readdirMap);
|
||||
return this.watchersLock(async () => {
|
||||
const watcherHandler = await this.watchDirectories([folderPath], options, handler, filePath, baseWatcherHandler);
|
||||
if (folderSubPaths.length) {
|
||||
const folderPathDepth = Utils.fs.getDepth(folderPath);
|
||||
for (const folderSubPath of folderSubPaths) {
|
||||
const folderSubPathDepth = Utils.fs.getDepth(folderSubPath);
|
||||
const subDepth = Math.max(0, depth - (folderSubPathDepth - folderPathDepth));
|
||||
const subOptions = { ...options, depth: subDepth }; // Updating the maximum depth to account for depth of the sub path
|
||||
await this.watchDirectories([folderSubPath], subOptions, handler, filePath, baseWatcherHandler || watcherHandler);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
async watchFileOnce(filePath, options, callback) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
options = { ...options, ignoreInitial: false }; // Ensuring initial events are detected too
|
||||
if (this.subwatcherExists(filePath, options))
|
||||
return;
|
||||
const config = { targetPath: filePath, options };
|
||||
const handler = (event, targetPath) => {
|
||||
if (targetPath !== filePath)
|
||||
return;
|
||||
stop();
|
||||
callback();
|
||||
};
|
||||
const watcher = new Watcher(handler);
|
||||
const start = () => {
|
||||
this._subwatchers.add(config);
|
||||
this.on(WatcherEvent.CLOSE, stop); // Ensuring the subwatcher is stopped on close
|
||||
watcher.watchFile(filePath, options, handler);
|
||||
};
|
||||
const stop = () => {
|
||||
this._subwatchers.delete(config);
|
||||
this.removeListener(WatcherEvent.CLOSE, stop); // Ensuring there are no leftover listeners
|
||||
watcher.close();
|
||||
};
|
||||
return start();
|
||||
}
|
||||
async watchFile(filePath, options, handler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
if (this.isIgnored(filePath, options.ignore))
|
||||
return;
|
||||
options = { ...options, recursive: false }; // Ensuring recursion is explicitly disabled
|
||||
const folderPath = path.dirname(filePath);
|
||||
return this.watchDirectory(folderPath, options, handler, filePath);
|
||||
}
|
||||
async watchPollingOnce(targetPath, options, callback) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
let isDone = false;
|
||||
const poller = new WatcherPoller();
|
||||
const disposer = await this.watchPolling(targetPath, options, async () => {
|
||||
if (isDone)
|
||||
return;
|
||||
const events = await poller.update(targetPath, options.pollingTimeout);
|
||||
if (!events.length)
|
||||
return; // Nothing actually changed, skipping
|
||||
if (isDone)
|
||||
return; // Another async callback has done the work already, skipping
|
||||
isDone = true;
|
||||
disposer();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
async watchPolling(targetPath, options, callback) {
|
||||
if (this.isClosed())
|
||||
return Utils.lang.noop;
|
||||
if (this.pollerExists(targetPath, options))
|
||||
return Utils.lang.noop;
|
||||
const watcherOptions = { ...options, interval: options.pollingInterval ?? POLLING_INTERVAL }; // Ensuring a default interval is set
|
||||
const config = { targetPath, options };
|
||||
const start = () => {
|
||||
this._pollers.add(config);
|
||||
this.on(WatcherEvent.CLOSE, stop); // Ensuring polling is stopped on close
|
||||
fs.watchFile(targetPath, watcherOptions, callback);
|
||||
};
|
||||
const stop = () => {
|
||||
this._pollers.delete(config);
|
||||
this.removeListener(WatcherEvent.CLOSE, stop); // Ensuring there are no leftover listeners
|
||||
fs.unwatchFile(targetPath, callback);
|
||||
};
|
||||
Utils.lang.attempt(start);
|
||||
return () => Utils.lang.attempt(stop);
|
||||
}
|
||||
async watchUnknownChild(targetPath, options, handler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
const watch = () => this.watchPath(targetPath, options, handler);
|
||||
return this.watchFileOnce(targetPath, options, watch);
|
||||
}
|
||||
async watchUnknownTarget(targetPath, options, handler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
const watch = () => this.watchPath(targetPath, options, handler);
|
||||
return this.watchPollingOnce(targetPath, options, watch);
|
||||
}
|
||||
async watchPaths(targetPaths, options, handler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
targetPaths = Utils.lang.uniq(targetPaths).sort();
|
||||
const isParallelizable = targetPaths.every((targetPath, index) => targetPaths.every((t, i) => i === index || !Utils.fs.isSubPath(targetPath, t))); // All paths are about separate subtrees, so we can start watching in parallel safely //TODO: Find parallelizable chunks rather than using an all or nothing approach
|
||||
if (isParallelizable) { // Watching in parallel
|
||||
await Promise.all(targetPaths.map(targetPath => {
|
||||
return this.watchPath(targetPath, options, handler);
|
||||
}));
|
||||
}
|
||||
else { // Watching serially
|
||||
for (const targetPath of targetPaths) {
|
||||
await this.watchPath(targetPath, options, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
async watchPath(targetPath, options, handler) {
|
||||
if (this.isClosed())
|
||||
return;
|
||||
targetPath = path.resolve(targetPath);
|
||||
if (this.isIgnored(targetPath, options.ignore))
|
||||
return;
|
||||
const stats = await Utils.fs.poll(targetPath, options.pollingTimeout);
|
||||
if (!stats) {
|
||||
const parentPath = path.dirname(targetPath);
|
||||
const parentStats = await Utils.fs.poll(parentPath, options.pollingTimeout);
|
||||
if (parentStats?.isDirectory()) {
|
||||
return this.watchUnknownChild(targetPath, options, handler);
|
||||
}
|
||||
else {
|
||||
return this.watchUnknownTarget(targetPath, options, handler);
|
||||
}
|
||||
}
|
||||
else if (stats.isFile()) {
|
||||
return this.watchFile(targetPath, options, handler);
|
||||
}
|
||||
else if (stats.isDirectory()) {
|
||||
return this.watchDirectory(targetPath, options, handler);
|
||||
}
|
||||
else {
|
||||
this.error(`"${targetPath}" is not supported`);
|
||||
}
|
||||
}
|
||||
async watch(target, options, handler = Utils.lang.noop) {
|
||||
if (Utils.lang.isFunction(target))
|
||||
return this.watch([], {}, target);
|
||||
if (Utils.lang.isUndefined(target))
|
||||
return this.watch([], options, handler);
|
||||
if (Utils.lang.isFunction(options))
|
||||
return this.watch(target, {}, options);
|
||||
if (Utils.lang.isUndefined(options))
|
||||
return this.watch(target, {}, handler);
|
||||
if (this.isClosed())
|
||||
return;
|
||||
if (this.isReady())
|
||||
options.readdirMap = undefined; // Only usable before initialization
|
||||
const targetPaths = Utils.lang.castArray(target);
|
||||
targetPaths.forEach(targetPath => this._roots.add(targetPath));
|
||||
await this.watchPaths(targetPaths, options, handler);
|
||||
if (this.isClosed())
|
||||
return;
|
||||
if (handler !== Utils.lang.noop) {
|
||||
this.on(WatcherEvent.ALL, handler);
|
||||
}
|
||||
options.readdirMap = undefined; // Only usable before initialization
|
||||
this.ready();
|
||||
}
|
||||
}
|
||||
/* EXPORT */
|
||||
export default Watcher;
|
||||
36
node_modules/watcher/dist/watcher_handler.d.ts
generated
vendored
Normal file
36
node_modules/watcher/dist/watcher_handler.d.ts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { FSTargetEvent } from './enums';
|
||||
import type Watcher from './watcher';
|
||||
import type { Event, FSWatcher, Handler, HandlerBatched, Path, WatcherOptions, WatcherConfig } from './types';
|
||||
declare class WatcherHandler {
|
||||
base?: WatcherHandler;
|
||||
watcher: Watcher;
|
||||
handler: Handler;
|
||||
handlerBatched: HandlerBatched;
|
||||
fswatcher: FSWatcher;
|
||||
options: WatcherOptions;
|
||||
folderPath: Path;
|
||||
filePath?: Path;
|
||||
constructor(watcher: Watcher, config: WatcherConfig, base?: WatcherHandler);
|
||||
_isSubRoot(targetPath: Path): boolean;
|
||||
_makeHandlerBatched(delay?: number): (event: FSTargetEvent, targetPath?: Path, isInitial?: boolean) => Promise<void>;
|
||||
eventsDeduplicate(events: Event[]): Event[];
|
||||
eventsPopulate(targetPaths: Path[], events?: Event[], isInitial?: boolean): Promise<Event[]>;
|
||||
eventsPopulateAddDir(targetPaths: Path[], targetPath: Path, events?: Event[], isInitial?: boolean): Promise<Event[]>;
|
||||
eventsPopulateUnlinkDir(targetPaths: Path[], targetPath: Path, events?: Event[], isInitial?: boolean): Promise<Event[]>;
|
||||
onTargetAdd(targetPath: Path): void;
|
||||
onTargetAddDir(targetPath: Path): void;
|
||||
onTargetChange(targetPath: Path): void;
|
||||
onTargetUnlink(targetPath: Path): void;
|
||||
onTargetUnlinkDir(targetPath: Path): void;
|
||||
onTargetEvent(event: Event): void;
|
||||
onTargetEvents(events: Event[]): void;
|
||||
onWatcherEvent(event?: FSTargetEvent, targetPath?: Path, isInitial?: boolean): Promise<void>;
|
||||
onWatcherChange(event?: FSTargetEvent, targetName?: string | null): void;
|
||||
onWatcherError(error: NodeJS.ErrnoException): void;
|
||||
init(): Promise<void>;
|
||||
initWatcherEvents(): Promise<void>;
|
||||
initInitialEvents(): Promise<void>;
|
||||
}
|
||||
export default WatcherHandler;
|
||||
248
node_modules/watcher/dist/watcher_handler.js
generated
vendored
Normal file
248
node_modules/watcher/dist/watcher_handler.js
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
/* IMPORT */
|
||||
import path from 'node:path';
|
||||
import { DEBOUNCE, DEPTH, LIMIT, HAS_NATIVE_RECURSION, IS_WINDOWS } from './constants.js';
|
||||
import { FSTargetEvent, FSWatcherEvent, TargetEvent } from './enums.js';
|
||||
import Utils from './utils.js';
|
||||
/* MAIN */
|
||||
class WatcherHandler {
|
||||
/* CONSTRUCTOR */
|
||||
constructor(watcher, config, base) {
|
||||
this.base = base;
|
||||
this.watcher = watcher;
|
||||
this.handler = config.handler;
|
||||
this.fswatcher = config.watcher;
|
||||
this.options = config.options;
|
||||
this.folderPath = config.folderPath;
|
||||
this.filePath = config.filePath;
|
||||
this.handlerBatched = this.base ? this.base.onWatcherEvent.bind(this.base) : this._makeHandlerBatched(this.options.debounce); //UGLY
|
||||
}
|
||||
/* HELPERS */
|
||||
_isSubRoot(targetPath) {
|
||||
if (this.filePath) {
|
||||
return targetPath === this.filePath;
|
||||
}
|
||||
else {
|
||||
return targetPath === this.folderPath || Utils.fs.isSubPath(this.folderPath, targetPath);
|
||||
}
|
||||
}
|
||||
_makeHandlerBatched(delay = DEBOUNCE) {
|
||||
return (() => {
|
||||
let lock = this.watcher._readyWait; // ~Ensuring no two flushes are active in parallel, or before the watcher is ready
|
||||
let initials = [];
|
||||
let regulars = new Set();
|
||||
const flush = async (initials, regulars) => {
|
||||
const initialEvents = this.options.ignoreInitial ? [] : initials;
|
||||
const regularEvents = await this.eventsPopulate([...regulars]);
|
||||
const events = this.eventsDeduplicate([...initialEvents, ...regularEvents]);
|
||||
this.onTargetEvents(events);
|
||||
};
|
||||
const flushDebounced = Utils.lang.debounce(() => {
|
||||
if (this.watcher.isClosed())
|
||||
return;
|
||||
lock = flush(initials, regulars);
|
||||
initials = [];
|
||||
regulars = new Set();
|
||||
}, delay);
|
||||
return async (event, targetPath = '', isInitial = false) => {
|
||||
if (isInitial) { // Poll immediately
|
||||
await this.eventsPopulate([targetPath], initials, true);
|
||||
}
|
||||
else { // Poll later
|
||||
regulars.add(targetPath);
|
||||
}
|
||||
lock.then(flushDebounced);
|
||||
};
|
||||
})();
|
||||
}
|
||||
/* EVENT HELPERS */
|
||||
eventsDeduplicate(events) {
|
||||
if (events.length < 2)
|
||||
return events;
|
||||
const targetsEventPrev = {};
|
||||
return events.reduce((acc, event) => {
|
||||
const [targetEvent, targetPath] = event;
|
||||
const targetEventPrev = targetsEventPrev[targetPath];
|
||||
if (targetEvent === targetEventPrev)
|
||||
return acc; // Same event, ignoring
|
||||
if (targetEvent === TargetEvent.CHANGE && targetEventPrev === TargetEvent.ADD)
|
||||
return acc; // "change" after "add", ignoring
|
||||
targetsEventPrev[targetPath] = targetEvent;
|
||||
acc.push(event);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
async eventsPopulate(targetPaths, events = [], isInitial = false) {
|
||||
await Promise.all(targetPaths.map(async (targetPath) => {
|
||||
const targetEvents = await this.watcher._poller.update(targetPath, this.options.pollingTimeout);
|
||||
await Promise.all(targetEvents.map(async (event) => {
|
||||
events.push([event, targetPath]);
|
||||
if (event === TargetEvent.ADD_DIR) {
|
||||
await this.eventsPopulateAddDir(targetPaths, targetPath, events, isInitial);
|
||||
}
|
||||
else if (event === TargetEvent.UNLINK_DIR) {
|
||||
await this.eventsPopulateUnlinkDir(targetPaths, targetPath, events, isInitial);
|
||||
}
|
||||
}));
|
||||
}));
|
||||
return events;
|
||||
}
|
||||
;
|
||||
async eventsPopulateAddDir(targetPaths, targetPath, events = [], isInitial = false) {
|
||||
if (isInitial)
|
||||
return events;
|
||||
const depth = this.options.recursive ? this.options.depth ?? DEPTH : Math.min(1, this.options.depth ?? DEPTH);
|
||||
const limit = this.options.limit ?? LIMIT;
|
||||
const [directories, files] = await Utils.fs.readdir(targetPath, this.options.ignore, depth, limit, this.watcher._closeSignal);
|
||||
const targetSubPaths = [...directories, ...files];
|
||||
await Promise.all(targetSubPaths.map(targetSubPath => {
|
||||
if (this.watcher.isIgnored(targetSubPath, this.options.ignore))
|
||||
return;
|
||||
if (targetPaths.includes(targetSubPath))
|
||||
return;
|
||||
return this.eventsPopulate([targetSubPath], events, true);
|
||||
}));
|
||||
return events;
|
||||
}
|
||||
async eventsPopulateUnlinkDir(targetPaths, targetPath, events = [], isInitial = false) {
|
||||
if (isInitial)
|
||||
return events;
|
||||
for (const folderPathOther of this.watcher._poller.stats.keys()) {
|
||||
if (!Utils.fs.isSubPath(targetPath, folderPathOther))
|
||||
continue;
|
||||
if (targetPaths.includes(folderPathOther))
|
||||
continue;
|
||||
await this.eventsPopulate([folderPathOther], events, true);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
/* EVENT HANDLERS */
|
||||
onTargetAdd(targetPath) {
|
||||
if (this._isSubRoot(targetPath)) {
|
||||
if (this.options.renameDetection) {
|
||||
this.watcher._locker.getLockTargetAdd(targetPath, this.options.renameTimeout);
|
||||
}
|
||||
else {
|
||||
this.watcher.event(TargetEvent.ADD, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
onTargetAddDir(targetPath) {
|
||||
if (targetPath !== this.folderPath && this.options.recursive && (!HAS_NATIVE_RECURSION && this.options.native !== false)) {
|
||||
this.watcher.watchDirectory(targetPath, this.options, this.handler, undefined, this.base || this);
|
||||
}
|
||||
if (this._isSubRoot(targetPath)) {
|
||||
if (this.options.renameDetection) {
|
||||
this.watcher._locker.getLockTargetAddDir(targetPath, this.options.renameTimeout);
|
||||
}
|
||||
else {
|
||||
this.watcher.event(TargetEvent.ADD_DIR, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
onTargetChange(targetPath) {
|
||||
if (this._isSubRoot(targetPath)) {
|
||||
this.watcher.event(TargetEvent.CHANGE, targetPath);
|
||||
}
|
||||
}
|
||||
onTargetUnlink(targetPath) {
|
||||
this.watcher.watchersClose(path.dirname(targetPath), targetPath, false);
|
||||
if (this._isSubRoot(targetPath)) {
|
||||
if (this.options.renameDetection) {
|
||||
this.watcher._locker.getLockTargetUnlink(targetPath, this.options.renameTimeout);
|
||||
}
|
||||
else {
|
||||
this.watcher.event(TargetEvent.UNLINK, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
onTargetUnlinkDir(targetPath) {
|
||||
this.watcher.watchersClose(path.dirname(targetPath), targetPath, false);
|
||||
this.watcher.watchersClose(targetPath);
|
||||
if (this._isSubRoot(targetPath)) {
|
||||
if (this.options.renameDetection) {
|
||||
this.watcher._locker.getLockTargetUnlinkDir(targetPath, this.options.renameTimeout);
|
||||
}
|
||||
else {
|
||||
this.watcher.event(TargetEvent.UNLINK_DIR, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
onTargetEvent(event) {
|
||||
const [targetEvent, targetPath] = event;
|
||||
if (targetEvent === TargetEvent.ADD) {
|
||||
this.onTargetAdd(targetPath);
|
||||
}
|
||||
else if (targetEvent === TargetEvent.ADD_DIR) {
|
||||
this.onTargetAddDir(targetPath);
|
||||
}
|
||||
else if (targetEvent === TargetEvent.CHANGE) {
|
||||
this.onTargetChange(targetPath);
|
||||
}
|
||||
else if (targetEvent === TargetEvent.UNLINK) {
|
||||
this.onTargetUnlink(targetPath);
|
||||
}
|
||||
else if (targetEvent === TargetEvent.UNLINK_DIR) {
|
||||
this.onTargetUnlinkDir(targetPath);
|
||||
}
|
||||
}
|
||||
onTargetEvents(events) {
|
||||
for (const event of events) {
|
||||
this.onTargetEvent(event);
|
||||
}
|
||||
}
|
||||
onWatcherEvent(event, targetPath, isInitial = false) {
|
||||
return this.handlerBatched(event, targetPath, isInitial);
|
||||
}
|
||||
onWatcherChange(event = FSTargetEvent.CHANGE, targetName) {
|
||||
if (this.watcher.isClosed())
|
||||
return;
|
||||
const targetPath = path.resolve(this.folderPath, targetName || '');
|
||||
if (this.filePath && targetPath !== this.folderPath && targetPath !== this.filePath)
|
||||
return;
|
||||
if (this.watcher.isIgnored(targetPath, this.options.ignore))
|
||||
return;
|
||||
this.onWatcherEvent(event, targetPath);
|
||||
}
|
||||
onWatcherError(error) {
|
||||
if (IS_WINDOWS && error.code === 'EPERM') { // This may happen when a folder is deleted
|
||||
this.onWatcherChange(FSTargetEvent.CHANGE, '');
|
||||
}
|
||||
else {
|
||||
this.watcher.error(error);
|
||||
}
|
||||
}
|
||||
/* API */
|
||||
async init() {
|
||||
await this.initWatcherEvents();
|
||||
await this.initInitialEvents();
|
||||
}
|
||||
async initWatcherEvents() {
|
||||
const onChange = this.onWatcherChange.bind(this);
|
||||
this.fswatcher.on(FSWatcherEvent.CHANGE, onChange);
|
||||
const onError = this.onWatcherError.bind(this);
|
||||
this.fswatcher.on(FSWatcherEvent.ERROR, onError);
|
||||
}
|
||||
async initInitialEvents() {
|
||||
const isInitial = !this.watcher.isReady(); // "isInitial" => is ignorable via the "ignoreInitial" option
|
||||
if (this.filePath) { // Single initial path
|
||||
if (this.watcher._poller.stats.has(this.filePath))
|
||||
return; // Already polled
|
||||
await this.onWatcherEvent(FSTargetEvent.CHANGE, this.filePath, isInitial);
|
||||
}
|
||||
else { // Multiple initial paths
|
||||
const depth = this.options.recursive && (HAS_NATIVE_RECURSION && this.options.native !== false) ? this.options.depth ?? DEPTH : Math.min(1, this.options.depth ?? DEPTH);
|
||||
const limit = this.options.limit ?? LIMIT;
|
||||
const [directories, files] = await Utils.fs.readdir(this.folderPath, this.options.ignore, depth, limit, this.watcher._closeSignal, this.options.readdirMap);
|
||||
const targetPaths = [this.folderPath, ...directories, ...files];
|
||||
await Promise.all(targetPaths.map(targetPath => {
|
||||
if (this.watcher._poller.stats.has(targetPath))
|
||||
return; // Already polled
|
||||
if (this.watcher.isIgnored(targetPath, this.options.ignore))
|
||||
return;
|
||||
return this.onWatcherEvent(FSTargetEvent.CHANGE, targetPath, isInitial);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* EXPORT */
|
||||
export default WatcherHandler;
|
||||
32
node_modules/watcher/dist/watcher_locker.d.ts
generated
vendored
Normal file
32
node_modules/watcher/dist/watcher_locker.d.ts
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { TargetEvent } from './enums';
|
||||
import type Watcher from './watcher';
|
||||
import type { Path, LocksAdd, LocksUnlink, LocksPair, LockConfig } from './types';
|
||||
declare class WatcherLocker {
|
||||
_locksAdd: LocksAdd;
|
||||
_locksAddDir: LocksAdd;
|
||||
_locksUnlink: LocksUnlink;
|
||||
_locksUnlinkDir: LocksUnlink;
|
||||
_locksDir: LocksPair;
|
||||
_locksFile: LocksPair;
|
||||
_watcher: Watcher;
|
||||
static DIR_EVENTS: {
|
||||
readonly add: TargetEvent.ADD_DIR;
|
||||
readonly rename: TargetEvent.RENAME_DIR;
|
||||
readonly unlink: TargetEvent.UNLINK_DIR;
|
||||
};
|
||||
static FILE_EVENTS: {
|
||||
readonly add: TargetEvent.ADD;
|
||||
readonly change: TargetEvent.CHANGE;
|
||||
readonly rename: TargetEvent.RENAME;
|
||||
readonly unlink: TargetEvent.UNLINK;
|
||||
};
|
||||
constructor(watcher: Watcher);
|
||||
getLockAdd(config: LockConfig, timeout?: number): void;
|
||||
getLockUnlink(config: LockConfig, timeout?: number): void;
|
||||
getLockTargetAdd(targetPath: Path, timeout?: number): void;
|
||||
getLockTargetAddDir(targetPath: Path, timeout?: number): void;
|
||||
getLockTargetUnlink(targetPath: Path, timeout?: number): void;
|
||||
getLockTargetUnlinkDir(targetPath: Path, timeout?: number): void;
|
||||
reset(): void;
|
||||
}
|
||||
export default WatcherLocker;
|
||||
139
node_modules/watcher/dist/watcher_locker.js
generated
vendored
Normal file
139
node_modules/watcher/dist/watcher_locker.js
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
/* IMPORT */
|
||||
import { RENAME_TIMEOUT } from './constants.js';
|
||||
import { FileType, TargetEvent } from './enums.js';
|
||||
import Utils from './utils.js';
|
||||
import WatcherLocksResolver from './watcher_locks_resolver.js';
|
||||
/* MAIN */
|
||||
//TODO: Use a better name for this thing, maybe "RenameDetector"
|
||||
class WatcherLocker {
|
||||
/* CONSTRUCTOR */
|
||||
constructor(watcher) {
|
||||
this._watcher = watcher;
|
||||
this.reset();
|
||||
}
|
||||
/* API */
|
||||
getLockAdd(config, timeout = RENAME_TIMEOUT) {
|
||||
const { ino, targetPath, events, locks } = config;
|
||||
const emit = () => {
|
||||
const otherPath = this._watcher._poller.paths.find(ino || -1, path => path !== targetPath); // Maybe this is actually a rename in a case-insensitive filesystem
|
||||
if (otherPath && otherPath !== targetPath) {
|
||||
if (Utils.fs.getRealPath(targetPath, true) === otherPath)
|
||||
return; //TODO: This seems a little too special-casey
|
||||
this._watcher.event(events.rename, otherPath, targetPath);
|
||||
}
|
||||
else {
|
||||
this._watcher.event(events.add, targetPath);
|
||||
}
|
||||
};
|
||||
if (!ino)
|
||||
return emit();
|
||||
const cleanup = () => {
|
||||
locks.add.delete(ino);
|
||||
WatcherLocksResolver.remove(free);
|
||||
};
|
||||
const free = () => {
|
||||
cleanup();
|
||||
emit();
|
||||
};
|
||||
WatcherLocksResolver.add(free, timeout);
|
||||
const resolve = () => {
|
||||
const unlink = locks.unlink.get(ino);
|
||||
if (!unlink)
|
||||
return; // No matching "unlink" lock found, skipping
|
||||
cleanup();
|
||||
const targetPathPrev = unlink();
|
||||
if (targetPath === targetPathPrev) {
|
||||
if (events.change) {
|
||||
if (this._watcher._poller.stats.has(targetPath)) {
|
||||
this._watcher.event(events.change, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._watcher.event(events.rename, targetPathPrev, targetPath);
|
||||
}
|
||||
};
|
||||
locks.add.set(ino, resolve);
|
||||
resolve();
|
||||
}
|
||||
getLockUnlink(config, timeout = RENAME_TIMEOUT) {
|
||||
const { ino, targetPath, events, locks } = config;
|
||||
const emit = () => {
|
||||
this._watcher.event(events.unlink, targetPath);
|
||||
};
|
||||
if (!ino)
|
||||
return emit();
|
||||
const cleanup = () => {
|
||||
locks.unlink.delete(ino);
|
||||
WatcherLocksResolver.remove(free);
|
||||
};
|
||||
const free = () => {
|
||||
cleanup();
|
||||
emit();
|
||||
};
|
||||
WatcherLocksResolver.add(free, timeout);
|
||||
const overridden = () => {
|
||||
cleanup();
|
||||
return targetPath;
|
||||
};
|
||||
locks.unlink.set(ino, overridden);
|
||||
locks.add.get(ino)?.();
|
||||
}
|
||||
getLockTargetAdd(targetPath, timeout) {
|
||||
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.ADD, FileType.FILE);
|
||||
return this.getLockAdd({
|
||||
ino,
|
||||
targetPath,
|
||||
events: WatcherLocker.FILE_EVENTS,
|
||||
locks: this._locksFile
|
||||
}, timeout);
|
||||
}
|
||||
getLockTargetAddDir(targetPath, timeout) {
|
||||
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.ADD_DIR, FileType.DIR);
|
||||
return this.getLockAdd({
|
||||
ino,
|
||||
targetPath,
|
||||
events: WatcherLocker.DIR_EVENTS,
|
||||
locks: this._locksDir
|
||||
}, timeout);
|
||||
}
|
||||
getLockTargetUnlink(targetPath, timeout) {
|
||||
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.UNLINK, FileType.FILE);
|
||||
return this.getLockUnlink({
|
||||
ino,
|
||||
targetPath,
|
||||
events: WatcherLocker.FILE_EVENTS,
|
||||
locks: this._locksFile
|
||||
}, timeout);
|
||||
}
|
||||
getLockTargetUnlinkDir(targetPath, timeout) {
|
||||
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.UNLINK_DIR, FileType.DIR);
|
||||
return this.getLockUnlink({
|
||||
ino,
|
||||
targetPath,
|
||||
events: WatcherLocker.DIR_EVENTS,
|
||||
locks: this._locksDir
|
||||
}, timeout);
|
||||
}
|
||||
reset() {
|
||||
this._locksAdd = new Map();
|
||||
this._locksAddDir = new Map();
|
||||
this._locksUnlink = new Map();
|
||||
this._locksUnlinkDir = new Map();
|
||||
this._locksDir = { add: this._locksAddDir, unlink: this._locksUnlinkDir };
|
||||
this._locksFile = { add: this._locksAdd, unlink: this._locksUnlink };
|
||||
}
|
||||
}
|
||||
WatcherLocker.DIR_EVENTS = {
|
||||
add: TargetEvent.ADD_DIR,
|
||||
rename: TargetEvent.RENAME_DIR,
|
||||
unlink: TargetEvent.UNLINK_DIR
|
||||
};
|
||||
WatcherLocker.FILE_EVENTS = {
|
||||
add: TargetEvent.ADD,
|
||||
change: TargetEvent.CHANGE,
|
||||
rename: TargetEvent.RENAME,
|
||||
unlink: TargetEvent.UNLINK
|
||||
};
|
||||
/* EXPORT */
|
||||
export default WatcherLocker;
|
||||
12
node_modules/watcher/dist/watcher_locks_resolver.d.ts
generated
vendored
Normal file
12
node_modules/watcher/dist/watcher_locks_resolver.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference types="node" />
|
||||
declare const WatcherLocksResolver: {
|
||||
interval: number;
|
||||
intervalId: NodeJS.Timeout | undefined;
|
||||
fns: Map<Function, number>;
|
||||
init: () => void;
|
||||
reset: () => void;
|
||||
add: (fn: Function, timeout: number) => void;
|
||||
remove: (fn: Function) => void;
|
||||
resolve: () => void;
|
||||
};
|
||||
export default WatcherLocksResolver;
|
||||
42
node_modules/watcher/dist/watcher_locks_resolver.js
generated
vendored
Normal file
42
node_modules/watcher/dist/watcher_locks_resolver.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/* MAIN */
|
||||
// Registering a single interval scales much better than registering N timeouts
|
||||
// Timeouts are respected within the interval margin
|
||||
const WatcherLocksResolver = {
|
||||
/* VARIABLES */
|
||||
interval: 100,
|
||||
intervalId: undefined,
|
||||
fns: new Map(),
|
||||
/* LIFECYCLE API */
|
||||
init: () => {
|
||||
if (WatcherLocksResolver.intervalId)
|
||||
return;
|
||||
WatcherLocksResolver.intervalId = setInterval(WatcherLocksResolver.resolve, WatcherLocksResolver.interval);
|
||||
},
|
||||
reset: () => {
|
||||
if (!WatcherLocksResolver.intervalId)
|
||||
return;
|
||||
clearInterval(WatcherLocksResolver.intervalId);
|
||||
delete WatcherLocksResolver.intervalId;
|
||||
},
|
||||
/* API */
|
||||
add: (fn, timeout) => {
|
||||
WatcherLocksResolver.fns.set(fn, Date.now() + timeout);
|
||||
WatcherLocksResolver.init();
|
||||
},
|
||||
remove: (fn) => {
|
||||
WatcherLocksResolver.fns.delete(fn);
|
||||
},
|
||||
resolve: () => {
|
||||
if (!WatcherLocksResolver.fns.size)
|
||||
return WatcherLocksResolver.reset();
|
||||
const now = Date.now();
|
||||
for (const [fn, timestamp] of WatcherLocksResolver.fns) {
|
||||
if (timestamp >= now)
|
||||
continue; // We should still wait some more for this
|
||||
WatcherLocksResolver.remove(fn);
|
||||
fn();
|
||||
}
|
||||
}
|
||||
};
|
||||
/* EXPORT */
|
||||
export default WatcherLocksResolver;
|
||||
17
node_modules/watcher/dist/watcher_poller.d.ts
generated
vendored
Normal file
17
node_modules/watcher/dist/watcher_poller.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { FileType, TargetEvent } from './enums';
|
||||
import LazyMapSet from './lazy_map_set';
|
||||
import WatcherStats from './watcher_stats';
|
||||
import type { INO, Path } from './types';
|
||||
declare class WatcherPoller {
|
||||
inos: Partial<Record<TargetEvent, Record<Path, [INO, FileType]>>>;
|
||||
paths: LazyMapSet<INO, Path>;
|
||||
stats: Map<Path, WatcherStats>;
|
||||
getIno(targetPath: Path, event: TargetEvent, type?: FileType): INO | undefined;
|
||||
getStats(targetPath: Path): WatcherStats | undefined;
|
||||
poll(targetPath: Path, timeout?: number): Promise<WatcherStats | undefined>;
|
||||
reset(): void;
|
||||
update(targetPath: Path, timeout?: number): Promise<TargetEvent[]>;
|
||||
updateIno(targetPath: Path, event: TargetEvent, stats: WatcherStats): void;
|
||||
updateStats(targetPath: Path, stats?: WatcherStats): void;
|
||||
}
|
||||
export default WatcherPoller;
|
||||
116
node_modules/watcher/dist/watcher_poller.js
generated
vendored
Normal file
116
node_modules/watcher/dist/watcher_poller.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/* IMPORT */
|
||||
import { FileType, TargetEvent } from './enums.js';
|
||||
import LazyMapSet from './lazy_map_set.js';
|
||||
import Utils from './utils.js';
|
||||
import WatcherStats from './watcher_stats.js';
|
||||
/* MAIN */
|
||||
class WatcherPoller {
|
||||
constructor() {
|
||||
/* VARIABLES */
|
||||
this.inos = {};
|
||||
this.paths = new LazyMapSet();
|
||||
this.stats = new Map();
|
||||
}
|
||||
/* API */
|
||||
getIno(targetPath, event, type) {
|
||||
const inos = this.inos[event];
|
||||
if (!inos)
|
||||
return;
|
||||
const ino = inos[targetPath];
|
||||
if (!ino)
|
||||
return;
|
||||
if (type && ino[1] !== type)
|
||||
return;
|
||||
return ino[0];
|
||||
}
|
||||
getStats(targetPath) {
|
||||
return this.stats.get(targetPath);
|
||||
}
|
||||
async poll(targetPath, timeout) {
|
||||
const stats = await Utils.fs.poll(targetPath, timeout);
|
||||
if (!stats)
|
||||
return;
|
||||
const isSupported = stats.isFile() || stats.isDirectory();
|
||||
if (!isSupported)
|
||||
return;
|
||||
return new WatcherStats(stats);
|
||||
}
|
||||
reset() {
|
||||
this.inos = {};
|
||||
this.paths = new LazyMapSet();
|
||||
this.stats = new Map();
|
||||
}
|
||||
async update(targetPath, timeout) {
|
||||
const prev = this.getStats(targetPath);
|
||||
const next = await this.poll(targetPath, timeout);
|
||||
this.updateStats(targetPath, next);
|
||||
if (!prev && next) {
|
||||
if (next.isFile()) {
|
||||
this.updateIno(targetPath, TargetEvent.ADD, next);
|
||||
return [TargetEvent.ADD];
|
||||
}
|
||||
if (next.isDirectory()) {
|
||||
this.updateIno(targetPath, TargetEvent.ADD_DIR, next);
|
||||
return [TargetEvent.ADD_DIR];
|
||||
}
|
||||
}
|
||||
else if (prev && !next) {
|
||||
if (prev.isFile()) {
|
||||
this.updateIno(targetPath, TargetEvent.UNLINK, prev);
|
||||
return [TargetEvent.UNLINK];
|
||||
}
|
||||
if (prev.isDirectory()) {
|
||||
this.updateIno(targetPath, TargetEvent.UNLINK_DIR, prev);
|
||||
return [TargetEvent.UNLINK_DIR];
|
||||
}
|
||||
}
|
||||
else if (prev && next) {
|
||||
if (prev.isFile()) {
|
||||
if (next.isFile()) {
|
||||
if (prev.ino === next.ino && !prev.size && !next.size)
|
||||
return []; // Same path, same content and same file, nothing actually changed
|
||||
this.updateIno(targetPath, TargetEvent.CHANGE, next);
|
||||
return [TargetEvent.CHANGE];
|
||||
}
|
||||
if (next.isDirectory()) {
|
||||
this.updateIno(targetPath, TargetEvent.UNLINK, prev);
|
||||
this.updateIno(targetPath, TargetEvent.ADD_DIR, next);
|
||||
return [TargetEvent.UNLINK, TargetEvent.ADD_DIR];
|
||||
}
|
||||
}
|
||||
else if (prev.isDirectory()) {
|
||||
if (next.isFile()) {
|
||||
this.updateIno(targetPath, TargetEvent.UNLINK_DIR, prev);
|
||||
this.updateIno(targetPath, TargetEvent.ADD, next);
|
||||
return [TargetEvent.UNLINK_DIR, TargetEvent.ADD];
|
||||
}
|
||||
if (next.isDirectory()) {
|
||||
if (prev.ino === next.ino)
|
||||
return []; // Same path and same directory, nothing actually changed
|
||||
this.updateIno(targetPath, TargetEvent.UNLINK_DIR, prev);
|
||||
this.updateIno(targetPath, TargetEvent.ADD_DIR, next);
|
||||
return [TargetEvent.UNLINK_DIR, TargetEvent.ADD_DIR];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
updateIno(targetPath, event, stats) {
|
||||
const inos = this.inos[event] = this.inos[event] || (this.inos[event] = {});
|
||||
const type = stats.isFile() ? FileType.FILE : FileType.DIR;
|
||||
inos[targetPath] = [stats.ino, type];
|
||||
}
|
||||
updateStats(targetPath, stats) {
|
||||
if (stats) {
|
||||
this.paths.set(stats.ino, targetPath);
|
||||
this.stats.set(targetPath, stats);
|
||||
}
|
||||
else {
|
||||
const ino = this.stats.get(targetPath)?.ino || -1;
|
||||
this.paths.delete(ino, targetPath);
|
||||
this.stats.delete(targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* EXPORT */
|
||||
export default WatcherPoller;
|
||||
17
node_modules/watcher/dist/watcher_stats.d.ts
generated
vendored
Normal file
17
node_modules/watcher/dist/watcher_stats.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { INO, Stats } from './types';
|
||||
declare class WatcherStats {
|
||||
ino: INO;
|
||||
size: number;
|
||||
atimeMs: number;
|
||||
mtimeMs: number;
|
||||
ctimeMs: number;
|
||||
birthtimeMs: number;
|
||||
_isFile: boolean;
|
||||
_isDirectory: boolean;
|
||||
_isSymbolicLink: boolean;
|
||||
constructor(stats: Stats);
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
||||
export default WatcherStats;
|
||||
29
node_modules/watcher/dist/watcher_stats.js
generated
vendored
Normal file
29
node_modules/watcher/dist/watcher_stats.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/* IMPORT */
|
||||
/* MAIN */
|
||||
// An more memory-efficient representation of the useful subset of stats objects
|
||||
class WatcherStats {
|
||||
/* CONSTRUCTOR */
|
||||
constructor(stats) {
|
||||
this.ino = (stats.ino <= Number.MAX_SAFE_INTEGER) ? Number(stats.ino) : stats.ino;
|
||||
this.size = Number(stats.size);
|
||||
this.atimeMs = Number(stats.atimeMs);
|
||||
this.mtimeMs = Number(stats.mtimeMs);
|
||||
this.ctimeMs = Number(stats.ctimeMs);
|
||||
this.birthtimeMs = Number(stats.birthtimeMs);
|
||||
this._isFile = stats.isFile();
|
||||
this._isDirectory = stats.isDirectory();
|
||||
this._isSymbolicLink = stats.isSymbolicLink();
|
||||
}
|
||||
/* API */
|
||||
isFile() {
|
||||
return this._isFile;
|
||||
}
|
||||
isDirectory() {
|
||||
return this._isDirectory;
|
||||
}
|
||||
isSymbolicLink() {
|
||||
return this._isSymbolicLink;
|
||||
}
|
||||
}
|
||||
/* EXPORT */
|
||||
export default WatcherStats;
|
||||
Reference in New Issue
Block a user