mirror of
https://github.com/fooflington/selfdefined.git
synced 2025-06-11 05:11:40 +00:00
update
This commit is contained in:
60
node_modules/chokidar/lib/constants.js
generated
vendored
Normal file
60
node_modules/chokidar/lib/constants.js
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const {sep} = require('path');
|
||||
const {platform} = process;
|
||||
|
||||
exports.EV_ALL = 'all';
|
||||
exports.EV_READY = 'ready';
|
||||
exports.EV_ADD = 'add';
|
||||
exports.EV_CHANGE = 'change';
|
||||
exports.EV_ADD_DIR = 'addDir';
|
||||
exports.EV_UNLINK = 'unlink';
|
||||
exports.EV_UNLINK_DIR = 'unlinkDir';
|
||||
exports.EV_RAW = 'raw';
|
||||
exports.EV_ERROR = 'error';
|
||||
|
||||
exports.STR_DATA = 'data';
|
||||
exports.STR_END = 'end';
|
||||
exports.STR_CLOSE = 'close';
|
||||
|
||||
exports.FSEVENT_CREATED = 'created';
|
||||
exports.FSEVENT_MODIFIED = 'modified';
|
||||
exports.FSEVENT_DELETED = 'deleted';
|
||||
exports.FSEVENT_MOVED = 'moved';
|
||||
exports.FSEVENT_CLONED = 'cloned';
|
||||
exports.FSEVENT_UNKNOWN = 'unknown';
|
||||
exports.FSEVENT_TYPE_DIRECTORY = 'directory';
|
||||
exports.FSEVENT_TYPE_SYMLINK = 'symlink';
|
||||
|
||||
exports.KEY_LISTENERS = 'listeners';
|
||||
exports.KEY_ERR = 'errHandlers';
|
||||
exports.KEY_RAW = 'rawEmitters';
|
||||
exports.HANDLER_KEYS = [exports.KEY_LISTENERS, exports.KEY_ERR, exports.KEY_RAW];
|
||||
|
||||
exports.DOT_SLASH = `.${sep}`;
|
||||
|
||||
exports.BACK_SLASH_RE = /\\/g;
|
||||
exports.DOUBLE_SLASH_RE = /\/\//;
|
||||
exports.SLASH_OR_BACK_SLASH_RE = /[/\\]/;
|
||||
exports.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
|
||||
exports.REPLACER_RE = /^\.[/\\]/;
|
||||
|
||||
exports.SLASH = '/';
|
||||
exports.BRACE_START = '{';
|
||||
exports.BANG = '!';
|
||||
exports.ONE_DOT = '.';
|
||||
exports.TWO_DOTS = '..';
|
||||
exports.STAR = '*';
|
||||
exports.GLOBSTAR = '**';
|
||||
exports.ROOT_GLOBSTAR = '/**/*';
|
||||
exports.SLASH_GLOBSTAR = '/**';
|
||||
exports.DIR_SUFFIX = 'Dir';
|
||||
exports.ANYMATCH_OPTS = {dot: true};
|
||||
exports.STRING_TYPE = 'string';
|
||||
exports.FUNCTION_TYPE = 'function';
|
||||
exports.EMPTY_STR = '';
|
||||
exports.EMPTY_FN = () => {};
|
||||
exports.IDENTITY_FN = val => val;
|
||||
|
||||
exports.isWindows = platform === 'win32';
|
||||
exports.isMacos = platform === 'darwin';
|
516
node_modules/chokidar/lib/fsevents-handler.js
generated
vendored
Normal file
516
node_modules/chokidar/lib/fsevents-handler.js
generated
vendored
Normal file
@ -0,0 +1,516 @@
|
||||
'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_DIRECTORY,
|
||||
FSEVENT_TYPE_SYMLINK,
|
||||
|
||||
ROOT_GLOBSTAR,
|
||||
DIR_SUFFIX,
|
||||
DOT_SLASH,
|
||||
FUNCTION_TYPE,
|
||||
EMPTY_FN,
|
||||
IDENTITY_FN
|
||||
} = require('./constants');
|
||||
const FS_MODE_READ = 'r';
|
||||
|
||||
const Depth = (value) => isNaN(value) ? {} : {depth: value};
|
||||
|
||||
const stat = promisify(fs.stat);
|
||||
const open = promisify(fs.open);
|
||||
const close = promisify(fs.close);
|
||||
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, fsw) {
|
||||
let watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path;
|
||||
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 (fsw.closed) 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 checkFd(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
|
||||
try {
|
||||
const fd = await open(path, FS_MODE_READ);
|
||||
if (this.fsw.closed) return;
|
||||
await close(fd);
|
||||
if (this.fsw.closed) return;
|
||||
this.addOrChange(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) {
|
||||
// suppress unlink events on never before seen files
|
||||
if (info.type === FSEVENT_TYPE_DIRECTORY || watchedDir.has(item)) {
|
||||
this.fsw._remove(parent, item);
|
||||
}
|
||||
} 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) return;
|
||||
if (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 (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.checkFd(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.checkFd(path, fullPath, realPath, parent, watchedDir, item, info, opts);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const closer = setFSEventsListener(
|
||||
watchPath,
|
||||
realPath,
|
||||
watchCallback,
|
||||
this.fsw._emitRaw,
|
||||
this.fsw
|
||||
);
|
||||
|
||||
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;
|
637
node_modules/chokidar/lib/nodefs-handler.js
generated
vendored
Normal file
637
node_modules/chokidar/lib/nodefs-handler.js
generated
vendored
Normal file
@ -0,0 +1,637 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const sysPath = require('path');
|
||||
const { promisify } = require('util');
|
||||
const isBinaryPath = require('is-binary-path');
|
||||
const {
|
||||
isWindows,
|
||||
EMPTY_FN,
|
||||
EMPTY_STR,
|
||||
KEY_LISTENERS,
|
||||
KEY_ERR,
|
||||
KEY_RAW,
|
||||
HANDLER_KEYS,
|
||||
EV_CHANGE,
|
||||
EV_ADD,
|
||||
EV_ADD_DIR,
|
||||
EV_ERROR,
|
||||
STR_DATA,
|
||||
STR_END,
|
||||
BRACE_START,
|
||||
STAR
|
||||
} = require('./constants');
|
||||
|
||||
const THROTTLE_MODE_WATCH = 'watch';
|
||||
|
||||
const open = promisify(fs.open);
|
||||
const stat = promisify(fs.stat);
|
||||
const lstat = promisify(fs.lstat);
|
||||
const close = promisify(fs.close);
|
||||
const fsrealpath = promisify(fs.realpath);
|
||||
|
||||
const statMethods = { lstat, stat };
|
||||
|
||||
// TODO: emit errors properly. Example: EMFILE on Macos.
|
||||
const foreach = (val, fn) => {
|
||||
if (val instanceof Set) {
|
||||
val.forEach(fn);
|
||||
} else {
|
||||
fn(val);
|
||||
}
|
||||
};
|
||||
|
||||
const addAndConvert = (main, prop, item) => {
|
||||
let container = main[prop];
|
||||
if (!(container instanceof Set)) {
|
||||
main[prop] = container = new Set([container]);
|
||||
}
|
||||
container.add(item);
|
||||
};
|
||||
|
||||
const clearItem = cont => key => {
|
||||
const set = cont[key];
|
||||
if (set instanceof Set) {
|
||||
set.clear();
|
||||
} else {
|
||||
delete cont[key];
|
||||
}
|
||||
};
|
||||
|
||||
const delFromSet = (main, prop, item) => {
|
||||
const container = main[prop];
|
||||
if (container instanceof Set) {
|
||||
container.delete(item);
|
||||
} else if (container === item) {
|
||||
delete main[prop];
|
||||
}
|
||||
};
|
||||
|
||||
const isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
||||
|
||||
/**
|
||||
* @typedef {String} Path
|
||||
*/
|
||||
|
||||
// fs_watch helpers
|
||||
|
||||
// object to hold per-process fs_watch instances
|
||||
// (may be shared across chokidar FSWatcher instances)
|
||||
|
||||
/**
|
||||
* @typedef {Object} FsWatchContainer
|
||||
* @property {Set} listeners
|
||||
* @property {Set} errHandlers
|
||||
* @property {Set} rawEmitters
|
||||
* @property {fs.FSWatcher=} watcher
|
||||
* @property {Boolean=} watcherUnusable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Map<String,FsWatchContainer>}
|
||||
*/
|
||||
const FsWatchInstances = new Map();
|
||||
|
||||
/**
|
||||
* Instantiates the fs_watch interface
|
||||
* @param {String} path to be watched
|
||||
* @param {Object} options to be passed to fs_watch
|
||||
* @param {Function} listener main event handler
|
||||
* @param {Function} errHandler emits info about errors
|
||||
* @param {Function} emitRaw emits raw event data
|
||||
* @returns {fs.FSWatcher} new fsevents instance
|
||||
*/
|
||||
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
|
||||
const handleEvent = (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), KEY_LISTENERS, sysPath.join(path, evPath)
|
||||
);
|
||||
}
|
||||
};
|
||||
try {
|
||||
return fs.watch(path, options, handleEvent);
|
||||
} catch (error) {
|
||||
errHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for passing fs_watch event data to a collection of listeners
|
||||
* @param {Path} fullPath absolute path bound to fs_watch instance
|
||||
* @param {String} type listener type
|
||||
* @param {*=} val1 arguments to be passed to listeners
|
||||
* @param {*=} val2
|
||||
* @param {*=} val3
|
||||
*/
|
||||
const fsWatchBroadcast = (fullPath, type, val1, val2, val3) => {
|
||||
const cont = FsWatchInstances.get(fullPath);
|
||||
if (!cont) return;
|
||||
foreach(cont[type], (listener) => {
|
||||
listener(val1, val2, val3);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiates the fs_watch interface or binds listeners
|
||||
* to an existing one covering the same file system entry
|
||||
* @param {String} path
|
||||
* @param {String} fullPath absolute path
|
||||
* @param {Object} options to be passed to fs_watch
|
||||
* @param {Object} handlers container for event listener functions
|
||||
*/
|
||||
const setFsWatchListener = (path, fullPath, options, handlers) => {
|
||||
const {listener, errHandler, rawEmitter} = handlers;
|
||||
let cont = FsWatchInstances.get(fullPath);
|
||||
|
||||
/** @type {fs.FSWatcher=} */
|
||||
let watcher;
|
||||
if (!options.persistent) {
|
||||
watcher = createFsWatchInstance(
|
||||
path, options, listener, errHandler, rawEmitter
|
||||
);
|
||||
return watcher.close.bind(watcher);
|
||||
}
|
||||
if (cont) {
|
||||
addAndConvert(cont, KEY_LISTENERS, listener);
|
||||
addAndConvert(cont, KEY_ERR, errHandler);
|
||||
addAndConvert(cont, KEY_RAW, rawEmitter);
|
||||
} else {
|
||||
watcher = createFsWatchInstance(
|
||||
path,
|
||||
options,
|
||||
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
||||
errHandler, // no need to use broadcast here
|
||||
fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
|
||||
);
|
||||
if (!watcher) return;
|
||||
watcher.on(EV_ERROR, async (error) => {
|
||||
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
|
||||
cont.watcherUnusable = true; // documented since Node 10.4.1
|
||||
// Workaround for https://github.com/joyent/node/issues/4337
|
||||
if (isWindows && error.code === 'EPERM') {
|
||||
try {
|
||||
const fd = await open(path, 'r');
|
||||
await close(fd);
|
||||
broadcastErr(error);
|
||||
} catch (err) {}
|
||||
} else {
|
||||
broadcastErr(error);
|
||||
}
|
||||
});
|
||||
cont = {
|
||||
listeners: listener,
|
||||
errHandlers: errHandler,
|
||||
rawEmitters: rawEmitter,
|
||||
watcher
|
||||
};
|
||||
FsWatchInstances.set(fullPath, cont);
|
||||
}
|
||||
// const index = cont.listeners.indexOf(listener);
|
||||
|
||||
// removes this instance's listeners and closes the underlying fs_watch
|
||||
// instance if there are no more listeners left
|
||||
return () => {
|
||||
delFromSet(cont, KEY_LISTENERS, listener);
|
||||
delFromSet(cont, KEY_ERR, errHandler);
|
||||
delFromSet(cont, KEY_RAW, rawEmitter);
|
||||
if (isEmptySet(cont.listeners)) {
|
||||
// Check to protect against issue gh-730.
|
||||
// if (cont.watcherUnusable) {
|
||||
cont.watcher.close();
|
||||
// }
|
||||
FsWatchInstances.delete(fullPath);
|
||||
HANDLER_KEYS.forEach(clearItem(cont));
|
||||
cont.watcher = undefined;
|
||||
Object.freeze(cont);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// fs_watchFile helpers
|
||||
|
||||
// object to hold per-process fs_watchFile instances
|
||||
// (may be shared across chokidar FSWatcher instances)
|
||||
const FsWatchFileInstances = new Map();
|
||||
|
||||
/**
|
||||
* Instantiates the fs_watchFile interface or binds listeners
|
||||
* to an existing one covering the same file system entry
|
||||
* @param {String} path to be watched
|
||||
* @param {String} fullPath absolute path
|
||||
* @param {Object} options options to be passed to fs_watchFile
|
||||
* @param {Object} handlers container for event listener functions
|
||||
* @returns {Function} closer
|
||||
*/
|
||||
const setFsWatchFileListener = (path, fullPath, options, handlers) => {
|
||||
const {listener, rawEmitter} = handlers;
|
||||
let cont = FsWatchFileInstances.get(fullPath);
|
||||
|
||||
/* eslint-disable no-unused-vars, prefer-destructuring */
|
||||
let listeners = new Set();
|
||||
let rawEmitters = new Set();
|
||||
|
||||
const copts = cont && cont.options;
|
||||
if (copts && (copts.persistent < options.persistent || copts.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 = cont.listeners;
|
||||
rawEmitters = cont.rawEmitters;
|
||||
fs.unwatchFile(fullPath);
|
||||
cont = undefined;
|
||||
}
|
||||
|
||||
/* eslint-enable no-unused-vars, prefer-destructuring */
|
||||
|
||||
if (cont) {
|
||||
addAndConvert(cont, KEY_LISTENERS, listener);
|
||||
addAndConvert(cont, KEY_RAW, rawEmitter);
|
||||
} else {
|
||||
// TODO
|
||||
// listeners.add(listener);
|
||||
// rawEmitters.add(rawEmitter);
|
||||
cont = {
|
||||
listeners: listener,
|
||||
rawEmitters: rawEmitter,
|
||||
options,
|
||||
watcher: fs.watchFile(fullPath, options, (curr, prev) => {
|
||||
foreach(cont.rawEmitters, (rawEmitter) => {
|
||||
rawEmitter(EV_CHANGE, fullPath, {curr, prev});
|
||||
});
|
||||
const currmtime = curr.mtimeMs;
|
||||
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
||||
foreach(cont.listeners, (listener) => listener(path, curr));
|
||||
}
|
||||
})
|
||||
};
|
||||
FsWatchFileInstances.set(fullPath, cont);
|
||||
}
|
||||
// const index = cont.listeners.indexOf(listener);
|
||||
|
||||
// Removes this instance's listeners and closes the underlying fs_watchFile
|
||||
// instance if there are no more listeners left.
|
||||
return () => {
|
||||
delFromSet(cont, KEY_LISTENERS, listener);
|
||||
delFromSet(cont, KEY_RAW, rawEmitter);
|
||||
if (isEmptySet(cont.listeners)) {
|
||||
FsWatchFileInstances.delete(fullPath);
|
||||
fs.unwatchFile(fullPath);
|
||||
cont.options = cont.watcher = undefined;
|
||||
Object.freeze(cont);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @mixin
|
||||
*/
|
||||
class NodeFsHandler {
|
||||
|
||||
/**
|
||||
* @param {import("../index").FSWatcher} fsW
|
||||
*/
|
||||
constructor(fsW) {
|
||||
this.fsw = fsW;
|
||||
this._boundHandleError = (error) => fsW._handleError(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch file for changes with fs_watchFile or fs_watch.
|
||||
* @param {String} path to file or dir
|
||||
* @param {Function} listener on fs change
|
||||
* @returns {Function} closer for the watcher instance
|
||||
*/
|
||||
_watchWithNodeFs(path, listener) {
|
||||
const opts = this.fsw.options;
|
||||
const directory = sysPath.dirname(path);
|
||||
const basename = sysPath.basename(path);
|
||||
const parent = this.fsw._getWatchedDir(directory);
|
||||
parent.add(basename);
|
||||
const absolutePath = sysPath.resolve(path);
|
||||
const options = {persistent: opts.persistent};
|
||||
if (!listener) listener = EMPTY_FN;
|
||||
|
||||
let closer;
|
||||
if (opts.usePolling) {
|
||||
options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ?
|
||||
opts.binaryInterval : opts.interval;
|
||||
closer = setFsWatchFileListener(path, absolutePath, options, {
|
||||
listener,
|
||||
rawEmitter: this.fsw._emitRaw
|
||||
});
|
||||
} else {
|
||||
closer = setFsWatchListener(path, absolutePath, options, {
|
||||
listener,
|
||||
errHandler: this._boundHandleError,
|
||||
rawEmitter: this.fsw._emitRaw
|
||||
});
|
||||
}
|
||||
return closer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch a file and emit add event if warranted.
|
||||
* @param {Path} file Path
|
||||
* @param {fs.Stats} stats result of fs_stat
|
||||
* @param {Boolean} initialAdd was the file added at watch instantiation?
|
||||
* @returns {Function} closer for the watcher instance
|
||||
*/
|
||||
_handleFile(file, stats, initialAdd) {
|
||||
if (this.fsw.closed) {
|
||||
return;
|
||||
}
|
||||
const dirname = sysPath.dirname(file);
|
||||
const basename = sysPath.basename(file);
|
||||
const parent = this.fsw._getWatchedDir(dirname);
|
||||
// stats is always present
|
||||
let prevStats = stats;
|
||||
|
||||
// if the file is already being watched, do nothing
|
||||
if (parent.has(basename)) return;
|
||||
|
||||
// kick off the watcher
|
||||
const closer = this._watchWithNodeFs(file, async (path, newStats) => {
|
||||
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return;
|
||||
if (!newStats || newStats.mtimeMs === 0) {
|
||||
try {
|
||||
const newStats = await stat(file);
|
||||
if (this.fsw.closed) return;
|
||||
// Check that change event was not fired because of changed only accessTime.
|
||||
const at = newStats.atimeMs;
|
||||
const mt = newStats.mtimeMs;
|
||||
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
||||
this.fsw._emit(EV_CHANGE, file, newStats);
|
||||
}
|
||||
prevStats = newStats;
|
||||
} catch (error) {
|
||||
// Fix issues where mtime is null but file is still present
|
||||
this.fsw._remove(dirname, basename);
|
||||
}
|
||||
// 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.
|
||||
const at = newStats.atimeMs;
|
||||
const mt = newStats.mtimeMs;
|
||||
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
||||
this.fsw._emit(EV_CHANGE, file, newStats);
|
||||
}
|
||||
prevStats = newStats;
|
||||
}
|
||||
});
|
||||
|
||||
// emit an add event if we're supposed to
|
||||
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
|
||||
if (!this.fsw._throttle(EV_ADD, file, 0)) return;
|
||||
this.fsw._emit(EV_ADD, file, stats);
|
||||
}
|
||||
|
||||
return closer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle symlinks encountered while reading a dir.
|
||||
* @param {Object} entry returned by readdirp
|
||||
* @param {String} directory path of dir being read
|
||||
* @param {String} path of this item
|
||||
* @param {String} item basename of this item
|
||||
* @returns {Promise<Boolean>} true if no more processing is needed for this entry.
|
||||
*/
|
||||
async _handleSymlink(entry, directory, path, item) {
|
||||
if (this.fsw.closed) {
|
||||
return;
|
||||
}
|
||||
const full = entry.fullPath;
|
||||
const dir = this.fsw._getWatchedDir(directory);
|
||||
|
||||
if (!this.fsw.options.followSymlinks) {
|
||||
// watch symlink directly (don't follow) and detect changes
|
||||
this.fsw._incrReadyCount();
|
||||
const linkPath = await fsrealpath(path);
|
||||
if (this.fsw.closed) return;
|
||||
if (dir.has(item)) {
|
||||
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
||||
this.fsw._symlinkPaths.set(full, linkPath);
|
||||
this.fsw._emit(EV_CHANGE, path, entry.stats);
|
||||
}
|
||||
} else {
|
||||
dir.add(item);
|
||||
this.fsw._symlinkPaths.set(full, linkPath);
|
||||
this.fsw._emit(EV_ADD, path, entry.stats);
|
||||
}
|
||||
this.fsw._emitReady();
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't follow the same symlink more than once
|
||||
if (this.fsw._symlinkPaths.has(full)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.fsw._symlinkPaths.set(full, true);
|
||||
}
|
||||
|
||||
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
||||
// Normalize the directory name on Windows
|
||||
directory = sysPath.join(directory, EMPTY_STR);
|
||||
|
||||
if (!wh.hasGlob) {
|
||||
throttler = this.fsw._throttle('readdir', directory, 1000);
|
||||
if (!throttler) return;
|
||||
}
|
||||
|
||||
const previous = this.fsw._getWatchedDir(wh.path);
|
||||
const current = new Set();
|
||||
|
||||
let stream = this.fsw._readdirp(directory, {
|
||||
fileFilter: entry => wh.filterPath(entry),
|
||||
directoryFilter: entry => wh.filterDir(entry),
|
||||
depth: 0
|
||||
}).on(STR_DATA, async (entry) => {
|
||||
if (this.fsw.closed) {
|
||||
stream = undefined;
|
||||
return;
|
||||
}
|
||||
const item = entry.path;
|
||||
let path = sysPath.join(directory, item);
|
||||
current.add(item);
|
||||
|
||||
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.fsw.closed) {
|
||||
stream = undefined;
|
||||
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.fsw._incrReadyCount();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}).on(EV_ERROR, this._boundHandleError);
|
||||
|
||||
return new Promise(resolve =>
|
||||
stream.once(STR_END, () => {
|
||||
if (this.fsw.closed) {
|
||||
stream = undefined;
|
||||
return;
|
||||
}
|
||||
const wasThrottled = throttler ? throttler.clear() : false;
|
||||
|
||||
resolve();
|
||||
|
||||
// Files that absent in current directory snapshot
|
||||
// but present in previous emit `remove` event
|
||||
// and are removed from @watched[directory].
|
||||
previous.getChildren().filter((item) => {
|
||||
return item !== directory &&
|
||||
!current.has(item) &&
|
||||
// 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((item) => {
|
||||
this.fsw._remove(directory, item);
|
||||
});
|
||||
|
||||
stream = undefined;
|
||||
|
||||
// one more time for any missed in case changes came in extremely quickly
|
||||
if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read directory to add / remove files from `@watched` list and re-read it on change.
|
||||
* @param {String} dir fs path
|
||||
* @param {fs.Stats} stats
|
||||
* @param {Boolean} initialAdd
|
||||
* @param {Number} depth relative to user-supplied path
|
||||
* @param {String} target child path targeted for watch
|
||||
* @param {Object} wh Common watch helpers for this path
|
||||
* @param {String} realpath
|
||||
* @returns {Promise<Function>} closer for the watcher instance.
|
||||
*/
|
||||
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
|
||||
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
|
||||
const tracked = parentDir.has(sysPath.basename(dir));
|
||||
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
||||
if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats);
|
||||
}
|
||||
|
||||
// ensure dir is tracked (harmless if redundant)
|
||||
parentDir.add(sysPath.basename(dir));
|
||||
this.fsw._getWatchedDir(dir);
|
||||
let throttler;
|
||||
let closer;
|
||||
|
||||
const oDepth = this.fsw.options.depth;
|
||||
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
|
||||
if (!target) {
|
||||
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
|
||||
if (this.fsw.closed) return;
|
||||
}
|
||||
|
||||
closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
|
||||
// if current directory is removed, do nothing
|
||||
if (stats && stats.mtimeMs === 0) return;
|
||||
|
||||
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
|
||||
});
|
||||
}
|
||||
return closer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle added file, directory, or glob pattern.
|
||||
* Delegates call to _handleFile / _handleDir after checks.
|
||||
* @param {String} path to file or ir
|
||||
* @param {Boolean} initialAdd was the file added at watch instantiation?
|
||||
* @param {Object} priorWh depth relative to user-supplied path
|
||||
* @param {Number} depth Child path actually targeted for watch
|
||||
* @param {String=} target Child path actually targeted for watch
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
|
||||
const ready = this.fsw._emitReady;
|
||||
if (this.fsw._isIgnored(path) || this.fsw.closed) {
|
||||
ready();
|
||||
return false;
|
||||
}
|
||||
|
||||
const wh = this.fsw._getWatchHelpers(path, depth);
|
||||
if (!wh.hasGlob && priorWh) {
|
||||
wh.hasGlob = priorWh.hasGlob;
|
||||
wh.globFilter = priorWh.globFilter;
|
||||
wh.filterPath = entry => priorWh.filterPath(entry);
|
||||
wh.filterDir = entry => priorWh.filterDir(entry);
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
ready();
|
||||
return false;
|
||||
}
|
||||
|
||||
const follow = this.fsw.options.followSymlinks && !path.includes(STAR) && !path.includes(BRACE_START);
|
||||
let closer;
|
||||
if (stats.isDirectory()) {
|
||||
const targetPath = follow ? await fsrealpath(path) : path;
|
||||
if (this.fsw.closed) return;
|
||||
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
||||
if (this.fsw.closed) return;
|
||||
// preserve this symlink's target path
|
||||
if (path !== targetPath && targetPath !== undefined) {
|
||||
this.fsw._symlinkPaths.set(targetPath, true);
|
||||
}
|
||||
} else if (stats.isSymbolicLink()) {
|
||||
const targetPath = follow ? await fsrealpath(path) : path;
|
||||
if (this.fsw.closed) return;
|
||||
const parent = sysPath.dirname(wh.watchPath);
|
||||
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
||||
this.fsw._emit(EV_ADD, wh.watchPath, stats);
|
||||
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
|
||||
if (this.fsw.closed) return;
|
||||
|
||||
// preserve this symlink's target path
|
||||
if (targetPath !== undefined) {
|
||||
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
|
||||
}
|
||||
} else {
|
||||
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
||||
}
|
||||
ready();
|
||||
|
||||
this.fsw._addPathCloser(path, closer);
|
||||
return false;
|
||||
|
||||
} catch (error) {
|
||||
if (this.fsw._handleError(error)) {
|
||||
ready();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NodeFsHandler;
|
Reference in New Issue
Block a user