diff options
Diffstat (limited to 'node_modules/chokidar/lib')
-rw-r--r-- | node_modules/chokidar/lib/fsevents-handler.js | 412 | ||||
-rw-r--r-- | node_modules/chokidar/lib/nodefs-handler.js | 506 |
2 files changed, 0 insertions, 918 deletions
diff --git a/node_modules/chokidar/lib/fsevents-handler.js b/node_modules/chokidar/lib/fsevents-handler.js deleted file mode 100644 index c85ba60..0000000 --- a/node_modules/chokidar/lib/fsevents-handler.js +++ /dev/null @@ -1,412 +0,0 @@ -'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; diff --git a/node_modules/chokidar/lib/nodefs-handler.js b/node_modules/chokidar/lib/nodefs-handler.js deleted file mode 100644 index 4f49ac2..0000000 --- a/node_modules/chokidar/lib/nodefs-handler.js +++ /dev/null @@ -1,506 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var sysPath = require('path'); -var readdirp = require('readdirp'); -var isBinaryPath = require('is-binary-path'); - -// fs.watch helpers - -// object to hold per-process fs.watch instances -// (may be shared across chokidar FSWatcher instances) -var FsWatchInstances = Object.create(null); - - -// Private function: Instantiates the fs.watch interface - -// * path - string, path to be watched -// * options - object, options to be passed to fs.watch -// * listener - function, main event handler -// * errHandler - function, handler which emits info about errors -// * emitRaw - function, handler which emits raw event data - -// Returns new fsevents instance -function createFsWatchInstance(path, options, listener, errHandler, emitRaw) { - var handleEvent = function(rawEvent, evPath) { - listener(path); - emitRaw(rawEvent, evPath, {watchedPath: path}); - - // emit based on events occurring for files from a directory's watcher in - // case the file's watcher misses it (and rely on throttling to de-dupe) - if (evPath && path !== evPath) { - fsWatchBroadcast( - sysPath.resolve(path, evPath), 'listeners', sysPath.join(path, evPath) - ); - } - }; - try { - return fs.watch(path, options, handleEvent); - } catch (error) { - errHandler(error); - } -} - -// Private function: Helper for passing fs.watch event data to a -// collection of listeners - -// * fullPath - string, absolute path bound to the fs.watch instance -// * type - string, listener type -// * val[1..3] - arguments to be passed to listeners - -// Returns nothing -function fsWatchBroadcast(fullPath, type, val1, val2, val3) { - if (!FsWatchInstances[fullPath]) return; - FsWatchInstances[fullPath][type].forEach(function(listener) { - listener(val1, val2, val3); - }); -} - -// Private function: Instantiates the fs.watch interface or binds listeners -// to an existing one covering the same file system entry - -// * path - string, path to be watched -// * fullPath - string, absolute path -// * options - object, options to be passed to fs.watch -// * handlers - object, container for event listener functions - -// Returns close function -function setFsWatchListener(path, fullPath, options, handlers) { - var listener = handlers.listener; - var errHandler = handlers.errHandler; - var rawEmitter = handlers.rawEmitter; - var container = FsWatchInstances[fullPath]; - var watcher; - if (!options.persistent) { - watcher = createFsWatchInstance( - path, options, listener, errHandler, rawEmitter - ); - return watcher.close.bind(watcher); - } - if (!container) { - watcher = createFsWatchInstance( - path, - options, - fsWatchBroadcast.bind(null, fullPath, 'listeners'), - errHandler, // no need to use broadcast here - fsWatchBroadcast.bind(null, fullPath, 'rawEmitters') - ); - if (!watcher) return; - var broadcastErr = fsWatchBroadcast.bind(null, fullPath, 'errHandlers'); - watcher.on('error', function(error) { - container.watcherUnusable = true; // documented since Node 10.4.1 - // Workaround for https://github.com/joyent/node/issues/4337 - if (process.platform === 'win32' && error.code === 'EPERM') { - fs.open(path, 'r', function(err, fd) { - if (!err) fs.close(fd, function(err) { - if (!err) broadcastErr(error); - }); - }); - } else { - broadcastErr(error); - } - }); - container = FsWatchInstances[fullPath] = { - listeners: [listener], - errHandlers: [errHandler], - rawEmitters: [rawEmitter], - watcher: watcher - }; - } else { - container.listeners.push(listener); - container.errHandlers.push(errHandler); - container.rawEmitters.push(rawEmitter); - } - var listenerIndex = container.listeners.length - 1; - - // removes this instance's listeners and closes the underlying fs.watch - // instance if there are no more listeners left - return function close() { - delete container.listeners[listenerIndex]; - delete container.errHandlers[listenerIndex]; - delete container.rawEmitters[listenerIndex]; - if (!Object.keys(container.listeners).length) { - if (!container.watcherUnusable) { // check to protect against issue #730 - container.watcher.close(); - } - delete FsWatchInstances[fullPath]; - } - }; -} - -// fs.watchFile helpers - -// object to hold per-process fs.watchFile instances -// (may be shared across chokidar FSWatcher instances) -var FsWatchFileInstances = Object.create(null); - -// Private function: Instantiates the fs.watchFile interface or binds listeners -// to an existing one covering the same file system entry - -// * path - string, path to be watched -// * fullPath - string, absolute path -// * options - object, options to be passed to fs.watchFile -// * handlers - object, container for event listener functions - -// Returns close function -function setFsWatchFileListener(path, fullPath, options, handlers) { - var listener = handlers.listener; - var rawEmitter = handlers.rawEmitter; - var container = FsWatchFileInstances[fullPath]; - var listeners = []; - var rawEmitters = []; - if ( - container && ( - container.options.persistent < options.persistent || - container.options.interval > options.interval - ) - ) { - // "Upgrade" the watcher to persistence or a quicker interval. - // This creates some unlikely edge case issues if the user mixes - // settings in a very weird way, but solving for those cases - // doesn't seem worthwhile for the added complexity. - listeners = container.listeners; - rawEmitters = container.rawEmitters; - fs.unwatchFile(fullPath); - container = false; - } - if (!container) { - listeners.push(listener); - rawEmitters.push(rawEmitter); - container = FsWatchFileInstances[fullPath] = { - listeners: listeners, - rawEmitters: rawEmitters, - options: options, - watcher: fs.watchFile(fullPath, options, function(curr, prev) { - container.rawEmitters.forEach(function(rawEmitter) { - rawEmitter('change', fullPath, {curr: curr, prev: prev}); - }); - var currmtime = curr.mtime.getTime(); - if (curr.size !== prev.size || currmtime > prev.mtime.getTime() || currmtime === 0) { - container.listeners.forEach(function(listener) { - listener(path, curr); - }); - } - }) - }; - } else { - container.listeners.push(listener); - container.rawEmitters.push(rawEmitter); - } - var listenerIndex = container.listeners.length - 1; - - // removes this instance's listeners and closes the underlying fs.watchFile - // instance if there are no more listeners left - return function close() { - delete container.listeners[listenerIndex]; - delete container.rawEmitters[listenerIndex]; - if (!Object.keys(container.listeners).length) { - fs.unwatchFile(fullPath); - delete FsWatchFileInstances[fullPath]; - } - }; -} - -// fake constructor for attaching nodefs-specific prototype methods that -// will be copied to FSWatcher's prototype -function NodeFsHandler() {} - -// Private method: Watch file for changes with fs.watchFile or fs.watch. - -// * path - string, path to file or directory. -// * listener - function, to be executed on fs change. - -// Returns close function for the watcher instance -NodeFsHandler.prototype._watchWithNodeFs = -function(path, listener) { - var directory = sysPath.dirname(path); - var basename = sysPath.basename(path); - var parent = this._getWatchedDir(directory); - parent.add(basename); - var absolutePath = sysPath.resolve(path); - var options = {persistent: this.options.persistent}; - if (!listener) listener = Function.prototype; // empty function - - var closer; - if (this.options.usePolling) { - options.interval = this.enableBinaryInterval && isBinaryPath(basename) ? - this.options.binaryInterval : this.options.interval; - closer = setFsWatchFileListener(path, absolutePath, options, { - listener: listener, - rawEmitter: this.emit.bind(this, 'raw') - }); - } else { - closer = setFsWatchListener(path, absolutePath, options, { - listener: listener, - errHandler: this._handleError.bind(this), - rawEmitter: this.emit.bind(this, 'raw') - }); - } - return closer; -}; - -// Private method: Watch a file and emit add event if warranted - -// * file - string, the file's path -// * stats - object, result of fs.stat -// * initialAdd - boolean, was the file added at watch instantiation? -// * callback - function, called when done processing as a newly seen file - -// Returns close function for the watcher instance -NodeFsHandler.prototype._handleFile = -function(file, stats, initialAdd, callback) { - var dirname = sysPath.dirname(file); - var basename = sysPath.basename(file); - var parent = this._getWatchedDir(dirname); - // stats is always present - var prevStats = stats; - - // if the file is already being watched, do nothing - if (parent.has(basename)) return callback(); - - // kick off the watcher - var closer = this._watchWithNodeFs(file, function(path, newStats) { - if (!this._throttle('watch', file, 5)) return; - if (!newStats || newStats && newStats.mtime.getTime() === 0) { - fs.stat(file, function(error, newStats) { - // Fix issues where mtime is null but file is still present - if (error) { - this._remove(dirname, basename); - } else { - // Check that change event was not fired because of changed only accessTime. - var at = newStats.atime.getTime(); - var mt = newStats.mtime.getTime(); - if (!at || at <= mt || mt !== prevStats.mtime.getTime()) { - this._emit('change', file, newStats); - } - prevStats = newStats; - } - }.bind(this)); - // add is about to be emitted if file not already tracked in parent - } else if (parent.has(basename)) { - // Check that change event was not fired because of changed only accessTime. - var at = newStats.atime.getTime(); - var mt = newStats.mtime.getTime(); - if (!at || at <= mt || mt !== prevStats.mtime.getTime()) { - this._emit('change', file, newStats); - } - prevStats = newStats; - } - }.bind(this)); - - // emit an add event if we're supposed to - if (!(initialAdd && this.options.ignoreInitial)) { - if (!this._throttle('add', file, 0)) return; - this._emit('add', file, stats); - } - - if (callback) callback(); - return closer; -}; - -// Private method: Handle symlinks encountered while reading a dir - -// * entry - object, entry object returned by readdirp -// * directory - string, path of the directory being read -// * path - string, path of this item -// * item - string, basename of this item - -// Returns true if no more processing is needed for this entry. -NodeFsHandler.prototype._handleSymlink = -function(entry, directory, path, item) { - var full = entry.fullPath; - var dir = this._getWatchedDir(directory); - - if (!this.options.followSymlinks) { - // watch symlink directly (don't follow) and detect changes - this._readyCount++; - fs.realpath(path, function(error, linkPath) { - if (dir.has(item)) { - if (this._symlinkPaths[full] !== linkPath) { - this._symlinkPaths[full] = linkPath; - this._emit('change', path, entry.stat); - } - } else { - dir.add(item); - this._symlinkPaths[full] = linkPath; - this._emit('add', path, entry.stat); - } - this._emitReady(); - }.bind(this)); - return true; - } - - // don't follow the same symlink more than once - if (this._symlinkPaths[full]) return true; - else this._symlinkPaths[full] = true; -}; - -// Private method: Read directory to add / remove files from `@watched` list -// and re-read it on change. - -// * dir - string, fs path. -// * stats - object, result of fs.stat -// * initialAdd - boolean, was the file added at watch instantiation? -// * depth - int, depth relative to user-supplied path -// * target - string, child path actually targeted for watch -// * wh - object, common watch helpers for this path -// * callback - function, called when dir scan is complete - -// Returns close function for the watcher instance -NodeFsHandler.prototype._handleDir = -function(dir, stats, initialAdd, depth, target, wh, callback) { - var parentDir = this._getWatchedDir(sysPath.dirname(dir)); - var tracked = parentDir.has(sysPath.basename(dir)); - if (!(initialAdd && this.options.ignoreInitial) && !target && !tracked) { - if (!wh.hasGlob || wh.globFilter(dir)) this._emit('addDir', dir, stats); - } - - // ensure dir is tracked (harmless if redundant) - parentDir.add(sysPath.basename(dir)); - this._getWatchedDir(dir); - - var read = function(directory, initialAdd, done) { - // Normalize the directory name on Windows - directory = sysPath.join(directory, ''); - - if (!wh.hasGlob) { - var throttler = this._throttle('readdir', directory, 1000); - if (!throttler) return; - } - - var previous = this._getWatchedDir(wh.path); - var current = []; - - readdirp({ - root: directory, - entryType: 'all', - fileFilter: wh.filterPath, - directoryFilter: wh.filterDir, - depth: 0, - lstat: true - }).on('data', function(entry) { - var item = entry.path; - var path = sysPath.join(directory, item); - current.push(item); - - if (entry.stat.isSymbolicLink() && - this._handleSymlink(entry, directory, path, item)) return; - - // Files that present in current directory snapshot - // but absent in previous are added to watch list and - // emit `add` event. - if (item === target || !target && !previous.has(item)) { - this._readyCount++; - - // ensure relativeness of path is preserved in case of watcher reuse - path = sysPath.join(dir, sysPath.relative(dir, path)); - - this._addToNodeFs(path, initialAdd, wh, depth + 1); - } - }.bind(this)).on('end', function() { - var wasThrottled = throttler ? throttler.clear() : false; - if (done) done(); - - // Files that absent in current directory snapshot - // but present in previous emit `remove` event - // and are removed from @watched[directory]. - previous.children().filter(function(item) { - return item !== directory && - current.indexOf(item) === -1 && - // in case of intersecting globs; - // a path may have been filtered out of this readdir, but - // shouldn't be removed because it matches a different glob - (!wh.hasGlob || wh.filterPath({ - fullPath: sysPath.resolve(directory, item) - })); - }).forEach(function(item) { - this._remove(directory, item); - }, this); - - // one more time for any missed in case changes came in extremely quickly - if (wasThrottled) read(directory, false); - }.bind(this)).on('error', this._handleError.bind(this)); - }.bind(this); - - var closer; - - if (this.options.depth == null || depth <= this.options.depth) { - if (!target) read(dir, initialAdd, callback); - closer = this._watchWithNodeFs(dir, function(dirPath, stats) { - // if current directory is removed, do nothing - if (stats && stats.mtime.getTime() === 0) return; - - read(dirPath, false); - }); - } else { - callback(); - } - return closer; -}; - -// Private method: Handle added file, directory, or glob pattern. -// Delegates call to _handleFile / _handleDir after checks. - -// * path - string, path to file or directory. -// * initialAdd - boolean, was the file added at watch instantiation? -// * depth - int, depth relative to user-supplied path -// * target - string, child path actually targeted for watch -// * callback - function, indicates whether the path was found or not - -// Returns nothing -NodeFsHandler.prototype._addToNodeFs = -function(path, initialAdd, priorWh, depth, target, callback) { - if (!callback) callback = Function.prototype; - var ready = this._emitReady; - if (this._isIgnored(path) || this.closed) { - ready(); - return callback(null, false); - } - - var wh = this._getWatchHelpers(path, depth); - if (!wh.hasGlob && priorWh) { - wh.hasGlob = priorWh.hasGlob; - wh.globFilter = priorWh.globFilter; - wh.filterPath = priorWh.filterPath; - wh.filterDir = priorWh.filterDir; - } - - // evaluate what is at the path we're being asked to watch - fs[wh.statMethod](wh.watchPath, function(error, stats) { - if (this._handleError(error)) return callback(null, path); - if (this._isIgnored(wh.watchPath, stats)) { - ready(); - return callback(null, false); - } - - var initDir = function(dir, target) { - return this._handleDir(dir, stats, initialAdd, depth, target, wh, ready); - }.bind(this); - - var closer; - if (stats.isDirectory()) { - closer = initDir(wh.watchPath, target); - } else if (stats.isSymbolicLink()) { - var parent = sysPath.dirname(wh.watchPath); - this._getWatchedDir(parent).add(wh.watchPath); - this._emit('add', wh.watchPath, stats); - closer = initDir(parent, path); - - // preserve this symlink's target path - fs.realpath(path, function(error, targetPath) { - this._symlinkPaths[sysPath.resolve(path)] = targetPath; - ready(); - }.bind(this)); - } else { - closer = this._handleFile(wh.watchPath, stats, initialAdd, ready); - } - - if (closer) { - this._closers[path] = this._closers[path] || []; - this._closers[path].push(closer); - } - callback(null, false); - }.bind(this)); -}; - -module.exports = NodeFsHandler; |