Skip to content

Commit

Permalink
Add exclude files, exts, paths and add new events
Browse files Browse the repository at this point in the history
  • Loading branch information
timursevimli committed Nov 10, 2023
1 parent abe77ce commit 72e9f7e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 31 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
```js
const metawatch = require('metawatch');

const watcher = new metawatch.DirectoryWatcher({ timeout: 200 });
const options = {
timeout: 200,
excludes: {
dirs: ['.git'],
files: ['package.json'],
exts: ['ts', 'md'],
},
};

const watcher = new metawatch.DirectoryWatcher(options);
watcher.watch('/home/marcus/Downloads');
watcher.watch('/home/marcus/Documents');

Expand Down
89 changes: 59 additions & 30 deletions metawatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,38 @@ const WATCH_TIMEOUT = 5000;
class DirectoryWatcher extends EventEmitter {
constructor(options = {}) {
super();
this.watchers = new Map();
const { dirs = [], files = [], exts = [] } = options.excludes || {};
const { timeout = WATCH_TIMEOUT } = options;
this.timeout = timeout;
this.watchers = new Map();
this.excludeExts = new Set(exts);
this.excludePaths = new Set(dirs);
this.excludeFiles = new Set(files);
this.timer = null;
this.queue = new Map();
}

isExcludedFile(filePath) {
const { excludeExts, excludeFiles } = this;
const { ext, base, name } = path.parse(filePath);
const extIsExclude = excludeExts.has(ext.slice(1));
if (extIsExclude) return true;
return excludeFiles.has(name) || excludeFiles.has(base);
}

isExcludedDir(dirPath) {
const { excludePaths } = this;
const dirName = path.basename(dirPath);
return excludePaths.has(dirName) || excludePaths.has(dirPath);
}

post(event, filePath) {
if (this.timer) clearTimeout(this.timer);
this.queue.set(filePath, event);
if (this.timeout === 0) return void this.sendQueue();
const events = this.queue.get(filePath);
if (events) events.add(event);
else this.queue.set(filePath, new Set(event));
this.timer = setTimeout(() => {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
this.timer = null;
this.sendQueue();
}, this.timeout);
}
Expand All @@ -34,49 +50,62 @@ class DirectoryWatcher extends EventEmitter {
const queue = [...this.queue.entries()];
this.queue.clear();
this.emit('before', queue);
for (const [filePath, event] of queue) {
this.emit(event, filePath);
for (const [filePath, events] of queue) {
for (const event of events) {
this.emit(event, filePath);
}
}
this.emit('after', queue);
}

watchDirectory(targetPath) {
if (this.watchers.get(targetPath)) return;
const watcher = fs.watch(targetPath, (event, fileName) => {
const target = targetPath.endsWith(path.sep + fileName);
const filePath = target ? targetPath : path.join(targetPath, fileName);
watchDirectory(dirPath) {
if (this.watchers.has(dirPath)) return;
const watcher = fs.watch(dirPath);
watcher.on('error', () => void this.unwatch(dirPath));
watcher.on('change', (_, fileName) => {
const target = dirPath.endsWith(path.sep + fileName);
const filePath = target ? dirPath : path.join(dirPath, fileName);
if (this.isExcludedFile(filePath)) return;
this.post('*', filePath);
fs.stat(filePath, (err, stats) => {
if (err) {
this.unwatch(filePath);
return void this.post('delete', filePath);
const keys = [...this.watchers.keys()];
const event = keys.includes(filePath) ? 'unlinkDir' : 'unlink';
this.post(event, filePath);
return void this.unwatch(filePath);
}
if (stats.isDirectory()) this.watch(filePath);
this.post('change', filePath);
});
});
this.watchers.set(targetPath, watcher);
this.watchers.set(dirPath, watcher);
}

watch(targetPath) {
const watcher = this.watchers.get(targetPath);
if (watcher) return;
unwatch(filePath) {
const watcher = this.watchers.get(filePath);
if (!watcher) return;
watcher.close();
this.watchers.delete(filePath);
}

watch(targetPath = process.cwd()) {
if (this.isExcludedDir(targetPath)) return this;
fs.readdir(targetPath, { withFileTypes: true }, (err, files) => {
if (err) return;
if (err) return this;
for (const file of files) {
if (file.isDirectory()) {
const dirPath = path.join(targetPath, file.name);
this.watch(dirPath);
}
if (!file.isDirectory()) continue;
const dirPath = path.join(targetPath, file.name);
this.watch(dirPath);
}
this.watchDirectory(targetPath);
return this;
});
return this;
}

unwatch(path) {
const watcher = this.watchers.get(path);
if (!watcher) return;
watcher.close();
this.watchers.delete(path);
stop(filePath) {
if (filePath) return void this.unwatch(filePath);
for (const [filePath] of this.watchers) this.unwatch(filePath);
}
}

Expand Down

0 comments on commit 72e9f7e

Please sign in to comment.