[0c6b92a] | 1 | # Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar)
|
---|
| 2 |
|
---|
| 3 | > Minimal and efficient cross-platform file watching library
|
---|
| 4 |
|
---|
| 5 | ## Why?
|
---|
| 6 |
|
---|
| 7 | There are many reasons to prefer Chokidar to raw fs.watch / fs.watchFile in 2024:
|
---|
| 8 |
|
---|
| 9 | - Events are properly reported
|
---|
| 10 | - macOS events report filenames
|
---|
| 11 | - events are not reported twice
|
---|
| 12 | - changes are reported as add / change / unlink instead of useless `rename`
|
---|
| 13 | - Atomic writes are supported, using `atomic` option
|
---|
| 14 | - Some file editors use them
|
---|
| 15 | - Chunked writes are supported, using `awaitWriteFinish` option
|
---|
| 16 | - Large files are commonly written in chunks
|
---|
| 17 | - File / dir filtering is supported
|
---|
| 18 | - Symbolic links are supported
|
---|
| 19 | - Recursive watching is always supported, instead of partial when using raw events
|
---|
| 20 | - Includes a way to limit recursion depth
|
---|
| 21 |
|
---|
| 22 | Chokidar relies on the Node.js core `fs` module, but when using
|
---|
| 23 | `fs.watch` and `fs.watchFile` for watching, it normalizes the events it
|
---|
| 24 | receives, often checking for truth by getting file stats and/or dir contents.
|
---|
| 25 | The `fs.watch`-based implementation is the default, which
|
---|
| 26 | avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
|
---|
| 27 | watchers recursively for everything within scope of the paths that have been
|
---|
| 28 | specified, so be judicious about not wasting system resources by watching much
|
---|
| 29 | more than needed. For some cases, `fs.watchFile`, which utilizes polling and uses more resources, is used.
|
---|
| 30 |
|
---|
| 31 | Made for [Brunch](https://brunch.io/) in 2012,
|
---|
| 32 | it is now used in [~30 million repositories](https://www.npmjs.com/browse/depended/chokidar) and
|
---|
| 33 | has proven itself in production environments.
|
---|
| 34 |
|
---|
| 35 | **Sep 2024 update:** v4 is out! It decreases dependency count from 13 to 1, removes
|
---|
| 36 | support for globs, adds support for ESM / Common.js modules, and bumps minimum node.js version from v8 to v14.
|
---|
| 37 | Check out [upgrading](#upgrading).
|
---|
| 38 |
|
---|
| 39 | ## Getting started
|
---|
| 40 |
|
---|
| 41 | Install with npm:
|
---|
| 42 |
|
---|
| 43 | ```sh
|
---|
| 44 | npm install chokidar
|
---|
| 45 | ```
|
---|
| 46 |
|
---|
| 47 | Use it in your code:
|
---|
| 48 |
|
---|
| 49 | ```javascript
|
---|
| 50 | import chokidar from 'chokidar';
|
---|
| 51 |
|
---|
| 52 | // One-liner for current directory
|
---|
| 53 | chokidar.watch('.').on('all', (event, path) => {
|
---|
| 54 | console.log(event, path);
|
---|
| 55 | });
|
---|
| 56 |
|
---|
| 57 |
|
---|
| 58 | // Extended options
|
---|
| 59 | // ----------------
|
---|
| 60 |
|
---|
| 61 | // Initialize watcher.
|
---|
| 62 | const watcher = chokidar.watch('file, dir, or array', {
|
---|
| 63 | ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
|
---|
| 64 | persistent: true
|
---|
| 65 | });
|
---|
| 66 |
|
---|
| 67 | // Something to use when events are received.
|
---|
| 68 | const log = console.log.bind(console);
|
---|
| 69 | // Add event listeners.
|
---|
| 70 | watcher
|
---|
| 71 | .on('add', path => log(`File ${path} has been added`))
|
---|
| 72 | .on('change', path => log(`File ${path} has been changed`))
|
---|
| 73 | .on('unlink', path => log(`File ${path} has been removed`));
|
---|
| 74 |
|
---|
| 75 | // More possible events.
|
---|
| 76 | watcher
|
---|
| 77 | .on('addDir', path => log(`Directory ${path} has been added`))
|
---|
| 78 | .on('unlinkDir', path => log(`Directory ${path} has been removed`))
|
---|
| 79 | .on('error', error => log(`Watcher error: ${error}`))
|
---|
| 80 | .on('ready', () => log('Initial scan complete. Ready for changes'))
|
---|
| 81 | .on('raw', (event, path, details) => { // internal
|
---|
| 82 | log('Raw event info:', event, path, details);
|
---|
| 83 | });
|
---|
| 84 |
|
---|
| 85 | // 'add', 'addDir' and 'change' events also receive stat() results as second
|
---|
| 86 | // argument when available: https://nodejs.org/api/fs.html#fs_class_fs_stats
|
---|
| 87 | watcher.on('change', (path, stats) => {
|
---|
| 88 | if (stats) console.log(`File ${path} changed size to ${stats.size}`);
|
---|
| 89 | });
|
---|
| 90 |
|
---|
| 91 | // Watch new files.
|
---|
| 92 | watcher.add('new-file');
|
---|
| 93 | watcher.add(['new-file-2', 'new-file-3']);
|
---|
| 94 |
|
---|
| 95 | // Get list of actual paths being watched on the filesystem
|
---|
| 96 | let watchedPaths = watcher.getWatched();
|
---|
| 97 |
|
---|
| 98 | // Un-watch some files.
|
---|
| 99 | await watcher.unwatch('new-file');
|
---|
| 100 |
|
---|
| 101 | // Stop watching. The method is async!
|
---|
| 102 | await watcher.close().then(() => console.log('closed'));
|
---|
| 103 |
|
---|
| 104 | // Full list of options. See below for descriptions.
|
---|
| 105 | // Do not use this example!
|
---|
| 106 | chokidar.watch('file', {
|
---|
| 107 | persistent: true,
|
---|
| 108 |
|
---|
| 109 | // ignore .txt files
|
---|
| 110 | ignored: (file) => file.endsWith('.txt'),
|
---|
| 111 | // watch only .txt files
|
---|
| 112 | // ignored: (file, _stats) => _stats?.isFile() && !file.endsWith('.txt'),
|
---|
| 113 |
|
---|
| 114 | awaitWriteFinish: true, // emit single event when chunked writes are completed
|
---|
[79a0317] | 115 | atomic: true, // emit proper events when "atomic writes" (mv _tmp file) are used
|
---|
[0c6b92a] | 116 |
|
---|
| 117 | // The options also allow specifying custom intervals in ms
|
---|
| 118 | // awaitWriteFinish: {
|
---|
| 119 | // stabilityThreshold: 2000,
|
---|
| 120 | // pollInterval: 100
|
---|
| 121 | // },
|
---|
| 122 | // atomic: 100,
|
---|
[79a0317] | 123 |
|
---|
[0c6b92a] | 124 | interval: 100,
|
---|
| 125 | binaryInterval: 300,
|
---|
| 126 |
|
---|
| 127 | cwd: '.',
|
---|
| 128 | depth: 99,
|
---|
| 129 |
|
---|
| 130 | followSymlinks: true,
|
---|
| 131 | ignoreInitial: false,
|
---|
| 132 | ignorePermissionErrors: false,
|
---|
| 133 | usePolling: false,
|
---|
| 134 | alwaysStat: false,
|
---|
| 135 | });
|
---|
| 136 |
|
---|
| 137 | ```
|
---|
| 138 |
|
---|
| 139 | `chokidar.watch(paths, [options])`
|
---|
| 140 |
|
---|
| 141 | * `paths` (string or array of strings). Paths to files, dirs to be watched
|
---|
| 142 | recursively.
|
---|
| 143 | * `options` (object) Options object as defined below:
|
---|
| 144 |
|
---|
| 145 | #### Persistence
|
---|
| 146 |
|
---|
| 147 | * `persistent` (default: `true`). Indicates whether the process
|
---|
| 148 | should continue to run as long as files are being watched.
|
---|
| 149 |
|
---|
| 150 | #### Path filtering
|
---|
| 151 |
|
---|
| 152 | * `ignored` function, regex, or path. Defines files/paths to be ignored.
|
---|
| 153 | The whole relative or absolute path is tested, not just filename. If a function with two arguments
|
---|
| 154 | is provided, it gets called twice per path - once with a single argument (the path), second
|
---|
| 155 | time with two arguments (the path and the
|
---|
| 156 | [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
|
---|
| 157 | object of that path).
|
---|
| 158 | * `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` events are also emitted for matching paths while
|
---|
| 159 | instantiating the watching as chokidar discovers these file paths (before the `ready` event).
|
---|
| 160 | * `followSymlinks` (default: `true`). When `false`, only the
|
---|
| 161 | symlinks themselves will be watched for changes instead of following
|
---|
| 162 | the link references and bubbling events through the link's path.
|
---|
| 163 | * `cwd` (no default). The base directory from which watch `paths` are to be
|
---|
| 164 | derived. Paths emitted with events will be relative to this.
|
---|
| 165 |
|
---|
| 166 | #### Performance
|
---|
| 167 |
|
---|
| 168 | * `usePolling` (default: `false`).
|
---|
| 169 | Whether to use fs.watchFile (backed by polling), or fs.watch. If polling
|
---|
| 170 | leads to high CPU utilization, consider setting this to `false`. It is
|
---|
| 171 | typically necessary to **set this to `true` to successfully watch files over
|
---|
| 172 | a network**, and it may be necessary to successfully watch files in other
|
---|
| 173 | non-standard situations. Setting to `true` explicitly on MacOS overrides the
|
---|
| 174 | `useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable
|
---|
| 175 | to true (1) or false (0) in order to override this option.
|
---|
| 176 | * _Polling-specific settings_ (effective when `usePolling: true`)
|
---|
| 177 | * `interval` (default: `100`). Interval of file system polling, in milliseconds. You may also
|
---|
| 178 | set the CHOKIDAR_INTERVAL env variable to override this option.
|
---|
| 179 | * `binaryInterval` (default: `300`). Interval of file system
|
---|
| 180 | polling for binary files.
|
---|
| 181 | ([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
|
---|
| 182 | * `alwaysStat` (default: `false`). If relying upon the
|
---|
| 183 | [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
|
---|
| 184 | object that may get passed with `add`, `addDir`, and `change` events, set
|
---|
| 185 | this to `true` to ensure it is provided even in cases where it wasn't
|
---|
| 186 | already available from the underlying watch events.
|
---|
| 187 | * `depth` (default: `undefined`). If set, limits how many levels of
|
---|
| 188 | subdirectories will be traversed.
|
---|
| 189 | * `awaitWriteFinish` (default: `false`).
|
---|
| 190 | By default, the `add` event will fire when a file first appears on disk, before
|
---|
| 191 | the entire file has been written. Furthermore, in some cases some `change`
|
---|
| 192 | events will be emitted while the file is being written. In some cases,
|
---|
| 193 | especially when watching for large files there will be a need to wait for the
|
---|
| 194 | write operation to finish before responding to a file creation or modification.
|
---|
| 195 | Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size,
|
---|
| 196 | holding its `add` and `change` events until the size does not change for a
|
---|
| 197 | configurable amount of time. The appropriate duration setting is heavily
|
---|
| 198 | dependent on the OS and hardware. For accurate detection this parameter should
|
---|
| 199 | be relatively high, making file watching much less responsive.
|
---|
| 200 | Use with caution.
|
---|
| 201 | * *`options.awaitWriteFinish` can be set to an object in order to adjust
|
---|
| 202 | timing params:*
|
---|
| 203 | * `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in
|
---|
| 204 | milliseconds for a file size to remain constant before emitting its event.
|
---|
| 205 | * `awaitWriteFinish.pollInterval` (default: 100). File size polling interval, in milliseconds.
|
---|
| 206 |
|
---|
| 207 | #### Errors
|
---|
| 208 |
|
---|
| 209 | * `ignorePermissionErrors` (default: `false`). Indicates whether to watch files
|
---|
| 210 | that don't have read permissions if possible. If watching fails due to `EPERM`
|
---|
| 211 | or `EACCES` with this set to `true`, the errors will be suppressed silently.
|
---|
| 212 | * `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`).
|
---|
| 213 | Automatically filters out artifacts that occur when using editors that use
|
---|
| 214 | "atomic writes" instead of writing directly to the source file. If a file is
|
---|
| 215 | re-added within 100 ms of being deleted, Chokidar emits a `change` event
|
---|
| 216 | rather than `unlink` then `add`. If the default of 100 ms does not work well
|
---|
| 217 | for you, you can override it by setting `atomic` to a custom value, in
|
---|
| 218 | milliseconds.
|
---|
| 219 |
|
---|
| 220 | ### Methods & Events
|
---|
| 221 |
|
---|
| 222 | `chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:
|
---|
| 223 |
|
---|
| 224 | * `.add(path / paths)`: Add files, directories for tracking.
|
---|
| 225 | Takes an array of strings or just one string.
|
---|
| 226 | * `.on(event, callback)`: Listen for an FS event.
|
---|
| 227 | Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
|
---|
| 228 | `raw`, `error`.
|
---|
| 229 | Additionally `all` is available which gets emitted with the underlying event
|
---|
| 230 | name and path for every event other than `ready`, `raw`, and `error`. `raw` is internal, use it carefully.
|
---|
| 231 | * `.unwatch(path / paths)`: Stop watching files or directories.
|
---|
| 232 | Takes an array of strings or just one string.
|
---|
| 233 | * `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise. Use with `await` to ensure bugs don't happen.
|
---|
| 234 | * `.getWatched()`: Returns an object representing all the paths on the file
|
---|
| 235 | system being watched by this `FSWatcher` instance. The object's keys are all the
|
---|
| 236 | directories (using absolute paths unless the `cwd` option was used), and the
|
---|
| 237 | values are arrays of the names of the items contained in each directory.
|
---|
| 238 |
|
---|
[79a0317] | 239 | ### CLI
|
---|
[0c6b92a] | 240 |
|
---|
[79a0317] | 241 | Check out third party [chokidar-cli](https://github.com/open-cli-tools/chokidar-cli),
|
---|
| 242 | which allows to execute a command on each change, or get a stdio stream of change events.
|
---|
[0c6b92a] | 243 |
|
---|
| 244 | ## Troubleshooting
|
---|
| 245 |
|
---|
[79a0317] | 246 | Sometimes, Chokidar runs out of file handles, causing `EMFILE` and `ENOSP` errors:
|
---|
| 247 |
|
---|
| 248 | * `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
|
---|
| 249 | * `Error: watch /home/ ENOSPC`
|
---|
| 250 |
|
---|
| 251 | There are two things that can cause it.
|
---|
| 252 |
|
---|
| 253 | 1. Exhausted file handles for generic fs operations
|
---|
| 254 | - Can be solved by using [graceful-fs](https://www.npmjs.com/package/graceful-fs),
|
---|
| 255 | which can monkey-patch native `fs` module used by chokidar: `let fs = require('fs'); let grfs = require('graceful-fs'); grfs.gracefulify(fs);`
|
---|
| 256 | - Can also be solved by tuning OS: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`.
|
---|
| 257 | 2. Exhausted file handles for `fs.watch`
|
---|
| 258 | - Can't seem to be solved by graceful-fs or OS tuning
|
---|
| 259 | - It's possible to start using `usePolling: true`, which will switch backend to resource-intensive `fs.watchFile`
|
---|
| 260 |
|
---|
| 261 | All fsevents-related issues (`WARN optional dep failed`, `fsevents is not a constructor`) are solved by upgrading to v4+.
|
---|
[0c6b92a] | 262 |
|
---|
| 263 | ## Changelog
|
---|
| 264 |
|
---|
| 265 | - **v4 (Sep 2024):** remove glob support and bundled fsevents. Decrease dependency count from 13 to 1. Rewrite in typescript. Bumps minimum node.js requirement to v14+
|
---|
| 266 | - **v3 (Apr 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16+.
|
---|
| 267 | - **v2 (Dec 2017):** globs are now posix-style-only. Tons of bugfixes.
|
---|
| 268 | - **v1 (Apr 2015):** glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
|
---|
| 269 | - **v0.1 (Apr 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)
|
---|
| 270 |
|
---|
| 271 | ### Upgrading
|
---|
| 272 |
|
---|
| 273 | If you've used globs before and want do replicate the functionality with v4:
|
---|
| 274 |
|
---|
| 275 | ```js
|
---|
| 276 | // v3
|
---|
| 277 | chok.watch('**/*.js');
|
---|
| 278 | chok.watch("./directory/**/*");
|
---|
| 279 |
|
---|
| 280 | // v4
|
---|
| 281 | chok.watch('.', {
|
---|
| 282 | ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
|
---|
| 283 | });
|
---|
| 284 | chok.watch('./directory');
|
---|
| 285 |
|
---|
| 286 | // other way
|
---|
| 287 | import { glob } from 'node:fs/promises';
|
---|
[79a0317] | 288 | const watcher = watch(await Array.fromAsync(glob('**/*.js')));
|
---|
[0c6b92a] | 289 |
|
---|
| 290 | // unwatching
|
---|
| 291 | // v3
|
---|
| 292 | chok.unwatch('**/*.js');
|
---|
| 293 | // v4
|
---|
| 294 | chok.unwatch(await glob('**/*.js'));
|
---|
| 295 | ```
|
---|
| 296 |
|
---|
| 297 | ## Also
|
---|
| 298 |
|
---|
| 299 | Why was chokidar named this way? What's the meaning behind it?
|
---|
| 300 |
|
---|
| 301 | >Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four). This word is also used in other languages like Urdu as (چوکیدار) which is widely used in Pakistan and India.
|
---|
| 302 |
|
---|
| 303 | ## License
|
---|
| 304 |
|
---|
| 305 | MIT (c) Paul Miller (<https://paulmillr.com>), see [LICENSE](LICENSE) file.
|
---|