forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			525 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			525 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const fs = require('fs');
							 | 
						||
| 
								 | 
							
								const sysPath = require('path');
							 | 
						||
| 
								 | 
							
								const { promisify } = require('util');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let fsevents;
							 | 
						||
| 
								 | 
							
								try {
							 | 
						||
| 
								 | 
							
								  fsevents = require('fsevents');
							 | 
						||
| 
								 | 
							
								} catch (error) {
							 | 
						||
| 
								 | 
							
								  if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (fsevents) {
							 | 
						||
| 
								 | 
							
								  // TODO: real check
							 | 
						||
| 
								 | 
							
								  const mtch = process.version.match(/v(\d+)\.(\d+)/);
							 | 
						||
| 
								 | 
							
								  if (mtch && mtch[1] && mtch[2]) {
							 | 
						||
| 
								 | 
							
								    const maj = Number.parseInt(mtch[1], 10);
							 | 
						||
| 
								 | 
							
								    const min = Number.parseInt(mtch[2], 10);
							 | 
						||
| 
								 | 
							
								    if (maj === 8 && min < 16) {
							 | 
						||
| 
								 | 
							
								      fsevents = undefined;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const {
							 | 
						||
| 
								 | 
							
								  EV_ADD,
							 | 
						||
| 
								 | 
							
								  EV_CHANGE,
							 | 
						||
| 
								 | 
							
								  EV_ADD_DIR,
							 | 
						||
| 
								 | 
							
								  EV_UNLINK,
							 | 
						||
| 
								 | 
							
								  EV_ERROR,
							 | 
						||
| 
								 | 
							
								  STR_DATA,
							 | 
						||
| 
								 | 
							
								  STR_END,
							 | 
						||
| 
								 | 
							
								  FSEVENT_CREATED,
							 | 
						||
| 
								 | 
							
								  FSEVENT_MODIFIED,
							 | 
						||
| 
								 | 
							
								  FSEVENT_DELETED,
							 | 
						||
| 
								 | 
							
								  FSEVENT_MOVED,
							 | 
						||
| 
								 | 
							
								  // FSEVENT_CLONED,
							 | 
						||
| 
								 | 
							
								  FSEVENT_UNKNOWN,
							 | 
						||
| 
								 | 
							
								  FSEVENT_TYPE_FILE,
							 | 
						||
| 
								 | 
							
								  FSEVENT_TYPE_DIRECTORY,
							 | 
						||
| 
								 | 
							
								  FSEVENT_TYPE_SYMLINK,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ROOT_GLOBSTAR,
							 | 
						||
| 
								 | 
							
								  DIR_SUFFIX,
							 | 
						||
| 
								 | 
							
								  DOT_SLASH,
							 | 
						||
| 
								 | 
							
								  FUNCTION_TYPE,
							 | 
						||
| 
								 | 
							
								  EMPTY_FN,
							 | 
						||
| 
								 | 
							
								  IDENTITY_FN
							 | 
						||
| 
								 | 
							
								} = require('./constants');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const Depth = (value) => isNaN(value) ? {} : {depth: value};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const stat = promisify(fs.stat);
							 | 
						||
| 
								 | 
							
								const lstat = promisify(fs.lstat);
							 | 
						||
| 
								 | 
							
								const realpath = promisify(fs.realpath);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const statMethods = { stat, lstat };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {String} Path
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} FsEventsWatchContainer
							 | 
						||
| 
								 | 
							
								 * @property {Set<Function>} listeners
							 | 
						||
| 
								 | 
							
								 * @property {Function} rawEmitter
							 | 
						||
| 
								 | 
							
								 * @property {{stop: Function}} watcher
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// fsevents instance helper functions
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Object to hold per-process fsevents instances (may be shared across chokidar FSWatcher instances)
							 | 
						||
| 
								 | 
							
								 * @type {Map<Path,FsEventsWatchContainer>}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								const FSEventsWatchers = new Map();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Threshold of duplicate path prefixes at which to start
							 | 
						||
| 
								 | 
							
								// consolidating going forward
							 | 
						||
| 
								 | 
							
								const consolidateThreshhold = 10;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const wrongEventFlags = new Set([
							 | 
						||
| 
								 | 
							
								  69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912
							 | 
						||
| 
								 | 
							
								]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Instantiates the fsevents interface
							 | 
						||
| 
								 | 
							
								 * @param {Path} path path to be watched
							 | 
						||
| 
								 | 
							
								 * @param {Function} callback called when fsevents is bound and ready
							 | 
						||
| 
								 | 
							
								 * @returns {{stop: Function}} new fsevents instance
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								const createFSEventsInstance = (path, callback) => {
							 | 
						||
| 
								 | 
							
								  const stop = fsevents.watch(path, callback);
							 | 
						||
| 
								 | 
							
								  return {stop};
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Instantiates the fsevents interface or binds listeners to an existing one covering
							 | 
						||
| 
								 | 
							
								 * the same file tree.
							 | 
						||
| 
								 | 
							
								 * @param {Path} path           - to be watched
							 | 
						||
| 
								 | 
							
								 * @param {Path} realPath       - real path for symlinks
							 | 
						||
| 
								 | 
							
								 * @param {Function} listener   - called when fsevents emits events
							 | 
						||
| 
								 | 
							
								 * @param {Function} rawEmitter - passes data to listeners of the 'raw' event
							 | 
						||
| 
								 | 
							
								 * @returns {Function} closer
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function setFSEventsListener(path, realPath, listener, rawEmitter) {
							 | 
						||
| 
								 | 
							
								  let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const parentPath = sysPath.dirname(watchPath);
							 | 
						||
| 
								 | 
							
								  let cont = FSEventsWatchers.get(watchPath);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // If we've accumulated a substantial number of paths that
							 | 
						||
| 
								 | 
							
								  // could have been consolidated by watching one directory
							 | 
						||
| 
								 | 
							
								  // above the current one, create a watcher on the parent
							 | 
						||
| 
								 | 
							
								  // path instead, so that we do consolidate going forward.
							 | 
						||
| 
								 | 
							
								  if (couldConsolidate(parentPath)) {
							 | 
						||
| 
								 | 
							
								    watchPath = parentPath;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const resolvedPath = sysPath.resolve(path);
							 | 
						||
| 
								 | 
							
								  const hasSymlink = resolvedPath !== realPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const filteredListener = (fullPath, flags, info) => {
							 | 
						||
| 
								 | 
							
								    if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath);
							 | 
						||
| 
								 | 
							
								    if (
							 | 
						||
| 
								 | 
							
								      fullPath === resolvedPath ||
							 | 
						||
| 
								 | 
							
								      !fullPath.indexOf(resolvedPath + sysPath.sep)
							 | 
						||
| 
								 | 
							
								    ) listener(fullPath, flags, info);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // check if there is already a watcher on a parent path
							 | 
						||
| 
								 | 
							
								  // modifies `watchPath` to the parent path when it finds a match
							 | 
						||
| 
								 | 
							
								  let watchedParent = false;
							 | 
						||
| 
								 | 
							
								  for (const watchedPath of FSEventsWatchers.keys()) {
							 | 
						||
| 
								 | 
							
								    if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) {
							 | 
						||
| 
								 | 
							
								      watchPath = watchedPath;
							 | 
						||
| 
								 | 
							
								      cont = FSEventsWatchers.get(watchPath);
							 | 
						||
| 
								 | 
							
								      watchedParent = true;
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (cont || watchedParent) {
							 | 
						||
| 
								 | 
							
								    cont.listeners.add(filteredListener);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    cont = {
							 | 
						||
| 
								 | 
							
								      listeners: new Set([filteredListener]),
							 | 
						||
| 
								 | 
							
								      rawEmitter,
							 | 
						||
| 
								 | 
							
								      watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
							 | 
						||
| 
								 | 
							
								        if (!cont.listeners.size) return;
							 | 
						||
| 
								 | 
							
								        const info = fsevents.getInfo(fullPath, flags);
							 | 
						||
| 
								 | 
							
								        cont.listeners.forEach(list => {
							 | 
						||
| 
								 | 
							
								          list(fullPath, flags, info);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cont.rawEmitter(info.event, fullPath, info);
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    FSEventsWatchers.set(watchPath, cont);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // removes this instance's listeners and closes the underlying fsevents
							 | 
						||
| 
								 | 
							
								  // instance if there are no more listeners left
							 | 
						||
| 
								 | 
							
								  return () => {
							 | 
						||
| 
								 | 
							
								    const lst = cont.listeners;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    lst.delete(filteredListener);
							 | 
						||
| 
								 | 
							
								    if (!lst.size) {
							 | 
						||
| 
								 | 
							
								      FSEventsWatchers.delete(watchPath);
							 | 
						||
| 
								 | 
							
								      if (cont.watcher) return cont.watcher.stop().then(() => {
							 | 
						||
| 
								 | 
							
								        cont.rawEmitter = cont.watcher = undefined;
							 | 
						||
| 
								 | 
							
								        Object.freeze(cont);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Decide whether or not we should start a new higher-level
							 | 
						||
| 
								 | 
							
								// parent watcher
							 | 
						||
| 
								 | 
							
								const couldConsolidate = (path) => {
							 | 
						||
| 
								 | 
							
								  let count = 0;
							 | 
						||
| 
								 | 
							
								  for (const watchPath of FSEventsWatchers.keys()) {
							 | 
						||
| 
								 | 
							
								    if (watchPath.indexOf(path) === 0) {
							 | 
						||
| 
								 | 
							
								      count++;
							 | 
						||
| 
								 | 
							
								      if (count >= consolidateThreshhold) {
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return false;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// returns boolean indicating whether fsevents can be used
							 | 
						||
| 
								 | 
							
								const canUse = () => fsevents && FSEventsWatchers.size < 128;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// determines subdirectory traversal levels from root to path
							 | 
						||
| 
								 | 
							
								const calcDepth = (path, root) => {
							 | 
						||
| 
								 | 
							
								  let i = 0;
							 | 
						||
| 
								 | 
							
								  while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++;
							 | 
						||
| 
								 | 
							
								  return i;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// returns boolean indicating whether the fsevents' event info has the same type
							 | 
						||
| 
								 | 
							
								// as the one returned by fs.stat
							 | 
						||
| 
								 | 
							
								const sameTypes = (info, stats) => (
							 | 
						||
| 
								 | 
							
								  info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() ||
							 | 
						||
| 
								 | 
							
								  info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() ||
							 | 
						||
| 
								 | 
							
								  info.type === FSEVENT_TYPE_FILE && stats.isFile()
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @mixin
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class FsEventsHandler {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @param {import('../index').FSWatcher} fsw
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								constructor(fsw) {
							 | 
						||
| 
								 | 
							
								  this.fsw = fsw;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								checkIgnored(path, stats) {
							 | 
						||
| 
								 | 
							
								  const ipaths = this.fsw._ignoredPaths;
							 | 
						||
| 
								 | 
							
								  if (this.fsw._isIgnored(path, stats)) {
							 | 
						||
| 
								 | 
							
								    ipaths.add(path);
							 | 
						||
| 
								 | 
							
								    if (stats && stats.isDirectory()) {
							 | 
						||
| 
								 | 
							
								      ipaths.add(path + ROOT_GLOBSTAR);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ipaths.delete(path);
							 | 
						||
| 
								 | 
							
								  ipaths.delete(path + ROOT_GLOBSTAR);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
							 | 
						||
| 
								 | 
							
								  const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD;
							 | 
						||
| 
								 | 
							
								  this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    const stats = await stat(path)
							 | 
						||
| 
								 | 
							
								    if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								    if (sameTypes(info, stats)) {
							 | 
						||
| 
								 | 
							
								      this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } catch (error) {
							 | 
						||
| 
								 | 
							
								    if (error.code === 'EACCES') {
							 | 
						||
| 
								 | 
							
								      this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts) {
							 | 
						||
| 
								 | 
							
								  if (this.fsw.closed || this.checkIgnored(path)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (event === EV_UNLINK) {
							 | 
						||
| 
								 | 
							
								    const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY
							 | 
						||
| 
								 | 
							
								    // suppress unlink events on never before seen files
							 | 
						||
| 
								 | 
							
								    if (isDirectory || watchedDir.has(item)) {
							 | 
						||
| 
								 | 
							
								      this.fsw._remove(parent, item, isDirectory);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    if (event === EV_ADD) {
							 | 
						||
| 
								 | 
							
								      // track new directories
							 | 
						||
| 
								 | 
							
								      if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) {
							 | 
						||
| 
								 | 
							
								        // push symlinks back to the top of the stack to get handled
							 | 
						||
| 
								 | 
							
								        const curDepth = opts.depth === undefined ?
							 | 
						||
| 
								 | 
							
								          undefined : calcDepth(fullPath, realPath) + 1;
							 | 
						||
| 
								 | 
							
								        return this._addToFsEvents(path, false, true, curDepth);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // track new paths
							 | 
						||
| 
								 | 
							
								      // (other than symlinks being followed, which will be tracked soon)
							 | 
						||
| 
								 | 
							
								      this.fsw._getWatchedDir(parent).add(item);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @type {'add'|'addDir'|'unlink'|'unlinkDir'}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event;
							 | 
						||
| 
								 | 
							
								    this.fsw._emit(eventName, path);
							 | 
						||
| 
								 | 
							
								    if (eventName === EV_ADD_DIR) this._addToFsEvents(path, false, true);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Handle symlinks encountered during directory scan
							 | 
						||
| 
								 | 
							
								 * @param {String} watchPath  - file/dir path to be watched with fsevents
							 | 
						||
| 
								 | 
							
								 * @param {String} realPath   - real path (in case of symlinks)
							 | 
						||
| 
								 | 
							
								 * @param {Function} transform  - path transformer
							 | 
						||
| 
								 | 
							
								 * @param {Function} globFilter - path filter in case a glob pattern was provided
							 | 
						||
| 
								 | 
							
								 * @returns {Function} closer for the watcher instance
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								_watchWithFsEvents(watchPath, realPath, transform, globFilter) {
							 | 
						||
| 
								 | 
							
								  if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return;
							 | 
						||
| 
								 | 
							
								  const opts = this.fsw.options;
							 | 
						||
| 
								 | 
							
								  const watchCallback = async (fullPath, flags, info) => {
							 | 
						||
| 
								 | 
							
								    if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								    if (
							 | 
						||
| 
								 | 
							
								      opts.depth !== undefined &&
							 | 
						||
| 
								 | 
							
								      calcDepth(fullPath, realPath) > opts.depth
							 | 
						||
| 
								 | 
							
								    ) return;
							 | 
						||
| 
								 | 
							
								    const path = transform(sysPath.join(
							 | 
						||
| 
								 | 
							
								      watchPath, sysPath.relative(watchPath, fullPath)
							 | 
						||
| 
								 | 
							
								    ));
							 | 
						||
| 
								 | 
							
								    if (globFilter && !globFilter(path)) return;
							 | 
						||
| 
								 | 
							
								    // ensure directories are tracked
							 | 
						||
| 
								 | 
							
								    const parent = sysPath.dirname(path);
							 | 
						||
| 
								 | 
							
								    const item = sysPath.basename(path);
							 | 
						||
| 
								 | 
							
								    const watchedDir = this.fsw._getWatchedDir(
							 | 
						||
| 
								 | 
							
								      info.type === FSEVENT_TYPE_DIRECTORY ? path : parent
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // correct for wrong events emitted
							 | 
						||
| 
								 | 
							
								    if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) {
							 | 
						||
| 
								 | 
							
								      if (typeof opts.ignored === FUNCTION_TYPE) {
							 | 
						||
| 
								 | 
							
								        let stats;
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          stats = await stat(path);
							 | 
						||
| 
								 | 
							
								        } catch (error) {}
							 | 
						||
| 
								 | 
							
								        if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								        if (this.checkIgnored(path, stats)) return;
							 | 
						||
| 
								 | 
							
								        if (sameTypes(info, stats)) {
							 | 
						||
| 
								 | 
							
								          this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      switch (info.event) {
							 | 
						||
| 
								 | 
							
								      case FSEVENT_CREATED:
							 | 
						||
| 
								 | 
							
								      case FSEVENT_MODIFIED:
							 | 
						||
| 
								 | 
							
								        return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								      case FSEVENT_DELETED:
							 | 
						||
| 
								 | 
							
								      case FSEVENT_MOVED:
							 | 
						||
| 
								 | 
							
								        return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const closer = setFSEventsListener(
							 | 
						||
| 
								 | 
							
								    watchPath,
							 | 
						||
| 
								 | 
							
								    realPath,
							 | 
						||
| 
								 | 
							
								    watchCallback,
							 | 
						||
| 
								 | 
							
								    this.fsw._emitRaw
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								  return closer;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Handle symlinks encountered during directory scan
							 | 
						||
| 
								 | 
							
								 * @param {String} linkPath path to symlink
							 | 
						||
| 
								 | 
							
								 * @param {String} fullPath absolute path to the symlink
							 | 
						||
| 
								 | 
							
								 * @param {Function} transform pre-existing path transformer
							 | 
						||
| 
								 | 
							
								 * @param {Number} curDepth level of subdirectories traversed to where symlink is
							 | 
						||
| 
								 | 
							
								 * @returns {Promise<void>}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) {
							 | 
						||
| 
								 | 
							
								  // don't follow the same symlink more than once
							 | 
						||
| 
								 | 
							
								  if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.fsw._symlinkPaths.set(fullPath, true);
							 | 
						||
| 
								 | 
							
								  this.fsw._incrReadyCount();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    const linkTarget = await realpath(linkPath);
							 | 
						||
| 
								 | 
							
								    if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								    if (this.fsw._isIgnored(linkTarget)) {
							 | 
						||
| 
								 | 
							
								      return this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.fsw._incrReadyCount();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // add the linkTarget for watching with a wrapper for transform
							 | 
						||
| 
								 | 
							
								    // that causes emitted paths to incorporate the link's path
							 | 
						||
| 
								 | 
							
								    this._addToFsEvents(linkTarget || linkPath, (path) => {
							 | 
						||
| 
								 | 
							
								      let aliasedPath = linkPath;
							 | 
						||
| 
								 | 
							
								      if (linkTarget && linkTarget !== DOT_SLASH) {
							 | 
						||
| 
								 | 
							
								        aliasedPath = path.replace(linkTarget, linkPath);
							 | 
						||
| 
								 | 
							
								      } else if (path !== DOT_SLASH) {
							 | 
						||
| 
								 | 
							
								        aliasedPath = sysPath.join(linkPath, path);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return transform(aliasedPath);
							 | 
						||
| 
								 | 
							
								    }, false, curDepth);
							 | 
						||
| 
								 | 
							
								  } catch(error) {
							 | 
						||
| 
								 | 
							
								    if (this.fsw._handleError(error)) {
							 | 
						||
| 
								 | 
							
								      return this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Path} newPath
							 | 
						||
| 
								 | 
							
								 * @param {fs.Stats} stats
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								emitAdd(newPath, stats, processPath, opts, forceAdd) {
							 | 
						||
| 
								 | 
							
								  const pp = processPath(newPath);
							 | 
						||
| 
								 | 
							
								  const isDir = stats.isDirectory();
							 | 
						||
| 
								 | 
							
								  const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp));
							 | 
						||
| 
								 | 
							
								  const base = sysPath.basename(pp);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // ensure empty dirs get tracked
							 | 
						||
| 
								 | 
							
								  if (isDir) this.fsw._getWatchedDir(pp);
							 | 
						||
| 
								 | 
							
								  if (dirObj.has(base)) return;
							 | 
						||
| 
								 | 
							
								  dirObj.add(base);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!opts.ignoreInitial || forceAdd === true) {
							 | 
						||
| 
								 | 
							
								    this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								initWatch(realPath, path, wh, processPath) {
							 | 
						||
| 
								 | 
							
								  if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								  const closer = this._watchWithFsEvents(
							 | 
						||
| 
								 | 
							
								    wh.watchPath,
							 | 
						||
| 
								 | 
							
								    sysPath.resolve(realPath || wh.watchPath),
							 | 
						||
| 
								 | 
							
								    processPath,
							 | 
						||
| 
								 | 
							
								    wh.globFilter
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								  this.fsw._addPathCloser(path, closer);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Handle added path with fsevents
							 | 
						||
| 
								 | 
							
								 * @param {String} path file/dir path or glob pattern
							 | 
						||
| 
								 | 
							
								 * @param {Function|Boolean=} transform converts working path to what the user expects
							 | 
						||
| 
								 | 
							
								 * @param {Boolean=} forceAdd ensure add is emitted
							 | 
						||
| 
								 | 
							
								 * @param {Number=} priorDepth Level of subdirectories already traversed.
							 | 
						||
| 
								 | 
							
								 * @returns {Promise<void>}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								async _addToFsEvents(path, transform, forceAdd, priorDepth) {
							 | 
						||
| 
								 | 
							
								  if (this.fsw.closed) {
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const opts = this.fsw.options;
							 | 
						||
| 
								 | 
							
								  const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const wh = this.fsw._getWatchHelpers(path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // evaluate what is at the path we're being asked to watch
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    const stats = await statMethods[wh.statMethod](wh.watchPath);
							 | 
						||
| 
								 | 
							
								    if (this.fsw.closed) return;
							 | 
						||
| 
								 | 
							
								    if (this.fsw._isIgnored(wh.watchPath, stats)) {
							 | 
						||
| 
								 | 
							
								      throw null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (stats.isDirectory()) {
							 | 
						||
| 
								 | 
							
								      // emit addDir unless this is a glob parent
							 | 
						||
| 
								 | 
							
								      if (!wh.globFilter) this.emitAdd(processPath(path), stats, processPath, opts, forceAdd);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // don't recurse further if it would exceed depth setting
							 | 
						||
| 
								 | 
							
								      if (priorDepth && priorDepth > opts.depth) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // scan the contents of the dir
							 | 
						||
| 
								 | 
							
								      this.fsw._readdirp(wh.watchPath, {
							 | 
						||
| 
								 | 
							
								        fileFilter: entry => wh.filterPath(entry),
							 | 
						||
| 
								 | 
							
								        directoryFilter: entry => wh.filterDir(entry),
							 | 
						||
| 
								 | 
							
								        ...Depth(opts.depth - (priorDepth || 0))
							 | 
						||
| 
								 | 
							
								      }).on(STR_DATA, (entry) => {
							 | 
						||
| 
								 | 
							
								        // need to check filterPath on dirs b/c filterDir is less restrictive
							 | 
						||
| 
								 | 
							
								        if (this.fsw.closed) {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (entry.stats.isDirectory() && !wh.filterPath(entry)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const joinedPath = sysPath.join(wh.watchPath, entry.path);
							 | 
						||
| 
								 | 
							
								        const {fullPath} = entry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (wh.followSymlinks && entry.stats.isSymbolicLink()) {
							 | 
						||
| 
								 | 
							
								          // preserve the current depth here since it can't be derived from
							 | 
						||
| 
								 | 
							
								          // real paths past the symlink
							 | 
						||
| 
								 | 
							
								          const curDepth = opts.depth === undefined ?
							 | 
						||
| 
								 | 
							
								            undefined : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => {
							 | 
						||
| 
								 | 
							
								        this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd);
							 | 
						||
| 
								 | 
							
								      this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } catch (error) {
							 | 
						||
| 
								 | 
							
								    if (!error || this.fsw._handleError(error)) {
							 | 
						||
| 
								 | 
							
								      // TODO: Strange thing: "should not choke on an ignored watch path" will be failed without 2 ready calls -__-
							 | 
						||
| 
								 | 
							
								      this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								      this.fsw._emitReady();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (opts.persistent && forceAdd !== true) {
							 | 
						||
| 
								 | 
							
								    if (typeof transform === FUNCTION_TYPE) {
							 | 
						||
| 
								 | 
							
								      // realpath has already been resolved
							 | 
						||
| 
								 | 
							
								      this.initWatch(undefined, path, wh, processPath);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      let realPath;
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        realPath = await realpath(wh.watchPath);
							 | 
						||
| 
								 | 
							
								      } catch (e) {}
							 | 
						||
| 
								 | 
							
								      this.initWatch(realPath, path, wh, processPath);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = FsEventsHandler;
							 | 
						||
| 
								 | 
							
								module.exports.canUse = canUse;
							 |