Added Gulp.js for compiling SCSS stylesheets
This commit is contained in:
412
node_modules/chokidar/lib/fsevents-handler.js
generated
vendored
Normal file
412
node_modules/chokidar/lib/fsevents-handler.js
generated
vendored
Normal file
@ -0,0 +1,412 @@
|
||||
'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;
|
506
node_modules/chokidar/lib/nodefs-handler.js
generated
vendored
Normal file
506
node_modules/chokidar/lib/nodefs-handler.js
generated
vendored
Normal file
@ -0,0 +1,506 @@
|
||||
'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;
|
Reference in New Issue
Block a user