| 'use strict'; | 
|   | 
| var fs = require('fs'); | 
| var sysPath = require('path'); | 
| var readdirp = require('readdirp'); | 
| var fsevents; | 
| try { fsevents = require('fsevents'); } catch (error) { | 
|   if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error) | 
| } | 
|   | 
| // fsevents instance helper functions | 
|   | 
| // object to hold per-process fsevents instances | 
| // (may be shared across chokidar FSWatcher instances) | 
| var FSEventsWatchers = Object.create(null); | 
|   | 
| // Threshold of duplicate path prefixes at which to start | 
| // consolidating going forward | 
| var consolidateThreshhold = 10; | 
|   | 
| // Private function: Instantiates the fsevents interface | 
|   | 
| // * path       - string, path to be watched | 
| // * callback   - function, called when fsevents is bound and ready | 
|   | 
| // Returns new fsevents instance | 
| function createFSEventsInstance(path, callback) { | 
|   return (new fsevents(path)).on('fsevent', callback).start(); | 
| } | 
|   | 
| // Private function: Instantiates the fsevents interface or binds listeners | 
| // to an existing one covering the same file tree | 
|   | 
| // * path       - string, path to be watched | 
| // * realPath   - string, real path (in case of symlinks) | 
| // * listener   - function, called when fsevents emits events | 
| // * rawEmitter - function, passes data to listeners of the 'raw' event | 
|   | 
| // Returns close function | 
| function setFSEventsListener(path, realPath, listener, rawEmitter) { | 
|   var watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path; | 
|   var watchContainer; | 
|   var parentPath = sysPath.dirname(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; | 
|   } | 
|   | 
|   var resolvedPath = sysPath.resolve(path); | 
|   var hasSymlink = resolvedPath !== realPath; | 
|   function 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 | 
|   function watchedParent() { | 
|     return Object.keys(FSEventsWatchers).some(function(watchedPath) { | 
|       // condition is met when indexOf returns 0 | 
|       if (!realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep)) { | 
|         watchPath = watchedPath; | 
|         return true; | 
|       } | 
|     }); | 
|   } | 
|   | 
|   if (watchPath in FSEventsWatchers || watchedParent()) { | 
|     watchContainer = FSEventsWatchers[watchPath]; | 
|     watchContainer.listeners.push(filteredListener); | 
|   } else { | 
|     watchContainer = FSEventsWatchers[watchPath] = { | 
|       listeners: [filteredListener], | 
|       rawEmitters: [rawEmitter], | 
|       watcher: createFSEventsInstance(watchPath, function(fullPath, flags) { | 
|         var info = fsevents.getInfo(fullPath, flags); | 
|         watchContainer.listeners.forEach(function(listener) { | 
|           listener(fullPath, flags, info); | 
|         }); | 
|         watchContainer.rawEmitters.forEach(function(emitter) { | 
|           emitter(info.event, fullPath, info); | 
|         }); | 
|       }) | 
|     }; | 
|   } | 
|   var listenerIndex = watchContainer.listeners.length - 1; | 
|   | 
|   // removes this instance's listeners and closes the underlying fsevents | 
|   // instance if there are no more listeners left | 
|   return function close() { | 
|     delete watchContainer.listeners[listenerIndex]; | 
|     delete watchContainer.rawEmitters[listenerIndex]; | 
|     if (!Object.keys(watchContainer.listeners).length) { | 
|       watchContainer.watcher.stop(); | 
|       delete FSEventsWatchers[watchPath]; | 
|     } | 
|   }; | 
| } | 
|   | 
| // Decide whether or not we should start a new higher-level | 
| // parent watcher | 
| function couldConsolidate(path) { | 
|   var keys = Object.keys(FSEventsWatchers); | 
|   var count = 0; | 
|   | 
|   for (var i = 0, len = keys.length; i < len; ++i) { | 
|     var watchPath = keys[i]; | 
|     if (watchPath.indexOf(path) === 0) { | 
|       count++; | 
|       if (count >= consolidateThreshhold) { | 
|         return true; | 
|       } | 
|     } | 
|   } | 
|   | 
|   return false; | 
| } | 
|   | 
| function isConstructor(obj) { | 
|   return obj.prototype !== undefined && obj.prototype.constructor !== undefined; | 
| } | 
|   | 
| // returns boolean indicating whether fsevents can be used | 
| function canUse() { | 
|   return fsevents && Object.keys(FSEventsWatchers).length < 128 && isConstructor(fsevents); | 
| } | 
|   | 
| // determines subdirectory traversal levels from root to path | 
| function depth(path, root) { | 
|   var i = 0; | 
|   while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++; | 
|   return i; | 
| } | 
|   | 
| // fake constructor for attaching fsevents-specific prototype methods that | 
| // will be copied to FSWatcher's prototype | 
| function FsEventsHandler() {} | 
|   | 
| // Private method: Handle symlinks encountered during directory scan | 
|   | 
| // * watchPath  - string, file/dir path to be watched with fsevents | 
| // * realPath   - string, real path (in case of symlinks) | 
| // * transform  - function, path transformer | 
| // * globFilter - function, path filter in case a glob pattern was provided | 
|   | 
| // Returns close function for the watcher instance | 
| FsEventsHandler.prototype._watchWithFsEvents = | 
| function(watchPath, realPath, transform, globFilter) { | 
|   if (this._isIgnored(watchPath)) return; | 
|   var watchCallback = function(fullPath, flags, info) { | 
|     if ( | 
|       this.options.depth !== undefined && | 
|       depth(fullPath, realPath) > this.options.depth | 
|     ) return; | 
|     var path = transform(sysPath.join( | 
|       watchPath, sysPath.relative(watchPath, fullPath) | 
|     )); | 
|     if (globFilter && !globFilter(path)) return; | 
|     // ensure directories are tracked | 
|     var parent = sysPath.dirname(path); | 
|     var item = sysPath.basename(path); | 
|     var watchedDir = this._getWatchedDir( | 
|       info.type === 'directory' ? path : parent | 
|     ); | 
|     var checkIgnored = function(stats) { | 
|       if (this._isIgnored(path, stats)) { | 
|         this._ignoredPaths[path] = true; | 
|         if (stats && stats.isDirectory()) { | 
|           this._ignoredPaths[path + '/**/*'] = true; | 
|         } | 
|         return true; | 
|       } else { | 
|         delete this._ignoredPaths[path]; | 
|         delete this._ignoredPaths[path + '/**/*']; | 
|       } | 
|     }.bind(this); | 
|   | 
|     var handleEvent = function(event) { | 
|       if (checkIgnored()) return; | 
|   | 
|       if (event === 'unlink') { | 
|         // suppress unlink events on never before seen files | 
|         if (info.type === 'directory' || watchedDir.has(item)) { | 
|           this._remove(parent, item); | 
|         } | 
|       } else { | 
|         if (event === 'add') { | 
|           // track new directories | 
|           if (info.type === 'directory') this._getWatchedDir(path); | 
|   | 
|           if (info.type === 'symlink' && this.options.followSymlinks) { | 
|             // push symlinks back to the top of the stack to get handled | 
|             var curDepth = this.options.depth === undefined ? | 
|               undefined : depth(fullPath, realPath) + 1; | 
|             return this._addToFsEvents(path, false, true, curDepth); | 
|           } else { | 
|             // track new paths | 
|             // (other than symlinks being followed, which will be tracked soon) | 
|             this._getWatchedDir(parent).add(item); | 
|           } | 
|         } | 
|         var eventName = info.type === 'directory' ? event + 'Dir' : event; | 
|         this._emit(eventName, path); | 
|         if (eventName === 'addDir') this._addToFsEvents(path, false, true); | 
|       } | 
|     }.bind(this); | 
|   | 
|     function addOrChange() { | 
|       handleEvent(watchedDir.has(item) ? 'change' : 'add'); | 
|     } | 
|     function checkFd() { | 
|       fs.open(path, 'r', function(error, fd) { | 
|         if (error) { | 
|           error.code !== 'EACCES' ? | 
|             handleEvent('unlink') : addOrChange(); | 
|         } else { | 
|           fs.close(fd, function(err) { | 
|             err && err.code !== 'EACCES' ? | 
|               handleEvent('unlink') : addOrChange(); | 
|           }); | 
|         } | 
|       }); | 
|     } | 
|     // correct for wrong events emitted | 
|     var wrongEventFlags = [ | 
|       69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912 | 
|     ]; | 
|     if (wrongEventFlags.indexOf(flags) !== -1 || info.event === 'unknown') { | 
|       if (typeof this.options.ignored === 'function') { | 
|         fs.stat(path, function(error, stats) { | 
|           if (checkIgnored(stats)) return; | 
|           stats ? addOrChange() : handleEvent('unlink'); | 
|         }); | 
|       } else { | 
|         checkFd(); | 
|       } | 
|     } else { | 
|       switch (info.event) { | 
|       case 'created': | 
|       case 'modified': | 
|         return addOrChange(); | 
|       case 'deleted': | 
|       case 'moved': | 
|         return checkFd(); | 
|       } | 
|     } | 
|   }.bind(this); | 
|   | 
|   var closer = setFSEventsListener( | 
|     watchPath, | 
|     realPath, | 
|     watchCallback, | 
|     this.emit.bind(this, 'raw') | 
|   ); | 
|   | 
|   this._emitReady(); | 
|   return closer; | 
| }; | 
|   | 
| // Private method: Handle symlinks encountered during directory scan | 
|   | 
| // * linkPath   - string, path to symlink | 
| // * fullPath   - string, absolute path to the symlink | 
| // * transform  - function, pre-existing path transformer | 
| // * curDepth   - int, level of subdirectories traversed to where symlink is | 
|   | 
| // Returns nothing | 
| FsEventsHandler.prototype._handleFsEventsSymlink = | 
| function(linkPath, fullPath, transform, curDepth) { | 
|   // don't follow the same symlink more than once | 
|   if (this._symlinkPaths[fullPath]) return; | 
|   else this._symlinkPaths[fullPath] = true; | 
|   | 
|   this._readyCount++; | 
|   | 
|   fs.realpath(linkPath, function(error, linkTarget) { | 
|     if (this._handleError(error) || this._isIgnored(linkTarget)) { | 
|       return this._emitReady(); | 
|     } | 
|   | 
|     this._readyCount++; | 
|   | 
|     // add the linkTarget for watching with a wrapper for transform | 
|     // that causes emitted paths to incorporate the link's path | 
|     this._addToFsEvents(linkTarget || linkPath, function(path) { | 
|       var dotSlash = '.' + sysPath.sep; | 
|       var aliasedPath = linkPath; | 
|       if (linkTarget && linkTarget !== dotSlash) { | 
|         aliasedPath = path.replace(linkTarget, linkPath); | 
|       } else if (path !== dotSlash) { | 
|         aliasedPath = sysPath.join(linkPath, path); | 
|       } | 
|       return transform(aliasedPath); | 
|     }, false, curDepth); | 
|   }.bind(this)); | 
| }; | 
|   | 
| // Private method: Handle added path with fsevents | 
|   | 
| // * path       - string, file/directory path or glob pattern | 
| // * transform  - function, converts working path to what the user expects | 
| // * forceAdd   - boolean, ensure add is emitted | 
| // * priorDepth - int, level of subdirectories already traversed | 
|   | 
| // Returns nothing | 
| FsEventsHandler.prototype._addToFsEvents = | 
| function(path, transform, forceAdd, priorDepth) { | 
|   | 
|   // applies transform if provided, otherwise returns same value | 
|   var processPath = typeof transform === 'function' ? | 
|     transform : function(val) { return val; }; | 
|   | 
|   var emitAdd = function(newPath, stats) { | 
|     var pp = processPath(newPath); | 
|     var isDir = stats.isDirectory(); | 
|     var dirObj = this._getWatchedDir(sysPath.dirname(pp)); | 
|     var base = sysPath.basename(pp); | 
|   | 
|     // ensure empty dirs get tracked | 
|     if (isDir) this._getWatchedDir(pp); | 
|   | 
|     if (dirObj.has(base)) return; | 
|     dirObj.add(base); | 
|   | 
|     if (!this.options.ignoreInitial || forceAdd === true) { | 
|       this._emit(isDir ? 'addDir' : 'add', pp, stats); | 
|     } | 
|   }.bind(this); | 
|   | 
|   var wh = this._getWatchHelpers(path); | 
|   | 
|   // evaluate what is at the path we're being asked to watch | 
|   fs[wh.statMethod](wh.watchPath, function(error, stats) { | 
|     if (this._handleError(error) || this._isIgnored(wh.watchPath, stats)) { | 
|       this._emitReady(); | 
|       return this._emitReady(); | 
|     } | 
|   | 
|     if (stats.isDirectory()) { | 
|       // emit addDir unless this is a glob parent | 
|       if (!wh.globFilter) emitAdd(processPath(path), stats); | 
|   | 
|       // don't recurse further if it would exceed depth setting | 
|       if (priorDepth && priorDepth > this.options.depth) return; | 
|   | 
|       // scan the contents of the dir | 
|       readdirp({ | 
|         root: wh.watchPath, | 
|         entryType: 'all', | 
|         fileFilter: wh.filterPath, | 
|         directoryFilter: wh.filterDir, | 
|         lstat: true, | 
|         depth: this.options.depth - (priorDepth || 0) | 
|       }).on('data', function(entry) { | 
|         // need to check filterPath on dirs b/c filterDir is less restrictive | 
|         if (entry.stat.isDirectory() && !wh.filterPath(entry)) return; | 
|   | 
|         var joinedPath = sysPath.join(wh.watchPath, entry.path); | 
|         var fullPath = entry.fullPath; | 
|   | 
|         if (wh.followSymlinks && entry.stat.isSymbolicLink()) { | 
|           // preserve the current depth here since it can't be derived from | 
|           // real paths past the symlink | 
|           var curDepth = this.options.depth === undefined ? | 
|             undefined : depth(joinedPath, sysPath.resolve(wh.watchPath)) + 1; | 
|   | 
|           this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth); | 
|         } else { | 
|           emitAdd(joinedPath, entry.stat); | 
|         } | 
|       }.bind(this)).on('error', function() { | 
|         // Ignore readdirp errors | 
|       }).on('end', this._emitReady); | 
|     } else { | 
|       emitAdd(wh.watchPath, stats); | 
|       this._emitReady(); | 
|     } | 
|   }.bind(this)); | 
|   | 
|   if (this.options.persistent && forceAdd !== true) { | 
|     var initWatch = function(error, realPath) { | 
|       if (this.closed) return; | 
|       var closer = this._watchWithFsEvents( | 
|         wh.watchPath, | 
|         sysPath.resolve(realPath || wh.watchPath), | 
|         processPath, | 
|         wh.globFilter | 
|       ); | 
|       if (closer) { | 
|         this._closers[path] = this._closers[path] || []; | 
|         this._closers[path].push(closer); | 
|       } | 
|     }.bind(this); | 
|   | 
|     if (typeof transform === 'function') { | 
|       // realpath has already been resolved | 
|       initWatch(); | 
|     } else { | 
|       fs.realpath(wh.watchPath, initWatch); | 
|     } | 
|   } | 
| }; | 
|   | 
| module.exports = FsEventsHandler; | 
| module.exports.canUse = canUse; |