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