Node.js library for watching deep nested directories with intelligent event deduplication and debouncing.
- 🔍 Recursive directory watching: auto and recursive watches subdirectories
- 🔄 Dynamic directory management: auto adds new directories and removes deleted ones
- ⚡ Event deduplication: prevents duplicate events for the same file changes
- 🎯 Debounced events: batches multiple changes within a configurable timeout
- 📦 Zero dependencies, uses only Node.js built-in modules
- 🎭 EventEmitter API: simple event-driven interface
npm i metawatchconst metawatch = require('metawatch');
const watcher = new metawatch.DirectoryWatcher({ timeout: 200 });
watcher.watch('/path/to/directory');
watcher.on('change', (fileName) => {
console.log('File changed:', fileName);
});
watcher.on('delete', (fileName) => {
console.log('File deleted:', fileName);
});new DirectoryWatcher(options);Options:
timeout(number, optional): Debounce timeout in milliseconds. Default:5000
Methods:
watch(targetPath)- Start watching directory recursivelyunwatch(path)- Stop watching directory
Events:
change- File created/modified (fileName)delete- File deleted (fileName)before- Before processing batch (changes)after- After processing batch (changes)
const metawatch = require('metawatch');
const watcher = new metawatch.DirectoryWatcher({ timeout: 500 });
watcher.watch('./src');
watcher.on('change', (fileName) => {
console.log(`File changed: ${fileName}`);
// Trigger rebuild, reload, etc.
});
watcher.on('delete', (fileName) => {
console.log(`File deleted: ${fileName}`);
// Clean up references, etc.
});const metawatch = require('metawatch');
const fs = require('fs');
const watcher = new metawatch.DirectoryWatcher({ timeout: 1000 });
const backupQueue = new Set();
watcher.watch('/important/documents');
watcher.on('change', (fileName) => {
console.log(`File modified: ${fileName}`);
backupQueue.add(fileName);
});
watcher.on('delete', (fileName) => {
console.log(`File deleted: ${fileName}`);
// Remove from backup if it exists
backupQueue.delete(fileName);
});
watcher.on('after', (changes) => {
if (backupQueue.size > 0) {
console.log(`Backing up ${backupQueue.size} files...`);
// Process backup queue
backupQueue.clear();
}
});const metawatch = require('metawatch');
const path = require('path');
const watcher = new metawatch.DirectoryWatcher({ timeout: 200 });
const directories = ['./src', './tests', './docs', './config'];
directories.forEach((dir) => {
if (fs.existsSync(dir)) {
watcher.watch(path.resolve(dir));
console.log(`Watching: ${dir}`);
}
});
watcher.on('change', (fileName) => {
const relativePath = path.relative(process.cwd(), fileName);
console.log(`Changed: ${relativePath}`);
});
watcher.on('before', (changes) => {
console.log(`Processing ${changes.length} changes...`);
});
watcher.on('after', (changes) => {
console.log(`Completed processing ${changes.length} changes`);
});import { DirectoryWatcher, DirectoryWatcherOptions } from 'metawatch';
const options: DirectoryWatcherOptions = {
timeout: 500,
};
const watcher = new DirectoryWatcher(options);
watcher.watch('./src');
watcher.on('change', (fileName: string) => {
console.log(`File changed: ${fileName}`);
});
watcher.on('delete', (fileName: string) => {
console.log(`File deleted: ${fileName}`);
});const watcher = new metawatch.DirectoryWatcher();
watcher.on('error', (error) => {
console.error('Watcher error:', error);
});
try {
watcher.watch('/restricted/path');
} catch (error) {
console.error('Failed to watch directory:', error.message);
}- Timur Shemsedinov [email protected]
- See github for full contributors list
Copyright (c) 2020-2025 Metarhia contributors. Metawatch is MIT licensed. Metawatch is a part of Metarhia technology stack.