Lightweight file watcher weighing about 300 LOC. No runtime dependencies and no platform specific code. Works everywhere. Monitors changes in the file system by polling. Has no config files.
npm install filewatcheror with yarn:
yarn add filewatcherWatch a list of files and directories:
import { Filewatcher } from 'filewatcher';
const filewatcher = new Filewatcher(['lib/', 'package.json']);
await filewatcher.watch((changes) => {
Object.entries(changes).forEach(([filename, event]) => {
console.log(`${filename} ${event}`);
});
});Watch a single directory, for changes in all files and subdirectories:
const filewatcher = new Filewatcher('lib/');
await filewatcher.watch((changes) => {
// Handle changes
});Notice that the previous is equivalent to the following:
const filewatcher = new Filewatcher('lib/**/*');
await filewatcher.watch((changes) => {
// Handle changes
});Watch files and directories in the given directory - and not in subdirectories:
const filewatcher = new Filewatcher('lib/*');
await filewatcher.watch((changes) => {
// Handle changes
});Watch an absolute directory:
const filewatcher = new Filewatcher('/tmp/foo');
await filewatcher.watch((changes) => {
// Handle changes
});To detect if a file is updated, added or deleted:
const filewatcher = new Filewatcher(['README.md']);
await filewatcher.watch((changes) => {
Object.entries(changes).forEach(([filename, event]) => {
console.log(`File ${event}: ${filename}`);
});
});When a file is renamed and every option is enabled, it is detected as
a new file followed by a file deletion:
const filewatcher = new Filewatcher(['lib/'], { every: true });
await filewatcher.watch((changes) => {
Object.entries(changes).forEach(([filename, event]) => {
console.log(`File ${event}: ${filename}`);
});
});
// Rename from `old_test.ts` to `new_test.ts` will print:
// File created: /absolute/path/lib/new_test.ts
// File deleted: /absolute/path/lib/old_test.tsThe API takes some of the same options as the command line interface. To watch all files recursively except files that matches *.ts and only wait for 0.1 seconds between each scan:
const filewatcher = new Filewatcher('**/*.*', {
exclude: '**/*.ts',
interval: 0.1
});
await filewatcher.watch((changes) => {
Object.entries(changes).forEach(([filename, event]) => {
console.log(filename);
});
});Use patterns to match filenames in current directory and subdirectories. The pattern is not a regular expression; instead it follows rules similar to shell filename globbing. See glob documentation for syntax.
const filewatcher = new Filewatcher(['*.ts', '*.js']);
await filewatcher.watch((changes) => {
Object.entries(changes).forEach(([filename, event]) => {
console.log(`Updated ${filename}`);
});
});Start, pause, resume, stop, and finalize a running watch. This is particularly useful when the update block takes a while to process each file (e.g. sending over the network).
const filewatcher = new Filewatcher(['*.ts']);
const watchPromise = filewatcher.watch((changes) => {
console.log(changes);
});
// ... later
filewatcher.pause(); // block stops responding to file system changes
filewatcher.finalize(); // Ensure all file system changes made prior to
// pausing are handled.
// ...
filewatcher.resume(); // block begins responding again, but is not given
// changes made between #pause and #resume
// ...
filewatcher.stop(); // block stops responding to file system changes
// and takes a final snapshot of the file system
await watchPromise;
filewatcher.finalize(); // Ensure all file system changes made prior to
// ending the watch are handled.new Filewatcher(filenames: string | string[], options?: FilewatcherOptions)filenames: File or directory paths to watch. Can be a string or array of strings. Supports glob patterns.options: Optional configuration object.
interface FilewatcherOptions {
/** Exclude patterns (glob patterns) */
exclude?: string | string[];
/** Polling interval in seconds (default: 0.5) */
interval?: number;
/** Whether to trigger immediately on start (default: false) */
immediate?: boolean;
/** Whether to detect all changes including renames (default: false) */
every?: boolean;
/** Custom logger instance */
logger?: Logger;
}Start watching for file changes.
onUpdate: Optional callback function that receives file changes.
Pause the file watcher. Changes made while paused will not be detected until resumed.
Resume the file watcher. Must be called after pause().
Stop the file watcher.
Process any remaining changes. Useful for ensuring all changes are handled before stopping.
Get the list of files currently being watched.
Print version information to console.
The Filewatcher extends EventEmitter and emits the following events:
change: Emitted when file changes are detected.
filewatcher.on('change', (changes) => {
console.log('Files changed:', changes);
});type FileEvent = 'created' | 'updated' | 'deleted';
interface FileChange {
[filename: string]: FileEvent;
}
type OnUpdateCallback = (changes: FileChange) => void;npm run buildnpm testnpm run test:watchChangelog can be found in an adjacent file.
This project would not be where it is today without the generous help provided by people reporting issues and these contributors:
-
Thomas Flemming: Original author. Restart option. Exported variables.
-
Penn Taylor: Spinner displayed in the terminal and Start, pause, resume, stop, and finalize a running watch.
-
Franco Leonardo Bulgarelli: Support for absolute and globbed paths.
-
Kristoffer Roupé: Command line globbing.
-
Alexander Popov: Plugin system, daemon mode (CLI), tests improvements, code style improvements, many other fixes and improvements.
This package was initially inspired by Tom Lieber's blog posting (Web Archive version).
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with package.json, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
- Send me a pull request. Bonus points for topic branches.
Copyright (c) 2010 - 2025 Thomas Flemming. See LICENSE for details.