basant307/AI_Governance_Project
048
1"use strict";2Object.defineProperty(exports, "__esModule", { value: true });3exports.FSWatcher = exports.WatchHelper = void 0;4exports.watch = watch;5/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */6const fs_1 = require("fs");7const promises_1 = require("fs/promises");8const events_1 = require("events");9const sysPath = require("path");10const readdirp_1 = require("readdirp");11const handler_js_1 = require("./handler.js");12const SLASH = '/';13const SLASH_SLASH = '//';14const ONE_DOT = '.';15const TWO_DOTS = '..';16const STRING_TYPE = 'string';17const BACK_SLASH_RE = /\\/g;18const DOUBLE_SLASH_RE = /\/\//;19const DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;20const REPLACER_RE = /^\.[/\\]/;21function arrify(item) {22 return Array.isArray(item) ? item : [item];23}24const isMatcherObject = (matcher) => typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);25function createPattern(matcher) {26 if (typeof matcher === 'function')27 return matcher;28 if (typeof matcher === 'string')29 return (string) => matcher === string;30 if (matcher instanceof RegExp)31 return (string) => matcher.test(string);32 if (typeof matcher === 'object' && matcher !== null) {33 return (string) => {34 if (matcher.path === string)35 return true;36 if (matcher.recursive) {37 const relative = sysPath.relative(matcher.path, string);38 if (!relative) {39 return false;40 }41 return !relative.startsWith('..') && !sysPath.isAbsolute(relative);42 }43 return false;44 };45 }46 return () => false;47}48function normalizePath(path) {49 if (typeof path !== 'string')50 throw new Error('string expected');51 path = sysPath.normalize(path);52 path = path.replace(/\\/g, '/');53 let prepend = false;54 if (path.startsWith('//'))55 prepend = true;56 const DOUBLE_SLASH_RE = /\/\//;57 while (path.match(DOUBLE_SLASH_RE))58 path = path.replace(DOUBLE_SLASH_RE, '/');59 if (prepend)60 path = '/' + path;61 return path;62}63function matchPatterns(patterns, testString, stats) {64 const path = normalizePath(testString);65 for (let index = 0; index < patterns.length; index++) {66 const pattern = patterns[index];67 if (pattern(path, stats)) {68 return true;69 }70 }71 return false;72}73function anymatch(matchers, testString) {74 if (matchers == null) {75 throw new TypeError('anymatch: specify first argument');76 }77 // Early cache for matchers.78 const matchersArray = arrify(matchers);79 const patterns = matchersArray.map((matcher) => createPattern(matcher));80 if (testString == null) {81 return (testString, stats) => {82 return matchPatterns(patterns, testString, stats);83 };84 }85 return matchPatterns(patterns, testString);86}87const unifyPaths = (paths_) => {88 const paths = arrify(paths_).flat();89 if (!paths.every((p) => typeof p === STRING_TYPE)) {90 throw new TypeError(`Non-string provided as watch path: ${paths}`);91 }92 return paths.map(normalizePathToUnix);93};94// If SLASH_SLASH occurs at the beginning of path, it is not replaced95// because "//StoragePC/DrivePool/Movies" is a valid network path96const toUnix = (string) => {97 let str = string.replace(BACK_SLASH_RE, SLASH);98 let prepend = false;99 if (str.startsWith(SLASH_SLASH)) {100 prepend = true;101 }102 while (str.match(DOUBLE_SLASH_RE)) {103 str = str.replace(DOUBLE_SLASH_RE, SLASH);104 }105 if (prepend) {106 str = SLASH + str;107 }108 return str;109};110// Our version of upath.normalize111// TODO: this is not equal to path-normalize module - investigate why112const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));113// TODO: refactor114const normalizeIgnored = (cwd = '') => (path) => {115 if (typeof path === 'string') {116 return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));117 }118 else {119 return path;120 }121};122const getAbsolutePath = (path, cwd) => {123 if (sysPath.isAbsolute(path)) {124 return path;125 }126 return sysPath.join(cwd, path);127};128const EMPTY_SET = Object.freeze(new Set());129/**130 * Directory entry.131 */132class DirEntry {133 constructor(dir, removeWatcher) {134 this.path = dir;135 this._removeWatcher = removeWatcher;136 this.items = new Set();137 }138 add(item) {139 const { items } = this;140 if (!items)141 return;142 if (item !== ONE_DOT && item !== TWO_DOTS)143 items.add(item);144 }145 async remove(item) {146 const { items } = this;147 if (!items)148 return;149 items.delete(item);150 if (items.size > 0)151 return;152 const dir = this.path;153 try {154 await (0, promises_1.readdir)(dir);155 }156 catch (err) {157 if (this._removeWatcher) {158 this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));159 }160 }161 }162 has(item) {163 const { items } = this;164 if (!items)165 return;166 return items.has(item);167 }168 getChildren() {169 const { items } = this;170 if (!items)171 return [];172 return [...items.values()];173 }174 dispose() {175 this.items.clear();176 this.path = '';177 this._removeWatcher = handler_js_1.EMPTY_FN;178 this.items = EMPTY_SET;179 Object.freeze(this);180 }181}182const STAT_METHOD_F = 'stat';183const STAT_METHOD_L = 'lstat';184class WatchHelper {185 constructor(path, follow, fsw) {186 this.fsw = fsw;187 const watchPath = path;188 this.path = path = path.replace(REPLACER_RE, '');189 this.watchPath = watchPath;190 this.fullWatchPath = sysPath.resolve(watchPath);191 this.dirParts = [];192 this.dirParts.forEach((parts) => {193 if (parts.length > 1)194 parts.pop();195 });196 this.followSymlinks = follow;197 this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;198 }199 entryPath(entry) {200 return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));201 }202 filterPath(entry) {203 const { stats } = entry;204 if (stats && stats.isSymbolicLink())205 return this.filterDir(entry);206 const resolvedPath = this.entryPath(entry);207 // TODO: what if stats is undefined? remove !208 return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);209 }210 filterDir(entry) {211 return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);212 }213}214exports.WatchHelper = WatchHelper;215/**216 * Watches files & directories for changes. Emitted events:217 * `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`218 *219 * new FSWatcher()220 * .add(directories)221 * .on('add', path => log('File', path, 'was added'))222 */223class FSWatcher extends events_1.EventEmitter {224 // Not indenting methods for history sake; for now.225 constructor(_opts = {}) {226 super();227 this.closed = false;228 this._closers = new Map();229 this._ignoredPaths = new Set();230 this._throttled = new Map();231 this._streams = new Set();232 this._symlinkPaths = new Map();233 this._watched = new Map();234 this._pendingWrites = new Map();235 this._pendingUnlinks = new Map();236 this._readyCount = 0;237 this._readyEmitted = false;238 const awf = _opts.awaitWriteFinish;239 const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };240 const opts = {241 // Defaults242 persistent: true,243 ignoreInitial: false,244 ignorePermissionErrors: false,245 interval: 100,246 binaryInterval: 300,247 followSymlinks: true,248 usePolling: false,249 // useAsync: false,250 atomic: true, // NOTE: overwritten later (depends on usePolling)251 ..._opts,252 // Change format253 ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),254 awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === 'object' ? { ...DEF_AWF, ...awf } : false,255 };256 // Always default to polling on IBM i because fs.watch() is not available on IBM i.257 if (handler_js_1.isIBMi)258 opts.usePolling = true;259 // Editor atomic write normalization enabled by default with fs.watch260 if (opts.atomic === undefined)261 opts.atomic = !opts.usePolling;262 // opts.atomic = typeof _opts.atomic === 'number' ? _opts.atomic : 100;263 // Global override. Useful for developers, who need to force polling for all264 // instances of chokidar, regardless of usage / dependency depth265 const envPoll = process.env.CHOKIDAR_USEPOLLING;266 if (envPoll !== undefined) {267 const envLower = envPoll.toLowerCase();268 if (envLower === 'false' || envLower === '0')269 opts.usePolling = false;270 else if (envLower === 'true' || envLower === '1')271 opts.usePolling = true;272 else273 opts.usePolling = !!envLower;274 }275 const envInterval = process.env.CHOKIDAR_INTERVAL;276 if (envInterval)277 opts.interval = Number.parseInt(envInterval, 10);278 // This is done to emit ready only once, but each 'add' will increase that?279 let readyCalls = 0;280 this._emitReady = () => {281 readyCalls++;282 if (readyCalls >= this._readyCount) {283 this._emitReady = handler_js_1.EMPTY_FN;284 this._readyEmitted = true;285 // use process.nextTick to allow time for listener to be bound286 process.nextTick(() => this.emit(handler_js_1.EVENTS.READY));287 }288 };289 this._emitRaw = (...args) => this.emit(handler_js_1.EVENTS.RAW, ...args);290 this._boundRemove = this._remove.bind(this);291 this.options = opts;292 this._nodeFsHandler = new handler_js_1.NodeFsHandler(this);293 // You’re frozen when your heart’s not open.294 Object.freeze(opts);295 }296 _addIgnoredPath(matcher) {297 if (isMatcherObject(matcher)) {298 // return early if we already have a deeply equal matcher object299 for (const ignored of this._ignoredPaths) {300 if (isMatcherObject(ignored) &&301 ignored.path === matcher.path &&302 ignored.recursive === matcher.recursive) {303 return;304 }305 }306 }307 this._ignoredPaths.add(matcher);308 }309 _removeIgnoredPath(matcher) {310 this._ignoredPaths.delete(matcher);311 // now find any matcher objects with the matcher as path312 if (typeof matcher === 'string') {313 for (const ignored of this._ignoredPaths) {314 // TODO (43081j): make this more efficient.315 // probably just make a `this._ignoredDirectories` or some316 // such thing.317 if (isMatcherObject(ignored) && ignored.path === matcher) {318 this._ignoredPaths.delete(ignored);319 }320 }321 }322 }323 // Public methods324 /**325 * Adds paths to be watched on an existing FSWatcher instance.326 * @param paths_ file or file list. Other arguments are unused327 */328 add(paths_, _origAdd, _internal) {329 const { cwd } = this.options;330 this.closed = false;331 this._closePromise = undefined;332 let paths = unifyPaths(paths_);333 if (cwd) {334 paths = paths.map((path) => {335 const absPath = getAbsolutePath(path, cwd);336 // Check `path` instead of `absPath` because the cwd portion can't be a glob337 return absPath;338 });339 }340 paths.forEach((path) => {341 this._removeIgnoredPath(path);342 });343 this._userIgnored = undefined;344 if (!this._readyCount)345 this._readyCount = 0;346 this._readyCount += paths.length;347 Promise.all(paths.map(async (path) => {348 const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, undefined, 0, _origAdd);349 if (res)350 this._emitReady();351 return res;352 })).then((results) => {353 if (this.closed)354 return;355 results.forEach((item) => {356 if (item)357 this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));358 });359 });360 return this;361 }362 /**363 * Close watchers or start ignoring events from specified paths.364 */365 unwatch(paths_) {366 if (this.closed)367 return this;368 const paths = unifyPaths(paths_);369 const { cwd } = this.options;370 paths.forEach((path) => {371 // convert to absolute path unless relative path already matches372 if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {373 if (cwd)374 path = sysPath.join(cwd, path);375 path = sysPath.resolve(path);376 }377 this._closePath(path);378 this._addIgnoredPath(path);379 if (this._watched.has(path)) {380 this._addIgnoredPath({381 path,382 recursive: true,383 });384 }385 // reset the cached userIgnored anymatch fn386 // to make ignoredPaths changes effective387 this._userIgnored = undefined;388 });389 return this;390 }391 /**392 * Close watchers and remove all listeners from watched paths.393 */394 close() {395 if (this._closePromise) {396 return this._closePromise;397 }398 this.closed = true;399 // Memory management.400 this.removeAllListeners();401 const closers = [];402 this._closers.forEach((closerList) => closerList.forEach((closer) => {403 const promise = closer();404 if (promise instanceof Promise)405 closers.push(promise);406 }));407 this._streams.forEach((stream) => stream.destroy());408 this._userIgnored = undefined;409 this._readyCount = 0;410 this._readyEmitted = false;411 this._watched.forEach((dirent) => dirent.dispose());412 this._closers.clear();413 this._watched.clear();414 this._streams.clear();415 this._symlinkPaths.clear();416 this._throttled.clear();417 this._closePromise = closers.length418 ? Promise.all(closers).then(() => undefined)419 : Promise.resolve();420 return this._closePromise;421 }422 /**423 * Expose list of watched paths424 * @returns for chaining425 */426 getWatched() {427 const watchList = {};428 this._watched.forEach((entry, dir) => {429 const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;430 const index = key || ONE_DOT;431 watchList[index] = entry.getChildren().sort();432 });433 return watchList;434 }435 emitWithAll(event, args) {436 this.emit(event, ...args);437 if (event !== handler_js_1.EVENTS.ERROR)438 this.emit(handler_js_1.EVENTS.ALL, event, ...args);439 }440 // Common helpers441 // --------------442 /**443 * Normalize and emit events.444 * Calling _emit DOES NOT MEAN emit() would be called!445 * @param event Type of event446 * @param path File or directory path447 * @param stats arguments to be passed with event448 * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag449 */450 async _emit(event, path, stats) {451 if (this.closed)452 return;453 const opts = this.options;454 if (handler_js_1.isWindows)455 path = sysPath.normalize(path);456 if (opts.cwd)457 path = sysPath.relative(opts.cwd, path);458 const args = [path];459 if (stats != null)460 args.push(stats);461 const awf = opts.awaitWriteFinish;462 let pw;463 if (awf && (pw = this._pendingWrites.get(path))) {464 pw.lastChange = new Date();465 return this;466 }467 if (opts.atomic) {468 if (event === handler_js_1.EVENTS.UNLINK) {469 this._pendingUnlinks.set(path, [event, ...args]);470 setTimeout(() => {471 this._pendingUnlinks.forEach((entry, path) => {472 this.emit(...entry);473 this.emit(handler_js_1.EVENTS.ALL, ...entry);474 this._pendingUnlinks.delete(path);475 });476 }, typeof opts.atomic === 'number' ? opts.atomic : 100);477 return this;478 }479 if (event === handler_js_1.EVENTS.ADD && this._pendingUnlinks.has(path)) {480 event = handler_js_1.EVENTS.CHANGE;481 this._pendingUnlinks.delete(path);482 }483 }484 if (awf && (event === handler_js_1.EVENTS.ADD || event === handler_js_1.EVENTS.CHANGE) && this._readyEmitted) {485 const awfEmit = (err, stats) => {486 if (err) {487 event = handler_js_1.EVENTS.ERROR;488 args[0] = err;489 this.emitWithAll(event, args);490 }491 else if (stats) {492 // if stats doesn't exist the file must have been deleted493 if (args.length > 1) {494 args[1] = stats;495 }496 else {497 args.push(stats);498 }499 this.emitWithAll(event, args);500 }501 };502 this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);503 return this;504 }505 if (event === handler_js_1.EVENTS.CHANGE) {506 const isThrottled = !this._throttle(handler_js_1.EVENTS.CHANGE, path, 50);507 if (isThrottled)508 return this;509 }510 if (opts.alwaysStat &&511 stats === undefined &&512 (event === handler_js_1.EVENTS.ADD || event === handler_js_1.EVENTS.ADD_DIR || event === handler_js_1.EVENTS.CHANGE)) {513 const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;514 let stats;515 try {516 stats = await (0, promises_1.stat)(fullPath);517 }518 catch (err) {519 // do nothing520 }521 // Suppress event when fs_stat fails, to avoid sending undefined 'stat'522 if (!stats || this.closed)523 return;524 args.push(stats);525 }526 this.emitWithAll(event, args);527 return this;528 }529 /**530 * Common handler for errors531 * @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag532 */533 _handleError(error) {534 const code = error && error.code;535 if (error &&536 code !== 'ENOENT' &&537 code !== 'ENOTDIR' &&538 (!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))) {539 this.emit(handler_js_1.EVENTS.ERROR, error);540 }541 return error || this.closed;542 }543 /**544 * Helper utility for throttling545 * @param actionType type being throttled546 * @param path being acted upon547 * @param timeout duration of time to suppress duplicate actions548 * @returns tracking object or false if action should be suppressed549 */550 _throttle(actionType, path, timeout) {551 if (!this._throttled.has(actionType)) {552 this._throttled.set(actionType, new Map());553 }554 const action = this._throttled.get(actionType);555 if (!action)556 throw new Error('invalid throttle');557 const actionPath = action.get(path);558 if (actionPath) {559 actionPath.count++;560 return false;561 }562 // eslint-disable-next-line prefer-const563 let timeoutObject;564 const clear = () => {565 const item = action.get(path);566 const count = item ? item.count : 0;567 action.delete(path);568 clearTimeout(timeoutObject);569 if (item)570 clearTimeout(item.timeoutObject);571 return count;572 };573 timeoutObject = setTimeout(clear, timeout);574 const thr = { timeoutObject, clear, count: 0 };575 action.set(path, thr);576 return thr;577 }578 _incrReadyCount() {579 return this._readyCount++;580 }581 /**582 * Awaits write operation to finish.583 * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.584 * @param path being acted upon585 * @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished586 * @param event587 * @param awfEmit Callback to be called when ready for event to be emitted.588 */589 _awaitWriteFinish(path, threshold, event, awfEmit) {590 const awf = this.options.awaitWriteFinish;591 if (typeof awf !== 'object')592 return;593 const pollInterval = awf.pollInterval;594 let timeoutHandler;595 let fullPath = path;596 if (this.options.cwd && !sysPath.isAbsolute(path)) {597 fullPath = sysPath.join(this.options.cwd, path);598 }599 const now = new Date();600 const writes = this._pendingWrites;601 function awaitWriteFinishFn(prevStat) {602 (0, fs_1.stat)(fullPath, (err, curStat) => {603 if (err || !writes.has(path)) {604 if (err && err.code !== 'ENOENT')605 awfEmit(err);606 return;607 }608 const now = Number(new Date());609 if (prevStat && curStat.size !== prevStat.size) {610 writes.get(path).lastChange = now;611 }612 const pw = writes.get(path);613 const df = now - pw.lastChange;614 if (df >= threshold) {615 writes.delete(path);616 awfEmit(undefined, curStat);617 }618 else {619 timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);620 }621 });622 }623 if (!writes.has(path)) {624 writes.set(path, {625 lastChange: now,626 cancelWait: () => {627 writes.delete(path);628 clearTimeout(timeoutHandler);629 return event;630 },631 });632 timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);633 }634 }635 /**636 * Determines whether user has asked to ignore this path.637 */638 _isIgnored(path, stats) {639 if (this.options.atomic && DOT_RE.test(path))640 return true;641 if (!this._userIgnored) {642 const { cwd } = this.options;643 const ign = this.options.ignored;644 const ignored = (ign || []).map(normalizeIgnored(cwd));645 const ignoredPaths = [...this._ignoredPaths];646 const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];647 this._userIgnored = anymatch(list, undefined);648 }649 return this._userIgnored(path, stats);650 }651 _isntIgnored(path, stat) {652 return !this._isIgnored(path, stat);653 }654 /**655 * Provides a set of common helpers and properties relating to symlink handling.656 * @param path file or directory pattern being watched657 */658 _getWatchHelpers(path) {659 return new WatchHelper(path, this.options.followSymlinks, this);660 }661 // Directory helpers662 // -----------------663 /**664 * Provides directory tracking objects665 * @param directory path of the directory666 */667 _getWatchedDir(directory) {668 const dir = sysPath.resolve(directory);669 if (!this._watched.has(dir))670 this._watched.set(dir, new DirEntry(dir, this._boundRemove));671 return this._watched.get(dir);672 }673 // File helpers674 // ------------675 /**676 * Check for read permissions: https://stackoverflow.com/a/11781404/1358405677 */678 _hasReadPermissions(stats) {679 if (this.options.ignorePermissionErrors)680 return true;681 return Boolean(Number(stats.mode) & 0o400);682 }683 /**684 * Handles emitting unlink events for685 * files and directories, and via recursion, for686 * files and directories within directories that are unlinked687 * @param directory within which the following item is located688 * @param item base path of item/directory689 */690 _remove(directory, item, isDirectory) {691 // if what is being deleted is a directory, get that directory's paths692 // for recursive deleting and cleaning of watched object693 // if it is not a directory, nestedDirectoryChildren will be empty array694 const path = sysPath.join(directory, item);695 const fullPath = sysPath.resolve(path);696 isDirectory =697 isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);698 // prevent duplicate handling in case of arriving here nearly simultaneously699 // via multiple paths (such as _handleFile and _handleDir)700 if (!this._throttle('remove', path, 100))701 return;702 // if the only watched file is removed, watch for its return703 if (!isDirectory && this._watched.size === 1) {704 this.add(directory, item, true);705 }706 // This will create a new entry in the watched object in either case707 // so we got to do the directory check beforehand708 const wp = this._getWatchedDir(path);709 const nestedDirectoryChildren = wp.getChildren();710 // Recursively remove children directories / files.711 nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));712 // Check if item was on the watched list and remove it713 const parent = this._getWatchedDir(directory);714 const wasTracked = parent.has(item);715 parent.remove(item);716 // Fixes issue #1042 -> Relative paths were detected and added as symlinks717 // (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),718 // but never removed from the map in case the path was deleted.719 // This leads to an incorrect state if the path was recreated:720 // https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553721 if (this._symlinkPaths.has(fullPath)) {722 this._symlinkPaths.delete(fullPath);723 }724 // If we wait for this file to be fully written, cancel the wait.725 let relPath = path;726 if (this.options.cwd)727 relPath = sysPath.relative(this.options.cwd, path);728 if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {729 const event = this._pendingWrites.get(relPath).cancelWait();730 if (event === handler_js_1.EVENTS.ADD)731 return;732 }733 // The Entry will either be a directory that just got removed734 // or a bogus entry to a file, in either case we have to remove it735 this._watched.delete(path);736 this._watched.delete(fullPath);737 const eventName = isDirectory ? handler_js_1.EVENTS.UNLINK_DIR : handler_js_1.EVENTS.UNLINK;738 if (wasTracked && !this._isIgnored(path))739 this._emit(eventName, path);740 // Avoid conflicts if we later create another file with the same name741 this._closePath(path);742 }743 /**744 * Closes all watchers for a path745 */746 _closePath(path) {747 this._closeFile(path);748 const dir = sysPath.dirname(path);749 this._getWatchedDir(dir).remove(sysPath.basename(path));750 }751 /**752 * Closes only file-specific watchers753 */754 _closeFile(path) {755 const closers = this._closers.get(path);756 if (!closers)757 return;758 closers.forEach((closer) => closer());759 this._closers.delete(path);760 }761 _addPathCloser(path, closer) {762 if (!closer)763 return;764 let list = this._closers.get(path);765 if (!list) {766 list = [];767 this._closers.set(path, list);768 }769 list.push(closer);770 }771 _readdirp(root, opts) {772 if (this.closed)773 return;774 const options = { type: handler_js_1.EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };775 let stream = (0, readdirp_1.readdirp)(root, options);776 this._streams.add(stream);777 stream.once(handler_js_1.STR_CLOSE, () => {778 stream = undefined;779 });780 stream.once(handler_js_1.STR_END, () => {781 if (stream) {782 this._streams.delete(stream);783 stream = undefined;784 }785 });786 return stream;787 }788}789exports.FSWatcher = FSWatcher;790/**791 * Instantiates watcher with paths to be tracked.792 * @param paths file / directory paths793 * @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others794 * @returns an instance of FSWatcher for chaining.795 * @example796 * const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });797 * watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })798 */799function watch(paths, options = {}) {800 const watcher = new FSWatcher(options);801 watcher.add(paths);802 return watcher;803}804exports.default = { watch, FSWatcher };805 