Fast, small, and reliable file system watcher with multiple monitoring strategies.
—
Different watcher implementations for various use cases and environments, all providing a consistent EventEmitter-based API with the same event types and method signatures.
Uses Node.js built-in fs.watch for efficient native file system watching.
/**
* Default fs.watch-based watcher
* @param {string} dir - Directory to watch
* @param {Object} options - Watcher options
*/
class NodeWatcher extends EventEmitter {
constructor(dir, options);
/**
* Stop watching and clean up resources
* @param {Function} callback - Optional callback called when closed
*/
close(callback);
}Best for:
Limitations:
Usage Example:
const { NodeWatcher } = require('sane');
const watcher = new NodeWatcher('/path/to/watch', {
glob: ['**/*.js', '**/*.json'],
ignored: ['node_modules/**']
});
watcher.on('ready', () => console.log('NodeWatcher ready'));
watcher.on('change', (file) => console.log('Changed:', file));Uses fs.watchFile polling for maximum compatibility across all environments.
/**
* Polling-based watcher using fs.watchFile
* @param {string} dir - Directory to watch
* @param {Object} options - Watcher options including interval
*/
class PollWatcher extends EventEmitter {
constructor(dir, options);
/**
* Stop watching and clean up resources
* @param {Function} callback - Optional callback called when closed
*/
close(callback);
}Additional Options:
interface PollWatcherOptions {
/** Polling interval in milliseconds (default: 100) */
interval?: number;
}Best for:
Limitations:
Usage Example:
const { PollWatcher } = require('sane');
const watcher = new PollWatcher('/path/to/watch', {
interval: 1000, // Poll every second
glob: ['**/*.css', '**/*.scss']
});
watcher.on('ready', () => console.log('PollWatcher ready'));
watcher.on('add', (file) => console.log('Added:', file));Uses Facebook's Watchman for highly efficient watching of large directory trees.
/**
* Facebook Watchman-based watcher
* @param {string} dir - Directory to watch
* @param {Object} options - Watcher options including watchmanPath
*/
class WatchmanWatcher extends EventEmitter {
constructor(dir, options);
/**
* Stop watching and clean up resources
* @param {Function} callback - Optional callback called when closed
*/
close(callback);
/**
* Create Watchman subscription options
* @returns {Object} Watchman subscription configuration
*/
createOptions();
/**
* Handle error events from Watchman
* @param {Error} error - Error from Watchman service
*/
handleErrorEvent(error);
/**
* Handle change events from Watchman
* @param {Object} resp - Watchman response object
*/
handleChangeEvent(resp);
}Additional Options:
interface WatchmanOptions {
/** Custom path to Watchman binary */
watchmanPath?: string;
}Best for:
Limitations:
Usage Example:
const { WatchmanWatcher } = require('sane');
const watcher = new WatchmanWatcher('/large/project', {
watchmanPath: '/usr/local/bin/watchman',
glob: ['src/**/*.js', 'test/**/*.js']
});
watcher.on('ready', () => console.log('Watchman ready'));
watcher.on('all', (type, file) => console.log(type, ':', file));Uses the external watchexec tool for cross-platform file watching.
/**
* Watchexec-based watcher using external watchexec process
* @param {string} dir - Directory to watch
* @param {Object} options - Watcher options
*/
class WatchexecWatcher extends EventEmitter {
constructor(dir, options);
/**
* Stop watching and clean up resources
* @param {Function} callback - Optional callback called when closed
*/
close(callback);
/**
* Emit filesystem events to the watcher
* @param {string} type - Event type ('change', 'add', 'delete')
* @param {string} file - File path relative to watched directory
* @param {fs.Stats|null} stat - File statistics (null for delete events)
*/
emitEvent(type, file, stat);
}
/**
* Static message handler for processing watchexec output
* @static
*/
WatchexecWatcher._messageHandler;Best for:
Limitations:
Usage Example:
const { WatchexecWatcher } = require('sane');
const watcher = new WatchexecWatcher('/path/to/watch', {
ignored: ['*.tmp', 'build/**']
});
watcher.on('ready', () => console.log('Watchexec ready'));
watcher.on('delete', (file) => console.log('Deleted:', file));All watcher classes emit the same events with identical signatures:
interface WatcherEvents {
/** Emitted when watcher is ready to detect changes */
'ready': () => void;
/** Emitted when a file is modified */
'change': (filepath: string, root: string, stat: fs.Stats) => void;
/** Emitted when a file or directory is added */
'add': (filepath: string, root: string, stat: fs.Stats) => void;
/** Emitted when a file or directory is deleted (stat parameter is null) */
'delete': (filepath: string, root: string, stat: null) => void;
/** Emitted for all change types */
'all': (eventType: 'change'|'add'|'delete', filepath: string, root: string, stat?: fs.Stats) => void;
/** Emitted when errors occur */
'error': (error: Error) => void;
}Choose the appropriate watcher mode based on your environment and requirements:
All watchers handle common filesystem errors gracefully:
ENOENT errors (file not found) are generally ignoredEPERM errors on Windows are handled appropriatelywatcher.on('error', (error) => {
if (error.code === 'ENOENT') {
// File was deleted, usually safe to ignore
} else {
console.error('Watcher error:', error);
}
});Install with Tessl CLI
npx tessl i tessl/npm-sane