diff --git a/src/handler.ts b/src/handler.ts index feb19975..c424b7df 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -3,7 +3,7 @@ import type { WatchListener, WatchEventType, Stats, FSWatcher as NativeFsWatcher import { open, stat, lstat, realpath as fsrealpath } from 'fs/promises'; import * as sysPath from 'path'; import { type as osType } from 'os'; -import type { FSWatcher, WatchHelper, FSWInstanceOptions } from './index.js'; +import type { FSWatcher, WatchHelper, FSWInstanceOptions, Throttler } from './index.js'; import type { EntryInfo } from 'readdirp'; export type Path = string; @@ -12,7 +12,7 @@ export const STR_DATA = 'data'; export const STR_END = 'end'; export const STR_CLOSE = 'close'; export const EMPTY_FN = () => {}; -export const IDENTITY_FN = (val: any) => val; +export const IDENTITY_FN = (val: unknown) => val; const pl = process.platform; export const isWindows = pl === 'win32'; @@ -82,7 +82,7 @@ const isBinaryPath = (filePath: string) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase()); // TODO: emit errors properly. Example: EMFILE on Macos. -const foreach = (val: any, fn: any) => { +const foreach = (val: V, fn: (arg: V) => unknown) => { if (val instanceof Set) { val.forEach(fn); } else { @@ -90,15 +90,15 @@ const foreach = (val: any, fn: any) => { } }; -const addAndConvert = (main: any, prop: any, item: any) => { - let container = main[prop]; +const addAndConvert = (main: Record, prop: string, item: unknown) => { + let container = (main as Record)[prop]; if (!(container instanceof Set)) { - main[prop] = container = new Set([container]); + (main as Record)[prop] = container = new Set([container]); } - container.add(item); + (container as Set).add(item); }; -const clearItem = (cont: any) => (key: string) => { +const clearItem = (cont: Record) => (key: string) => { const set = cont[key]; if (set instanceof Set) { set.clear(); @@ -107,16 +107,16 @@ const clearItem = (cont: any) => (key: string) => { } }; -const delFromSet = (main: any, prop: any, item: any) => { - const container = main[prop]; +const delFromSet = (main: Record | Set, prop: string, item: unknown) => { + const container = (main as Record)[prop]; if (container instanceof Set) { container.delete(item); } else if (container === item) { - delete main[prop]; + delete (main as Record)[prop]; } }; -const isEmptySet = (val: any) => (val instanceof Set ? val.size === 0 : !val); +const isEmptySet = (val: unknown) => (val instanceof Set ? val.size === 0 : !val); // fs_watch helpers @@ -180,9 +180,9 @@ function createFsWatchInstance( const fsWatchBroadcast = ( fullPath: Path, listenerType: string, - val1?: any, - val2?: any, - val3?: any + val1?: unknown, + val2?: unknown, + val3?: unknown ) => { const cont = FsWatchInstances.get(fullPath); if (!cont) return; @@ -283,7 +283,7 @@ const setFsWatchListener = ( // object to hold per-process fs_watchFile instances // (may be shared across chokidar FSWatcher instances) -const FsWatchFileInstances = new Map(); +const FsWatchFileInstances = new Map(); /** * Instantiates the fs_watchFile interface or binds listeners @@ -297,8 +297,8 @@ const FsWatchFileInstances = new Map(); const setFsWatchFileListener = ( path: Path, fullPath: Path, - options: any, - handlers: any + options: Partial, + handlers: Pick ): (() => void) => { const { listener, rawEmitter } = handlers; let cont = FsWatchFileInstances.get(fullPath); @@ -307,7 +307,7 @@ const setFsWatchFileListener = ( // let rawEmitters = new Set(); const copts = cont && cont.options; - if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) { + if (copts && (copts.persistent < options.persistent! || copts.interval > options.interval!)) { // "Upgrade" the watcher to persistence or a quicker interval. // This creates some unlikely edge case issues if the user mixes // settings in a very weird way, but solving for those cases @@ -330,12 +330,12 @@ const setFsWatchFileListener = ( rawEmitters: rawEmitter, options, watcher: watchFile(fullPath, options, (curr, prev) => { - foreach(cont.rawEmitters, (rawEmitter: any) => { + foreach(cont.rawEmitters, (rawEmitter) => { rawEmitter(EV.CHANGE, fullPath, { curr, prev }); }); const currmtime = curr.mtimeMs; if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) { - foreach(cont.listeners, (listener: any) => listener(path, curr)); + foreach(cont.listeners, (listener) => listener(path, curr)); } }), }; @@ -362,10 +362,10 @@ const setFsWatchFileListener = ( */ export class NodeFsHandler { fsw: FSWatcher; - _boundHandleError: any; + _boundHandleError: (error: unknown) => void; constructor(fsW: FSWatcher) { this.fsw = fsW; - this._boundHandleError = (error: Error) => fsW._handleError(error); + this._boundHandleError = (error) => fsW._handleError(error as Error); } /** @@ -530,12 +530,12 @@ export class NodeFsHandler { target: Path, dir: Path, depth: number, - throttler: any + throttler: Throttler ) { // Normalize the directory name on Windows directory = sysPath.join(directory, ''); - throttler = this.fsw._throttle('readdir', directory, 1000); + throttler = this.fsw._throttle('readdir', directory, 1000) as Throttler; if (!throttler) return; const previous = this.fsw._getWatchedDir(wh.path); @@ -641,7 +641,7 @@ export class NodeFsHandler { // ensure dir is tracked (harmless if redundant) parentDir.add(sysPath.basename(dir)); this.fsw._getWatchedDir(dir); - let throttler: any; + let throttler!: Throttler; let closer; const oDepth = this.fsw.options.depth; diff --git a/src/index.ts b/src/index.ts index 921e0195..070c3ab4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,12 @@ type BasicOpts = { // ioLimit?: number; // Limit parallel IO operations (CPU usage + OS limits) }; +export type Throttler = { + timeoutObject: NodeJS.Timeout; + clear: () => void; + count: number; +}; + export type ChokidarOptions = Partial< BasicOpts & { ignored: Matcher | Matcher[]; @@ -142,20 +148,8 @@ function anymatch(matchers: Matcher[], testString: string | undefined): boolean return matchPatterns(patterns, testString); } -const flatten = (list: any[], result = []) => { - list.forEach((item) => { - if (Array.isArray(item)) { - flatten(item, result); - } else { - // @ts-ignore - result.push(item); - } - }); - return result; -}; - const unifyPaths = (paths_: Path | Path[]) => { - const paths = flatten(arrify(paths_)); + const paths = arrify(paths_).flat(); if (!paths.every((p) => typeof p === STRING_TYPE)) { throw new TypeError(`Non-string provided as watch path: ${paths}`); } @@ -186,11 +180,11 @@ const normalizePathToUnix = (path: Path) => toUnix(sysPath.normalize(toUnix(path // TODO: refactor const normalizeIgnored = (cwd = '') => - (path: any): any => { + (path: unknown): string => { if (typeof path === 'string') { return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path)); } else { - return path; + return path as string; } }; @@ -207,10 +201,10 @@ const EMPTY_SET = Object.freeze(new Set()); */ class DirEntry { path: Path; - _removeWatcher: any; + _removeWatcher: (dir: string, base: string) => void; items: Set; - constructor(dir: Path, removeWatcher: any) { + constructor(dir: Path, removeWatcher: (dir: string, base: string) => void) { this.path = dir; this._removeWatcher = removeWatcher; this.items = new Set(); @@ -270,7 +264,7 @@ export class WatchHelper { followSymlinks: boolean; statMethod: 'stat' | 'lstat'; - constructor(path: string, follow: boolean, fsw: any) { + constructor(path: string, follow: boolean, fsw: FSWatcher) { this.fsw = fsw; const watchPath = path; this.path = path = path.replace(REPLACER_RE, ''); @@ -316,12 +310,12 @@ export class FSWatcher extends EventEmitter { _closers: Map>; _ignoredPaths: Set; _throttled: Map>; - _streams: Set; + _streams: Set; _symlinkPaths: Map; _watched: Map; - _pendingWrites: Map; - _pendingUnlinks: Map; + _pendingWrites: Map; + _pendingUnlinks: Map; _readyCount: number; _emitReady: () => void; _closePromise?: Promise; @@ -626,7 +620,7 @@ export class FSWatcher extends EventEmitter { } if (awf && (event === EV.ADD || event === EV.CHANGE) && this._readyEmitted) { - const awfEmit = (err: Error, stats: Stats) => { + const awfEmit = (err?: Error, stats?: Stats) => { if (err) { event = args[0] = EV.ERROR; args[1] = err; @@ -696,17 +690,7 @@ export class FSWatcher extends EventEmitter { * @param timeout duration of time to suppress duplicate actions * @returns tracking object or false if action should be suppressed */ - _throttle( - actionType: ThrottleType, - path: Path, - timeout: number - ): - | { - timeoutObject: any; - clear: () => any; - count: number; - } - | false { + _throttle(actionType: ThrottleType, path: Path, timeout: number): Throttler | false { if (!this._throttled.has(actionType)) { this._throttled.set(actionType, new Map()); } @@ -721,7 +705,7 @@ export class FSWatcher extends EventEmitter { } // eslint-disable-next-line prefer-const - let timeoutObject: any; + let timeoutObject: NodeJS.Timeout; const clear = () => { const item = action.get(path); const count = item ? item.count : 0; @@ -748,11 +732,16 @@ export class FSWatcher extends EventEmitter { * @param event * @param awfEmit Callback to be called when ready for event to be emitted. */ - _awaitWriteFinish(path: Path, threshold: number, event: EventName, awfEmit: any) { + _awaitWriteFinish( + path: Path, + threshold: number, + event: EventName, + awfEmit: (err?: Error, stat?: Stats) => void + ) { const awf = this.options.awaitWriteFinish; if (typeof awf !== 'object') return; const pollInterval = awf.pollInterval as unknown as number; - let timeoutHandler: any; + let timeoutHandler: NodeJS.Timeout; let fullPath = path; if (this.options.cwd && !sysPath.isAbsolute(path)) {