140 lines
4.7 KiB
JavaScript
140 lines
4.7 KiB
JavaScript
/* IMPORT */
|
|
import { RENAME_TIMEOUT } from './constants.js';
|
|
import { FileType, TargetEvent } from './enums.js';
|
|
import Utils from './utils.js';
|
|
import WatcherLocksResolver from './watcher_locks_resolver.js';
|
|
/* MAIN */
|
|
//TODO: Use a better name for this thing, maybe "RenameDetector"
|
|
class WatcherLocker {
|
|
/* CONSTRUCTOR */
|
|
constructor(watcher) {
|
|
this._watcher = watcher;
|
|
this.reset();
|
|
}
|
|
/* API */
|
|
getLockAdd(config, timeout = RENAME_TIMEOUT) {
|
|
const { ino, targetPath, events, locks } = config;
|
|
const emit = () => {
|
|
const otherPath = this._watcher._poller.paths.find(ino || -1, path => path !== targetPath); // Maybe this is actually a rename in a case-insensitive filesystem
|
|
if (otherPath && otherPath !== targetPath) {
|
|
if (Utils.fs.getRealPath(targetPath, true) === otherPath)
|
|
return; //TODO: This seems a little too special-casey
|
|
this._watcher.event(events.rename, otherPath, targetPath);
|
|
}
|
|
else {
|
|
this._watcher.event(events.add, targetPath);
|
|
}
|
|
};
|
|
if (!ino)
|
|
return emit();
|
|
const cleanup = () => {
|
|
locks.add.delete(ino);
|
|
WatcherLocksResolver.remove(free);
|
|
};
|
|
const free = () => {
|
|
cleanup();
|
|
emit();
|
|
};
|
|
WatcherLocksResolver.add(free, timeout);
|
|
const resolve = () => {
|
|
const unlink = locks.unlink.get(ino);
|
|
if (!unlink)
|
|
return; // No matching "unlink" lock found, skipping
|
|
cleanup();
|
|
const targetPathPrev = unlink();
|
|
if (targetPath === targetPathPrev) {
|
|
if (events.change) {
|
|
if (this._watcher._poller.stats.has(targetPath)) {
|
|
this._watcher.event(events.change, targetPath);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this._watcher.event(events.rename, targetPathPrev, targetPath);
|
|
}
|
|
};
|
|
locks.add.set(ino, resolve);
|
|
resolve();
|
|
}
|
|
getLockUnlink(config, timeout = RENAME_TIMEOUT) {
|
|
const { ino, targetPath, events, locks } = config;
|
|
const emit = () => {
|
|
this._watcher.event(events.unlink, targetPath);
|
|
};
|
|
if (!ino)
|
|
return emit();
|
|
const cleanup = () => {
|
|
locks.unlink.delete(ino);
|
|
WatcherLocksResolver.remove(free);
|
|
};
|
|
const free = () => {
|
|
cleanup();
|
|
emit();
|
|
};
|
|
WatcherLocksResolver.add(free, timeout);
|
|
const overridden = () => {
|
|
cleanup();
|
|
return targetPath;
|
|
};
|
|
locks.unlink.set(ino, overridden);
|
|
locks.add.get(ino)?.();
|
|
}
|
|
getLockTargetAdd(targetPath, timeout) {
|
|
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.ADD, FileType.FILE);
|
|
return this.getLockAdd({
|
|
ino,
|
|
targetPath,
|
|
events: WatcherLocker.FILE_EVENTS,
|
|
locks: this._locksFile
|
|
}, timeout);
|
|
}
|
|
getLockTargetAddDir(targetPath, timeout) {
|
|
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.ADD_DIR, FileType.DIR);
|
|
return this.getLockAdd({
|
|
ino,
|
|
targetPath,
|
|
events: WatcherLocker.DIR_EVENTS,
|
|
locks: this._locksDir
|
|
}, timeout);
|
|
}
|
|
getLockTargetUnlink(targetPath, timeout) {
|
|
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.UNLINK, FileType.FILE);
|
|
return this.getLockUnlink({
|
|
ino,
|
|
targetPath,
|
|
events: WatcherLocker.FILE_EVENTS,
|
|
locks: this._locksFile
|
|
}, timeout);
|
|
}
|
|
getLockTargetUnlinkDir(targetPath, timeout) {
|
|
const ino = this._watcher._poller.getIno(targetPath, TargetEvent.UNLINK_DIR, FileType.DIR);
|
|
return this.getLockUnlink({
|
|
ino,
|
|
targetPath,
|
|
events: WatcherLocker.DIR_EVENTS,
|
|
locks: this._locksDir
|
|
}, timeout);
|
|
}
|
|
reset() {
|
|
this._locksAdd = new Map();
|
|
this._locksAddDir = new Map();
|
|
this._locksUnlink = new Map();
|
|
this._locksUnlinkDir = new Map();
|
|
this._locksDir = { add: this._locksAddDir, unlink: this._locksUnlinkDir };
|
|
this._locksFile = { add: this._locksAdd, unlink: this._locksUnlink };
|
|
}
|
|
}
|
|
WatcherLocker.DIR_EVENTS = {
|
|
add: TargetEvent.ADD_DIR,
|
|
rename: TargetEvent.RENAME_DIR,
|
|
unlink: TargetEvent.UNLINK_DIR
|
|
};
|
|
WatcherLocker.FILE_EVENTS = {
|
|
add: TargetEvent.ADD,
|
|
change: TargetEvent.CHANGE,
|
|
rename: TargetEvent.RENAME,
|
|
unlink: TargetEvent.UNLINK
|
|
};
|
|
/* EXPORT */
|
|
export default WatcherLocker;
|