diff options
Diffstat (limited to 'node_modules/chokidar')
-rw-r--r-- | node_modules/chokidar/CHANGELOG.md | 290 | ||||
-rw-r--r-- | node_modules/chokidar/README.md | 293 | ||||
-rw-r--r-- | node_modules/chokidar/index.js | 743 | ||||
-rw-r--r-- | node_modules/chokidar/lib/fsevents-handler.js | 405 | ||||
-rw-r--r-- | node_modules/chokidar/lib/nodefs-handler.js | 488 | ||||
-rw-r--r-- | node_modules/chokidar/package.json | 92 |
6 files changed, 2311 insertions, 0 deletions
diff --git a/node_modules/chokidar/CHANGELOG.md b/node_modules/chokidar/CHANGELOG.md new file mode 100644 index 0000000..0900987 --- /dev/null +++ b/node_modules/chokidar/CHANGELOG.md @@ -0,0 +1,290 @@ +# Chokidar 2.0.2 (Feb 14, 2018) +* Allow semver range updates for upath dependency + +# Chokidar 2.0.1 (Feb 8, 2018) + * Fix #668 glob issue on Windows when using `ignore` and `cwd`. Thanks @remy! + * Fix #546 possible uncaught exception when using `awaitWriteFinish`. + Thanks @dsagal! + +# Chokidar 2.0.0 (Dec 29, 2017) +* Breaking: Upgrade globbing dependencies which require globs to be more strict and always use POSIX-style slashes because Windows-style slashes are used as escape sequences +* Update tests to work with upgraded globbing dependencies +* Add ability to log FSEvents require error by setting `CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR` env +* Fix for handling braces in globs +* Add node 8 & 9 to CI configs +* Allow node 0.10 failures on Windows + +# Chokidar 1.7.0 (May 8, 2017) +* Add `disableGlobbing` option +* Add ability to force interval value by setting CHOKIDAR_INTERVAL env + variable +* Fix issue with `.close()` being called before `ready` + +# Chokidar 1.6.0 (Jun 22, 2016) +* Added ability for force `usePolling` mode by setting `CHOKIDAR_USEPOLLING` + env variable + +# Chokidar 1.5.2 (Jun 7, 2016) +* Fix missing `addDir` events when using `cwd` and `alwaysStat` options +* Fix missing `add` events for files within a renamed directory + +# Chokidar 1.5.1 (May 20, 2016) +* To help prevent exhaustion of FSEvents system limitations, consolidate watch + instances to the common parent upon detection of separate watch instances on + many siblings + +# Chokidar 1.5.0 (May 10, 2016) +* Make debounce delay setting used with `atomic: true` user-customizable +* Fixes and improvements to `awaitWriteFinish` features + +# Chokidar 1.4.3 (Feb 26, 2016) +* Update async-each dependency to ^1.0.0 + +# Chokidar 1.4.2 (Dec 30, 2015) +* Now correctly emitting `stats` with `awaitWriteFinish` option. + +# Chokidar 1.4.1 (Dec 9, 2015) +* The watcher could now be correctly subclassed with ES6 class syntax. + +# Chokidar 1.4.0 (Dec 3, 2015) +* Add `.getWatched()` method, exposing all file system entries being watched +* Apply `awaitWriteFinish` methodology to `change` events (in addition to `add`) +* Fix handling of symlinks within glob paths (#293) +* Fix `addDir` and `unlinkDir` events under globs (#337, #401) +* Fix issues with `.unwatch()` (#374, #403) + +# Chokidar 1.3.0 (Nov 18, 2015) +* Improve `awaitWriteFinish` option behavior +* Fix some `cwd` option behavior on Windows +* `awaitWriteFinish` and `cwd` are now compatible +* Fix some race conditions. +* #379: Recreating deleted directory doesn't trigger event +* When adding a previously-deleted file, emit 'add', not 'change' + +# Chokidar 1.2.0 (Oct 1, 2015) +* Allow nested arrays of paths to be provided to `.watch()` and `.add()` +* Add `awaitWriteFinish` option + +# Chokidar 1.1.0 (Sep 23, 2015) +* Dependency updates including fsevents@1.0.0, improving installation + +# Chokidar 1.0.6 (Sep 18, 2015) +* Fix issue with `.unwatch()` method and relative paths + +# Chokidar 1.0.5 (Jul 20, 2015) +* Fix regression with regexes/fns using in `ignored` + +# Chokidar 1.0.4 (Jul 15, 2015) +* Fix bug with `ignored` files/globs while `cwd` option is set + +# Chokidar 1.0.3 (Jun 4, 2015) +* Fix race issue with `alwaysStat` option and removed files + +# Chokidar 1.0.2 (May 30, 2015) +* Fix bug with absolute paths and ENAMETOOLONG error + +# Chokidar 1.0.1 (Apr 8, 2015) +* Fix bug with `.close()` method in `fs.watch` mode with `persistent: false` + option + +# Chokidar 1.0.0 (Apr 7, 2015) +* Glob support! Use globs in `watch`, `add`, and `unwatch` methods +* Comprehensive symlink support +* New `unwatch` method to turn off watching of previously watched paths +* More flexible `ignored` option allowing regex, function, glob, or array + courtesy of [anymatch](https://github.com/es128/anymatch) +* New `cwd` option to set base dir from which relative paths are derived +* New `depth` option for limiting recursion +* New `alwaysStat` option to ensure + [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) gets passed + with every add/change event +* New `ready` event emitted when initial fs tree scan is done and watcher is + ready for changes +* New `raw` event exposing data and events from the lower-level watch modules +* New `followSymlinks` option to impact whether symlinks' targets or the symlink + files themselves are watched +* New `atomic` option for normalizing artifacts from text editors that use + atomic write methods +* Ensured watcher's stability with lots of bugfixes. + +# Chokidar 0.12.6 (Jan 6, 2015) +* Fix bug which breaks `persistent: false` mode when change events occur + +# Chokidar 0.12.5 (Dec 17, 2014) +* Fix bug with matching parent path detection for fsevents instance sharing +* Fix bug with ignored watch path in nodefs modes + +# Chokidar 0.12.4 (Dec 14, 2014) +* Fix bug in `fs.watch` mode that caused watcher to leak into `cwd` +* Fix bug preventing ready event when there are symlinks to ignored paths + +# Chokidar 0.12.3 (Dec 13, 2014) +* Fix handling of special files such as named pipes and sockets + +# Chokidar 0.12.2 (Dec 12, 2014) +* Fix recursive symlink handling and some other path resolution problems + +# Chokidar 0.12.1 (Dec 10, 2014) +* Fix a case where file symlinks were not followed properly + +# Chokidar 0.12.0 (Dec 8, 2014) +* Symlink support + * Add `followSymlinks` option, which defaults to `true` +* Change default watch mode on Linux to non-polling `fs.watch` +* Add `atomic` option to normalize events from editors using atomic writes + * Particularly Vim and Sublime +* Add `raw` event which exposes data from the underlying watch method + +# Chokidar 0.11.1 (Nov 19, 2014) +* Fix a bug where an error is thrown when `fs.watch` instantiation fails + +# Chokidar 0.11.0 (Nov 16, 2014) +* Add a `ready` event, which is emitted after initial file scan completes +* Fix issue with options keys passed in defined as `undefined` +* Rename some internal `FSWatcher` properties to indicate they're private + +# Chokidar 0.10.9 (Nov 15, 2014) +* Fix some leftover issues from adding watcher reuse + +# Chokidar 0.10.8 (Nov 14, 2014) +* Remove accidentally committed/published `console.log` statement. +* Sry 'bout that :crying_cat_face: + +# Chokidar 0.10.7 (Nov 14, 2014) +* Apply watcher reuse methodology to `fs.watch` and `fs.watchFile` as well + +# Chokidar 0.10.6 (Nov 12, 2014) +* More efficient creation/reuse of FSEvents instances to avoid system limits +* Reduce simultaneous FSEvents instances allowed in a process +* Handle errors thrown by `fs.watch` upon invocation + +# Chokidar 0.10.5 (Nov 6, 2014) +* Limit number of simultaneous FSEvents instances (fall back to other methods) +* Prevent some cases of EMFILE errors during initialization +* Fix ignored files emitting events in some fsevents-mode circumstances + +# Chokidar 0.10.4 (Nov 5, 2014) +* Bump fsevents dependency to ~0.3.1 + * Should resolve build warnings and `npm rebuild` on non-Macs + +# Chokidar 0.10.3 (Oct 28, 2014) +* Fix removed dir emitting as `unlink` instead of `unlinkDir` +* Fix issues with file changing to dir or vice versa (gh-165) +* Fix handling of `ignored` option in fsevents mode + +# Chokidar 0.10.2 (Oct 23, 2014) +* Improve individual file watching +* Fix fsevents keeping process alive when `persistent: false` + +# Chokidar 0.10.1 (19 October 2014) +* Improve handling of text editor atomic writes + +# Chokidar 0.10.0 (Oct 18, 2014) +* Many stability and consistency improvements +* Resolve many cases of duplicate or wrong events +* Correct for fsevents inconsistencies +* Standardize handling of errors and relative paths +* Fix issues with watching `./` + +# Chokidar 0.9.0 (Sep 25, 2014) +* Updated fsevents to 0.3 +* Update per-system defaults +* Fix issues with closing chokidar instance +* Fix duplicate change events on win32 + +# Chokidar 0.8.2 (Mar 26, 2014) +* Fixed npm issues related to fsevents dep. +* Updated fsevents to 0.2. + +# Chokidar 0.8.1 (Dec 16, 2013) +* Optional deps are now truly optional on windows and + linux. +* Rewritten in JS, again. +* Fixed some FSEvents-related bugs. + +# Chokidar 0.8.0 (Nov 29, 2013) +* Added ultra-fast low-CPU OS X file watching with FSEvents. + It is enabled by default. +* Added `addDir` and `unlinkDir` events. +* Polling is now disabled by default on all platforms. + +# Chokidar 0.7.1 (Nov 18, 2013) +* `Watcher#close` now also removes all event listeners. + +# Chokidar 0.7.0 (Oct 22, 2013) +* When `options.ignored` is two-argument function, it will + also be called after stating the FS, with `stats` argument. +* `unlink` is no longer emitted on directories. + +# Chokidar 0.6.3 (Aug 12, 2013) +* Added `usePolling` option (default: `true`). + When `false`, chokidar will use `fs.watch` as backend. + `fs.watch` is much faster, but not like super reliable. + +# Chokidar 0.6.2 (Mar 19, 2013) +* Fixed watching initially empty directories with `ignoreInitial` option. + +# Chokidar 0.6.1 (Mar 19, 2013) +* Added node.js 0.10 support. + +# Chokidar 0.6.0 (Mar 10, 2013) +* File attributes (stat()) are now passed to `add` and `change` events as second + arguments. +* Changed default polling interval for binary files to 300ms. + +# Chokidar 0.5.3 (Jan 13, 2013) +* Removed emitting of `change` events before `unlink`. + +# Chokidar 0.5.2 (Jan 13, 2013) +* Removed postinstall script to prevent various npm bugs. + +# Chokidar 0.5.1 (Jan 6, 2013) +* When starting to watch non-existing paths, chokidar will no longer throw + ENOENT error. +* Fixed bug with absolute path. + +# Chokidar 0.5.0 (Dec 9, 2012) +* Added a bunch of new options: + * `ignoreInitial` that allows to ignore initial `add` events. + * `ignorePermissionErrors` that allows to ignore ENOENT etc perm errors. + * `interval` and `binaryInterval` that allow to change default + fs polling intervals. + +# Chokidar 0.4.0 (Jul 26, 2012) +* Added `all` event that receives two args (event name and path) that combines + `add`, `change` and `unlink` events. +* Switched to `fs.watchFile` on node.js 0.8 on windows. +* Files are now correctly unwatched after unlink. + +# Chokidar 0.3.0 (Jun 24, 2012) +* `unlink` event are no longer emitted for directories, for consistency with + `add`. + +# Chokidar 0.2.6 (Jun 8, 2012) +* Prevented creating of duplicate 'add' events. + +# Chokidar 0.2.5 (Jun 8, 2012) +* Fixed a bug when new files in new directories hadn't been added. + +# Chokidar 0.2.4 (Jun 7, 2012) +* Fixed a bug when unlinked files emitted events after unlink. + +# Chokidar 0.2.3 (May 12, 2012) +* Fixed watching of files on windows. + +# Chokidar 0.2.2 (May 4, 2012) +* Fixed watcher signature. + +# Chokidar 0.2.1 (May 4, 2012) +* Fixed invalid API bug when using `watch()`. + +# Chokidar 0.2.0 (May 4, 2012) +* Rewritten in js. + +# Chokidar 0.1.1 (Apr 26, 2012) +* Changed api to `chokidar.watch()`. +* Fixed compilation on windows. + +# Chokidar 0.1.0 (Apr 20, 2012) +* Initial release, extracted from + [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66) diff --git a/node_modules/chokidar/README.md b/node_modules/chokidar/README.md new file mode 100644 index 0000000..9758774 --- /dev/null +++ b/node_modules/chokidar/README.md @@ -0,0 +1,293 @@ +# Chokidar [![Mac/Linux Build Status](https://img.shields.io/travis/paulmillr/chokidar/master.svg?label=Mac%20OSX%20%26%20Linux)](https://travis-ci.org/paulmillr/chokidar) [![Windows Build status](https://img.shields.io/appveyor/ci/paulmillr/chokidar/master.svg?label=Windows)](https://ci.appveyor.com/project/paulmillr/chokidar/branch/master) [![Coverage Status](https://coveralls.io/repos/paulmillr/chokidar/badge.svg)](https://coveralls.io/r/paulmillr/chokidar) [![Join the chat at https://gitter.im/paulmillr/chokidar](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paulmillr/chokidar?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +> A neat wrapper around node.js fs.watch / fs.watchFile / fsevents. + +[![NPM](https://nodei.co/npm-dl/chokidar.png)](https://nodei.co/npm/chokidar/) +[![NPM](https://nodei.co/npm/chokidar.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/chokidar/) + +## Why? +Node.js `fs.watch`: + +* Doesn't report filenames on OS X. +* Doesn't report events at all when using editors like Sublime on OS X. +* Often reports events twice. +* Emits most changes as `rename`. +* Has [a lot of other issues](https://github.com/nodejs/node/search?q=fs.watch&type=Issues) +* Does not provide an easy way to recursively watch file trees. + +Node.js `fs.watchFile`: + +* Almost as bad at event handling. +* Also does not provide any recursive watching. +* Results in high CPU utilization. + +Chokidar resolves these problems. + +Initially made for [brunch](http://brunch.io) (an ultra-swift web app build tool), it is now used in +[gulp](https://github.com/gulpjs/gulp/), +[karma](http://karma-runner.github.io), +[PM2](https://github.com/Unitech/PM2), +[browserify](http://browserify.org/), +[webpack](http://webpack.github.io/), +[BrowserSync](http://www.browsersync.io/), +[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode), +and [many others](https://www.npmjs.org/browse/depended/chokidar/). +It has proven itself in production environments. + +## How? +Chokidar does still rely on the Node.js core `fs` module, but when using +`fs.watch` and `fs.watchFile` for watching, it normalizes the events it +receives, often checking for truth by getting file stats and/or dir contents. + +On Mac OS X, chokidar by default uses a native extension exposing the Darwin +`FSEvents` API. This provides very efficient recursive watching compared with +implementations like `kqueue` available on most \*nix platforms. Chokidar still +does have to do some work to normalize the events received that way as well. + +On other platforms, the `fs.watch`-based implementation is the default, which +avoids polling and keeps CPU usage down. Be advised that chokidar will initiate +watchers recursively for everything within scope of the paths that have been +specified, so be judicious about not wasting system resources by watching much +more than needed. + +## Getting started +Install with npm: + + npm install chokidar --save + +Then `require` and use it in your code: + +```javascript +var chokidar = require('chokidar'); + +// One-liner for current directory, ignores .dotfiles +chokidar.watch('.', {ignored: /(^|[\/\\])\../}).on('all', (event, path) => { + console.log(event, path); +}); +``` + +```javascript +// Example of a more typical implementation structure: + +// Initialize watcher. +var watcher = chokidar.watch('file, dir, glob, or array', { + ignored: /(^|[\/\\])\../, + persistent: true +}); + +// Something to use when events are received. +var log = console.log.bind(console); +// Add event listeners. +watcher + .on('add', path => log(`File ${path} has been added`)) + .on('change', path => log(`File ${path} has been changed`)) + .on('unlink', path => log(`File ${path} has been removed`)); + +// More possible events. +watcher + .on('addDir', path => log(`Directory ${path} has been added`)) + .on('unlinkDir', path => log(`Directory ${path} has been removed`)) + .on('error', error => log(`Watcher error: ${error}`)) + .on('ready', () => log('Initial scan complete. Ready for changes')) + .on('raw', (event, path, details) => { + log('Raw event info:', event, path, details); + }); + +// 'add', 'addDir' and 'change' events also receive stat() results as second +// argument when available: http://nodejs.org/api/fs.html#fs_class_fs_stats +watcher.on('change', (path, stats) => { + if (stats) console.log(`File ${path} changed size to ${stats.size}`); +}); + +// Watch new files. +watcher.add('new-file'); +watcher.add(['new-file-2', 'new-file-3', '**/other-file*']); + +// Get list of actual paths being watched on the filesystem +var watchedPaths = watcher.getWatched(); + +// Un-watch some files. +watcher.unwatch('new-file*'); + +// Stop watching. +watcher.close(); + +// Full list of options. See below for descriptions. (do not use this example) +chokidar.watch('file', { + persistent: true, + + ignored: '*.txt', + ignoreInitial: false, + followSymlinks: true, + cwd: '.', + disableGlobbing: false, + + usePolling: true, + interval: 100, + binaryInterval: 300, + alwaysStat: false, + depth: 99, + awaitWriteFinish: { + stabilityThreshold: 2000, + pollInterval: 100 + }, + + ignorePermissionErrors: false, + atomic: true // or a custom 'atomicity delay', in milliseconds (default 100) +}); + +``` + +## API + +`chokidar.watch(paths, [options])` + +* `paths` (string or array of strings). Paths to files, dirs to be watched +recursively, or glob patterns. +* `options` (object) Options object as defined below: + +#### Persistence + +* `persistent` (default: `true`). Indicates whether the process +should continue to run as long as files are being watched. If set to +`false` when using `fsevents` to watch, no more events will be emitted +after `ready`, even if the process continues to run. + +#### Path filtering + +* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible definition) +Defines files/paths to be ignored. The whole relative or absolute path is +tested, not just filename. If a function with two arguments is provided, it +gets called twice per path - once with a single argument (the path), second +time with two arguments (the path and the +[`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats) +object of that path). +* `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` events are also emitted for matching paths while +instantiating the watching as chokidar discovers these file paths (before the `ready` event). +* `followSymlinks` (default: `true`). When `false`, only the +symlinks themselves will be watched for changes instead of following +the link references and bubbling events through the link's path. +* `cwd` (no default). The base directory from which watch `paths` are to be +derived. Paths emitted with events will be relative to this. +* `disableGlobbing` (default: `false`). If set to `true` then the strings passed to `.watch()` and `.add()` are treated as +literal path names, even if they look like globs. + +#### Performance + +* `usePolling` (default: `false`). +Whether to use fs.watchFile (backed by polling), or fs.watch. If polling +leads to high CPU utilization, consider setting this to `false`. It is +typically necessary to **set this to `true` to successfully watch files over +a network**, and it may be necessary to successfully watch files in other +non-standard situations. Setting to `true` explicitly on OS X overrides the +`useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable +to true (1) or false (0) in order to override this option. +* _Polling-specific settings_ (effective when `usePolling: true`) + * `interval` (default: `100`). Interval of file system polling. You may also + set the CHOKIDAR_INTERVAL env variable to override this option. + * `binaryInterval` (default: `300`). Interval of file system + polling for binary files. + ([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) +* `useFsEvents` (default: `true` on OS X). Whether to use the +`fsevents` watching interface if available. When set to `true` explicitly +and `fsevents` is available this supercedes the `usePolling` setting. When +set to `false` on OS X, `usePolling: true` becomes the default. +* `alwaysStat` (default: `false`). If relying upon the +[`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats) +object that may get passed with `add`, `addDir`, and `change` events, set +this to `true` to ensure it is provided even in cases where it wasn't +already available from the underlying watch events. +* `depth` (default: `undefined`). If set, limits how many levels of +subdirectories will be traversed. +* `awaitWriteFinish` (default: `false`). +By default, the `add` event will fire when a file first appears on disk, before +the entire file has been written. Furthermore, in some cases some `change` +events will be emitted while the file is being written. In some cases, +especially when watching for large files there will be a need to wait for the +write operation to finish before responding to a file creation or modification. +Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size, +holding its `add` and `change` events until the size does not change for a +configurable amount of time. The appropriate duration setting is heavily +dependent on the OS and hardware. For accurate detection this parameter should +be relatively high, making file watching much less responsive. +Use with caution. + * *`options.awaitWriteFinish` can be set to an object in order to adjust + timing params:* + * `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in + milliseconds for a file size to remain constant before emitting its event. + * `awaitWriteFinish.pollInterval` (default: 100). File size polling interval. + +#### Errors +* `ignorePermissionErrors` (default: `false`). Indicates whether to watch files +that don't have read permissions if possible. If watching fails due to `EPERM` +or `EACCES` with this set to `true`, the errors will be suppressed silently. +* `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`). +Automatically filters out artifacts that occur when using editors that use +"atomic writes" instead of writing directly to the source file. If a file is +re-added within 100 ms of being deleted, Chokidar emits a `change` event +rather than `unlink` then `add`. If the default of 100 ms does not work well +for you, you can override it by setting `atomic` to a custom value, in +milliseconds. + +### Methods & Events + +`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`: + +* `.add(path / paths)`: Add files, directories, or glob patterns for tracking. +Takes an array of strings or just one string. +* `.on(event, callback)`: Listen for an FS event. +Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`, +`raw`, `error`. +Additionally `all` is available which gets emitted with the underlying event +name and path for every event other than `ready`, `raw`, and `error`. +* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns. +Takes an array of strings or just one string. +* `.close()`: Removes all listeners from watched files. +* `.getWatched()`: Returns an object representing all the paths on the file +system being watched by this `FSWatcher` instance. The object's keys are all the +directories (using absolute paths unless the `cwd` option was used), and the +values are arrays of the names of the items contained in each directory. + +## CLI + +If you need a CLI interface for your file watching, check out +[chokidar-cli](https://github.com/kimmobrunfeldt/chokidar-cli), allowing you to +execute a command on each change, or get a stdio stream of change events. + +## Install Troubleshooting + +* `npm WARN optional dep failed, continuing fsevents@n.n.n` + * This message is normal part of how `npm` handles optional dependencies and is + not indicative of a problem. Even if accompanied by other related error messages, + Chokidar should function properly. + +* `ERR! stack Error: Python executable "python" is v3.4.1, which is not supported by gyp.` + * You should be able to resolve this by installing python 2.7 and running: + `npm config set python python2.7` + +* `gyp ERR! stack Error: not found: make` + * On Mac, install the XCode command-line tools + +## License + +The MIT License (MIT) + +Copyright (c) 2016 Paul Miller (http://paulmillr.com) & Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/chokidar/index.js b/node_modules/chokidar/index.js new file mode 100644 index 0000000..121703a --- /dev/null +++ b/node_modules/chokidar/index.js @@ -0,0 +1,743 @@ +'use strict'; +var EventEmitter = require('events').EventEmitter; +var fs = require('fs'); +var sysPath = require('path'); +var asyncEach = require('async-each'); +var anymatch = require('anymatch'); +var globParent = require('glob-parent'); +var isGlob = require('is-glob'); +var isAbsolute = require('path-is-absolute'); +var inherits = require('inherits'); +var braces = require('braces'); +var normalizePath = require('normalize-path'); +var upath = require('upath'); + +var NodeFsHandler = require('./lib/nodefs-handler'); +var FsEventsHandler = require('./lib/fsevents-handler'); + +var arrify = function(value) { + if (value == null) return []; + return Array.isArray(value) ? value : [value]; +}; + +var flatten = function(list, result) { + if (result == null) result = []; + list.forEach(function(item) { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + }); + return result; +}; + +// Little isString util for use in Array#every. +var isString = function(thing) { + return typeof thing === 'string'; +}; + +// Public: Main class. +// Watches files & directories for changes. +// +// * _opts - object, chokidar options hash +// +// Emitted events: +// `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error` +// +// Examples +// +// var watcher = new FSWatcher() +// .add(directories) +// .on('add', path => console.log('File', path, 'was added')) +// .on('change', path => console.log('File', path, 'was changed')) +// .on('unlink', path => console.log('File', path, 'was removed')) +// .on('all', (event, path) => console.log(path, ' emitted ', event)) +// +function FSWatcher(_opts) { + EventEmitter.call(this); + var opts = {}; + // in case _opts that is passed in is a frozen object + if (_opts) for (var opt in _opts) opts[opt] = _opts[opt]; + this._watched = Object.create(null); + this._closers = Object.create(null); + this._ignoredPaths = Object.create(null); + Object.defineProperty(this, '_globIgnored', { + get: function() { return Object.keys(this._ignoredPaths); } + }); + this.closed = false; + this._throttled = Object.create(null); + this._symlinkPaths = Object.create(null); + + function undef(key) { + return opts[key] === undefined; + } + + // Set up default options. + if (undef('persistent')) opts.persistent = true; + if (undef('ignoreInitial')) opts.ignoreInitial = false; + if (undef('ignorePermissionErrors')) opts.ignorePermissionErrors = false; + if (undef('interval')) opts.interval = 100; + if (undef('binaryInterval')) opts.binaryInterval = 300; + if (undef('disableGlobbing')) opts.disableGlobbing = false; + this.enableBinaryInterval = opts.binaryInterval !== opts.interval; + + // Enable fsevents on OS X when polling isn't explicitly enabled. + if (undef('useFsEvents')) opts.useFsEvents = !opts.usePolling; + + // If we can't use fsevents, ensure the options reflect it's disabled. + if (!FsEventsHandler.canUse()) opts.useFsEvents = false; + + // Use polling on Mac if not using fsevents. + // Other platforms use non-polling fs.watch. + if (undef('usePolling') && !opts.useFsEvents) { + opts.usePolling = process.platform === 'darwin'; + } + + // Global override (useful for end-developers that need to force polling for all + // instances of chokidar, regardless of usage/dependency depth) + var envPoll = process.env.CHOKIDAR_USEPOLLING; + if (envPoll !== undefined) { + var envLower = envPoll.toLowerCase(); + + if (envLower === 'false' || envLower === '0') { + opts.usePolling = false; + } else if (envLower === 'true' || envLower === '1') { + opts.usePolling = true; + } else { + opts.usePolling = !!envLower + } + } + var envInterval = process.env.CHOKIDAR_INTERVAL; + if (envInterval) { + opts.interval = parseInt(envInterval); + } + + // Editor atomic write normalization enabled by default with fs.watch + if (undef('atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents; + if (opts.atomic) this._pendingUnlinks = Object.create(null); + + if (undef('followSymlinks')) opts.followSymlinks = true; + + if (undef('awaitWriteFinish')) opts.awaitWriteFinish = false; + if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {}; + var awf = opts.awaitWriteFinish; + if (awf) { + if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000; + if (!awf.pollInterval) awf.pollInterval = 100; + + this._pendingWrites = Object.create(null); + } + if (opts.ignored) opts.ignored = arrify(opts.ignored); + + this._isntIgnored = function(path, stat) { + return !this._isIgnored(path, stat); + }.bind(this); + + var readyCalls = 0; + this._emitReady = function() { + if (++readyCalls >= this._readyCount) { + this._emitReady = Function.prototype; + this._readyEmitted = true; + // use process.nextTick to allow time for listener to be bound + process.nextTick(this.emit.bind(this, 'ready')); + } + }.bind(this); + + this.options = opts; + + // You’re frozen when your heart’s not open. + Object.freeze(opts); +} + +inherits(FSWatcher, EventEmitter); + +// Common helpers +// -------------- + +// Private method: Normalize and emit events +// +// * event - string, type of event +// * path - string, file or directory path +// * val[1..3] - arguments to be passed with event +// +// Returns the error if defined, otherwise the value of the +// FSWatcher instance's `closed` flag +FSWatcher.prototype._emit = function(event, path, val1, val2, val3) { + if (this.options.cwd) path = sysPath.relative(this.options.cwd, path); + var args = [event, path]; + if (val3 !== undefined) args.push(val1, val2, val3); + else if (val2 !== undefined) args.push(val1, val2); + else if (val1 !== undefined) args.push(val1); + + var awf = this.options.awaitWriteFinish; + if (awf && this._pendingWrites[path]) { + this._pendingWrites[path].lastChange = new Date(); + return this; + } + + if (this.options.atomic) { + if (event === 'unlink') { + this._pendingUnlinks[path] = args; + setTimeout(function() { + Object.keys(this._pendingUnlinks).forEach(function(path) { + this.emit.apply(this, this._pendingUnlinks[path]); + this.emit.apply(this, ['all'].concat(this._pendingUnlinks[path])); + delete this._pendingUnlinks[path]; + }.bind(this)); + }.bind(this), typeof this.options.atomic === "number" + ? this.options.atomic + : 100); + return this; + } else if (event === 'add' && this._pendingUnlinks[path]) { + event = args[0] = 'change'; + delete this._pendingUnlinks[path]; + } + } + + var emitEvent = function() { + this.emit.apply(this, args); + if (event !== 'error') this.emit.apply(this, ['all'].concat(args)); + }.bind(this); + + if (awf && (event === 'add' || event === 'change') && this._readyEmitted) { + var awfEmit = function(err, stats) { + if (err) { + event = args[0] = 'error'; + args[1] = err; + emitEvent(); + } else if (stats) { + // if stats doesn't exist the file must have been deleted + if (args.length > 2) { + args[2] = stats; + } else { + args.push(stats); + } + emitEvent(); + } + }; + + this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit); + return this; + } + + if (event === 'change') { + if (!this._throttle('change', path, 50)) return this; + } + + if ( + this.options.alwaysStat && val1 === undefined && + (event === 'add' || event === 'addDir' || event === 'change') + ) { + var fullPath = this.options.cwd ? sysPath.join(this.options.cwd, path) : path; + fs.stat(fullPath, function(error, stats) { + // Suppress event when fs.stat fails, to avoid sending undefined 'stat' + if (error || !stats) return; + + args.push(stats); + emitEvent(); + }); + } else { + emitEvent(); + } + + return this; +}; + +// Private method: Common handler for errors +// +// * error - object, Error instance +// +// Returns the error if defined, otherwise the value of the +// FSWatcher instance's `closed` flag +FSWatcher.prototype._handleError = function(error) { + var code = error && error.code; + var ipe = this.options.ignorePermissionErrors; + if (error && + code !== 'ENOENT' && + code !== 'ENOTDIR' && + (!ipe || (code !== 'EPERM' && code !== 'EACCES')) + ) this.emit('error', error); + return error || this.closed; +}; + +// Private method: Helper utility for throttling +// +// * action - string, type of action being throttled +// * path - string, path being acted upon +// * timeout - int, duration of time to suppress duplicate actions +// +// Returns throttle tracking object or false if action should be suppressed +FSWatcher.prototype._throttle = function(action, path, timeout) { + if (!(action in this._throttled)) { + this._throttled[action] = Object.create(null); + } + var throttled = this._throttled[action]; + if (path in throttled) { + throttled[path].count++; + return false; + } + function clear() { + var count = throttled[path] ? throttled[path].count : 0; + delete throttled[path]; + clearTimeout(timeoutObject); + return count; + } + var timeoutObject = setTimeout(clear, timeout); + throttled[path] = {timeoutObject: timeoutObject, clear: clear, count: 0}; + return throttled[path]; +}; + +// Private method: Awaits write operation to finish +// +// * path - string, path being acted upon +// * threshold - int, time in milliseconds a file size must be fixed before +// acknowledgeing write operation is finished +// * awfEmit - function, to be called when ready for event to be emitted +// Polls a newly created file for size variations. When files size does not +// change for 'threshold' milliseconds calls callback. +FSWatcher.prototype._awaitWriteFinish = function(path, threshold, event, awfEmit) { + var timeoutHandler; + + var fullPath = path; + if (this.options.cwd && !isAbsolute(path)) { + fullPath = sysPath.join(this.options.cwd, path); + } + + var now = new Date(); + + var awaitWriteFinish = (function (prevStat) { + fs.stat(fullPath, function(err, curStat) { + if (err || !(path in this._pendingWrites)) { + if (err && err.code !== 'ENOENT') awfEmit(err); + return; + } + + var now = new Date(); + + if (prevStat && curStat.size != prevStat.size) { + this._pendingWrites[path].lastChange = now; + } + + if (now - this._pendingWrites[path].lastChange >= threshold) { + delete this._pendingWrites[path]; + awfEmit(null, curStat); + } else { + timeoutHandler = setTimeout( + awaitWriteFinish.bind(this, curStat), + this.options.awaitWriteFinish.pollInterval + ); + } + }.bind(this)); + }.bind(this)); + + if (!(path in this._pendingWrites)) { + this._pendingWrites[path] = { + lastChange: now, + cancelWait: function() { + delete this._pendingWrites[path]; + clearTimeout(timeoutHandler); + return event; + }.bind(this) + }; + timeoutHandler = setTimeout( + awaitWriteFinish.bind(this), + this.options.awaitWriteFinish.pollInterval + ); + } +}; + +// Private method: Determines whether user has asked to ignore this path +// +// * path - string, path to file or directory +// * stats - object, result of fs.stat +// +// Returns boolean +var dotRe = /\..*\.(sw[px])$|\~$|\.subl.*\.tmp/; +FSWatcher.prototype._isIgnored = function(path, stats) { + if (this.options.atomic && dotRe.test(path)) return true; + + if (!this._userIgnored) { + var cwd = this.options.cwd; + var ignored = this.options.ignored; + if (cwd && ignored) { + ignored = ignored.map(function (path) { + if (typeof path !== 'string') return path; + return upath.normalize(isAbsolute(path) ? path : sysPath.join(cwd, path)); + }); + } + var paths = arrify(ignored) + .filter(function(path) { + return typeof path === 'string' && !isGlob(path); + }).map(function(path) { + return path + '/**'; + }); + this._userIgnored = anymatch( + this._globIgnored.concat(ignored).concat(paths) + ); + } + + return this._userIgnored([path, stats]); +}; + +// Private method: Provides a set of common helpers and properties relating to +// symlink and glob handling +// +// * path - string, file, directory, or glob pattern being watched +// * depth - int, at any depth > 0, this isn't a glob +// +// Returns object containing helpers for this path +var replacerRe = /^\.[\/\\]/; +FSWatcher.prototype._getWatchHelpers = function(path, depth) { + path = path.replace(replacerRe, ''); + var watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path); + var fullWatchPath = sysPath.resolve(watchPath); + var hasGlob = watchPath !== path; + var globFilter = hasGlob ? anymatch(path) : false; + var follow = this.options.followSymlinks; + var globSymlink = hasGlob && follow ? null : false; + + var checkGlobSymlink = function(entry) { + // only need to resolve once + // first entry should always have entry.parentDir === '' + if (globSymlink == null) { + globSymlink = entry.fullParentDir === fullWatchPath ? false : { + realPath: entry.fullParentDir, + linkPath: fullWatchPath + }; + } + + if (globSymlink) { + return entry.fullPath.replace(globSymlink.realPath, globSymlink.linkPath); + } + + return entry.fullPath; + }; + + var entryPath = function(entry) { + return sysPath.join(watchPath, + sysPath.relative(watchPath, checkGlobSymlink(entry)) + ); + }; + + var filterPath = function(entry) { + if (entry.stat && entry.stat.isSymbolicLink()) return filterDir(entry); + var resolvedPath = entryPath(entry); + return (!hasGlob || globFilter(resolvedPath)) && + this._isntIgnored(resolvedPath, entry.stat) && + (this.options.ignorePermissionErrors || + this._hasReadPermissions(entry.stat)); + }.bind(this); + + var getDirParts = function(path) { + if (!hasGlob) return false; + var parts = []; + var expandedPath = braces.expand(path); + expandedPath.forEach(function(path) { + parts.push(sysPath.relative(watchPath, path).split(/[\/\\]/)); + }); + return parts; + }; + + var dirParts = getDirParts(path); + if (dirParts) { + dirParts.forEach(function(parts) { + if (parts.length > 1) parts.pop(); + }); + } + var unmatchedGlob; + + var filterDir = function(entry) { + if (hasGlob) { + var entryParts = getDirParts(checkGlobSymlink(entry)); + var globstar = false; + unmatchedGlob = !dirParts.some(function(parts) { + return parts.every(function(part, i) { + if (part === '**') globstar = true; + return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i]); + }); + }); + } + return !unmatchedGlob && this._isntIgnored(entryPath(entry), entry.stat); + }.bind(this); + + return { + followSymlinks: follow, + statMethod: follow ? 'stat' : 'lstat', + path: path, + watchPath: watchPath, + entryPath: entryPath, + hasGlob: hasGlob, + globFilter: globFilter, + filterPath: filterPath, + filterDir: filterDir + }; +}; + +// Directory helpers +// ----------------- + +// Private method: Provides directory tracking objects +// +// * directory - string, path of the directory +// +// Returns the directory's tracking object +FSWatcher.prototype._getWatchedDir = function(directory) { + var dir = sysPath.resolve(directory); + var watcherRemove = this._remove.bind(this); + if (!(dir in this._watched)) this._watched[dir] = { + _items: Object.create(null), + add: function(item) { + if (item !== '.' && item !== '..') this._items[item] = true; + }, + remove: function(item) { + delete this._items[item]; + if (!this.children().length) { + fs.readdir(dir, function(err) { + if (err) watcherRemove(sysPath.dirname(dir), sysPath.basename(dir)); + }); + } + }, + has: function(item) {return item in this._items;}, + children: function() {return Object.keys(this._items);} + }; + return this._watched[dir]; +}; + +// File helpers +// ------------ + +// Private method: Check for read permissions +// Based on this answer on SO: http://stackoverflow.com/a/11781404/1358405 +// +// * stats - object, result of fs.stat +// +// Returns boolean +FSWatcher.prototype._hasReadPermissions = function(stats) { + return Boolean(4 & parseInt(((stats && stats.mode) & 0x1ff).toString(8)[0], 10)); +}; + +// Private method: Handles emitting unlink events for +// files and directories, and via recursion, for +// files and directories within directories that are unlinked +// +// * directory - string, directory within which the following item is located +// * item - string, base path of item/directory +// +// Returns nothing +FSWatcher.prototype._remove = function(directory, item) { + // if what is being deleted is a directory, get that directory's paths + // for recursive deleting and cleaning of watched object + // if it is not a directory, nestedDirectoryChildren will be empty array + var path = sysPath.join(directory, item); + var fullPath = sysPath.resolve(path); + var isDirectory = this._watched[path] || this._watched[fullPath]; + + // prevent duplicate handling in case of arriving here nearly simultaneously + // via multiple paths (such as _handleFile and _handleDir) + if (!this._throttle('remove', path, 100)) return; + + // if the only watched file is removed, watch for its return + var watchedDirs = Object.keys(this._watched); + if (!isDirectory && !this.options.useFsEvents && watchedDirs.length === 1) { + this.add(directory, item, true); + } + + // This will create a new entry in the watched object in either case + // so we got to do the directory check beforehand + var nestedDirectoryChildren = this._getWatchedDir(path).children(); + + // Recursively remove children directories / files. + nestedDirectoryChildren.forEach(function(nestedItem) { + this._remove(path, nestedItem); + }, this); + + // Check if item was on the watched list and remove it + var parent = this._getWatchedDir(directory); + var wasTracked = parent.has(item); + parent.remove(item); + + // If we wait for this file to be fully written, cancel the wait. + var relPath = path; + if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path); + if (this.options.awaitWriteFinish && this._pendingWrites[relPath]) { + var event = this._pendingWrites[relPath].cancelWait(); + if (event === 'add') return; + } + + // The Entry will either be a directory that just got removed + // or a bogus entry to a file, in either case we have to remove it + delete this._watched[path]; + delete this._watched[fullPath]; + var eventName = isDirectory ? 'unlinkDir' : 'unlink'; + if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path); + + // Avoid conflicts if we later create another file with the same name + if (!this.options.useFsEvents) { + this._closePath(path); + } +}; + +FSWatcher.prototype._closePath = function(path) { + if (!this._closers[path]) return; + this._closers[path](); + delete this._closers[path]; + this._getWatchedDir(sysPath.dirname(path)).remove(sysPath.basename(path)); +} + +// Public method: Adds paths to be watched on an existing FSWatcher instance + +// * paths - string or array of strings, file/directory paths and/or globs +// * _origAdd - private boolean, for handling non-existent paths to be watched +// * _internal - private boolean, indicates a non-user add + +// Returns an instance of FSWatcher for chaining. +FSWatcher.prototype.add = function(paths, _origAdd, _internal) { + var disableGlobbing = this.options.disableGlobbing; + var cwd = this.options.cwd; + this.closed = false; + paths = flatten(arrify(paths)); + + if (!paths.every(isString)) { + throw new TypeError('Non-string provided as watch path: ' + paths); + } + + if (cwd) paths = paths.map(function(path) { + var absPath; + if (isAbsolute(path)) { + absPath = path; + } else if (path[0] === '!') { + absPath = '!' + sysPath.join(cwd, path.substring(1)); + } else { + absPath = sysPath.join(cwd, path); + } + + // Check `path` instead of `absPath` because the cwd portion can't be a glob + if (disableGlobbing || !isGlob(path)) { + return absPath; + } else { + return normalizePath(absPath); + } + }); + + // set aside negated glob strings + paths = paths.filter(function(path) { + if (path[0] === '!') { + this._ignoredPaths[path.substring(1)] = true; + } else { + // if a path is being added that was previously ignored, stop ignoring it + delete this._ignoredPaths[path]; + delete this._ignoredPaths[path + '/**']; + + // reset the cached userIgnored anymatch fn + // to make ignoredPaths changes effective + this._userIgnored = null; + + return true; + } + }, this); + + if (this.options.useFsEvents && FsEventsHandler.canUse()) { + if (!this._readyCount) this._readyCount = paths.length; + if (this.options.persistent) this._readyCount *= 2; + paths.forEach(this._addToFsEvents, this); + } else { + if (!this._readyCount) this._readyCount = 0; + this._readyCount += paths.length; + asyncEach(paths, function(path, next) { + this._addToNodeFs(path, !_internal, 0, 0, _origAdd, function(err, res) { + if (res) this._emitReady(); + next(err, res); + }.bind(this)); + }.bind(this), function(error, results) { + results.forEach(function(item) { + if (!item || this.closed) return; + this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item)); + }, this); + }.bind(this)); + } + + return this; +}; + +// Public method: Close watchers or start ignoring events from specified paths. + +// * paths - string or array of strings, file/directory paths and/or globs + +// Returns instance of FSWatcher for chaining. +FSWatcher.prototype.unwatch = function(paths) { + if (this.closed) return this; + paths = flatten(arrify(paths)); + + paths.forEach(function(path) { + // convert to absolute path unless relative path already matches + if (!isAbsolute(path) && !this._closers[path]) { + if (this.options.cwd) path = sysPath.join(this.options.cwd, path); + path = sysPath.resolve(path); + } + + this._closePath(path); + + this._ignoredPaths[path] = true; + if (path in this._watched) { + this._ignoredPaths[path + '/**'] = true; + } + + // reset the cached userIgnored anymatch fn + // to make ignoredPaths changes effective + this._userIgnored = null; + }, this); + + return this; +}; + +// Public method: Close watchers and remove all listeners from watched paths. + +// Returns instance of FSWatcher for chaining. +FSWatcher.prototype.close = function() { + if (this.closed) return this; + + this.closed = true; + Object.keys(this._closers).forEach(function(watchPath) { + this._closers[watchPath](); + delete this._closers[watchPath]; + }, this); + this._watched = Object.create(null); + + this.removeAllListeners(); + return this; +}; + +// Public method: Expose list of watched paths + +// Returns object w/ dir paths as keys and arrays of contained paths as values. +FSWatcher.prototype.getWatched = function() { + var watchList = {}; + Object.keys(this._watched).forEach(function(dir) { + var key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir; + watchList[key || '.'] = Object.keys(this._watched[dir]._items).sort(); + }.bind(this)); + return watchList; +}; + +// Attach watch handler prototype methods +function importHandler(handler) { + Object.keys(handler.prototype).forEach(function(method) { + FSWatcher.prototype[method] = handler.prototype[method]; + }); +} +importHandler(NodeFsHandler); +if (FsEventsHandler.canUse()) importHandler(FsEventsHandler); + +// Export FSWatcher class +exports.FSWatcher = FSWatcher; + +// Public function: Instantiates watcher with paths to be tracked. + +// * paths - string or array of strings, file/directory paths and/or globs +// * options - object, chokidar options + +// Returns an instance of FSWatcher for chaining. +exports.watch = function(paths, options) { + return new FSWatcher(options).add(paths); +}; diff --git a/node_modules/chokidar/lib/fsevents-handler.js b/node_modules/chokidar/lib/fsevents-handler.js new file mode 100644 index 0000000..a748a1b --- /dev/null +++ b/node_modules/chokidar/lib/fsevents-handler.js @@ -0,0 +1,405 @@ +'use strict'; + +var fs = require('fs'); +var sysPath = require('path'); +var readdirp = require('readdirp'); +var fsevents; +try { fsevents = require('fsevents'); } catch (error) { + if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error) +} + +// fsevents instance helper functions + +// object to hold per-process fsevents instances +// (may be shared across chokidar FSWatcher instances) +var FSEventsWatchers = Object.create(null); + +// Threshold of duplicate path prefixes at which to start +// consolidating going forward +var consolidateThreshhold = 10; + +// Private function: Instantiates the fsevents interface + +// * path - string, path to be watched +// * callback - function, called when fsevents is bound and ready + +// Returns new fsevents instance +function createFSEventsInstance(path, callback) { + return (new fsevents(path)).on('fsevent', callback).start(); +} + +// Private function: Instantiates the fsevents interface or binds listeners +// to an existing one covering the same file tree + +// * path - string, path to be watched +// * realPath - string, real path (in case of symlinks) +// * listener - function, called when fsevents emits events +// * rawEmitter - function, passes data to listeners of the 'raw' event + +// Returns close function +function setFSEventsListener(path, realPath, listener, rawEmitter) { + var watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path; + var watchContainer; + var parentPath = sysPath.dirname(watchPath); + + // If we've accumulated a substantial number of paths that + // could have been consolidated by watching one directory + // above the current one, create a watcher on the parent + // path instead, so that we do consolidate going forward. + if (couldConsolidate(parentPath)) { + watchPath = parentPath; + } + + var resolvedPath = sysPath.resolve(path); + var hasSymlink = resolvedPath !== realPath; + function filteredListener(fullPath, flags, info) { + if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath); + if ( + fullPath === resolvedPath || + !fullPath.indexOf(resolvedPath + sysPath.sep) + ) listener(fullPath, flags, info); + } + + // check if there is already a watcher on a parent path + // modifies `watchPath` to the parent path when it finds a match + function watchedParent() { + return Object.keys(FSEventsWatchers).some(function(watchedPath) { + // condition is met when indexOf returns 0 + if (!realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep)) { + watchPath = watchedPath; + return true; + } + }); + } + + if (watchPath in FSEventsWatchers || watchedParent()) { + watchContainer = FSEventsWatchers[watchPath]; + watchContainer.listeners.push(filteredListener); + } else { + watchContainer = FSEventsWatchers[watchPath] = { + listeners: [filteredListener], + rawEmitters: [rawEmitter], + watcher: createFSEventsInstance(watchPath, function(fullPath, flags) { + var info = fsevents.getInfo(fullPath, flags); + watchContainer.listeners.forEach(function(listener) { + listener(fullPath, flags, info); + }); + watchContainer.rawEmitters.forEach(function(emitter) { + emitter(info.event, fullPath, info); + }); + }) + }; + } + var listenerIndex = watchContainer.listeners.length - 1; + + // removes this instance's listeners and closes the underlying fsevents + // instance if there are no more listeners left + return function close() { + delete watchContainer.listeners[listenerIndex]; + delete watchContainer.rawEmitters[listenerIndex]; + if (!Object.keys(watchContainer.listeners).length) { + watchContainer.watcher.stop(); + delete FSEventsWatchers[watchPath]; + } + }; +} + +// Decide whether or not we should start a new higher-level +// parent watcher +function couldConsolidate(path) { + var keys = Object.keys(FSEventsWatchers); + var count = 0; + + for (var i = 0, len = keys.length; i < len; ++i) { + var watchPath = keys[i]; + if (watchPath.indexOf(path) === 0) { + count++; + if (count >= consolidateThreshhold) { + return true; + } + } + } + + return false; +} + +// returns boolean indicating whether fsevents can be used +function canUse() { + return fsevents && Object.keys(FSEventsWatchers).length < 128; +} + +// determines subdirectory traversal levels from root to path +function depth(path, root) { + var i = 0; + while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++; + return i; +} + +// fake constructor for attaching fsevents-specific prototype methods that +// will be copied to FSWatcher's prototype +function FsEventsHandler() {} + +// Private method: Handle symlinks encountered during directory scan + +// * watchPath - string, file/dir path to be watched with fsevents +// * realPath - string, real path (in case of symlinks) +// * transform - function, path transformer +// * globFilter - function, path filter in case a glob pattern was provided + +// Returns close function for the watcher instance +FsEventsHandler.prototype._watchWithFsEvents = +function(watchPath, realPath, transform, globFilter) { + if (this._isIgnored(watchPath)) return; + var watchCallback = function(fullPath, flags, info) { + if ( + this.options.depth !== undefined && + depth(fullPath, realPath) > this.options.depth + ) return; + var path = transform(sysPath.join( + watchPath, sysPath.relative(watchPath, fullPath) + )); + if (globFilter && !globFilter(path)) return; + // ensure directories are tracked + var parent = sysPath.dirname(path); + var item = sysPath.basename(path); + var watchedDir = this._getWatchedDir( + info.type === 'directory' ? path : parent + ); + var checkIgnored = function(stats) { + if (this._isIgnored(path, stats)) { + this._ignoredPaths[path] = true; + if (stats && stats.isDirectory()) { + this._ignoredPaths[path + '/**/*'] = true; + } + return true; + } else { + delete this._ignoredPaths[path]; + delete this._ignoredPaths[path + '/**/*']; + } + }.bind(this); + + var handleEvent = function(event) { + if (checkIgnored()) return; + + if (event === 'unlink') { + // suppress unlink events on never before seen files + if (info.type === 'directory' || watchedDir.has(item)) { + this._remove(parent, item); + } + } else { + if (event === 'add') { + // track new directories + if (info.type === 'directory') this._getWatchedDir(path); + + if (info.type === 'symlink' && this.options.followSymlinks) { + // push symlinks back to the top of the stack to get handled + var curDepth = this.options.depth === undefined ? + undefined : depth(fullPath, realPath) + 1; + return this._addToFsEvents(path, false, true, curDepth); + } else { + // track new paths + // (other than symlinks being followed, which will be tracked soon) + this._getWatchedDir(parent).add(item); + } + } + var eventName = info.type === 'directory' ? event + 'Dir' : event; + this._emit(eventName, path); + if (eventName === 'addDir') this._addToFsEvents(path, false, true); + } + }.bind(this); + + function addOrChange() { + handleEvent(watchedDir.has(item) ? 'change' : 'add'); + } + function checkFd() { + fs.open(path, 'r', function(error, fd) { + if (error) { + error.code !== 'EACCES' ? + handleEvent('unlink') : addOrChange(); + } else { + fs.close(fd, function(err) { + err && err.code !== 'EACCES' ? + handleEvent('unlink') : addOrChange(); + }); + } + }); + } + // correct for wrong events emitted + var wrongEventFlags = [ + 69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912 + ]; + if (wrongEventFlags.indexOf(flags) !== -1 || info.event === 'unknown') { + if (typeof this.options.ignored === 'function') { + fs.stat(path, function(error, stats) { + if (checkIgnored(stats)) return; + stats ? addOrChange() : handleEvent('unlink'); + }); + } else { + checkFd(); + } + } else { + switch (info.event) { + case 'created': + case 'modified': + return addOrChange(); + case 'deleted': + case 'moved': + return checkFd(); + } + } + }.bind(this); + + var closer = setFSEventsListener( + watchPath, + realPath, + watchCallback, + this.emit.bind(this, 'raw') + ); + + this._emitReady(); + return closer; +}; + +// Private method: Handle symlinks encountered during directory scan + +// * linkPath - string, path to symlink +// * fullPath - string, absolute path to the symlink +// * transform - function, pre-existing path transformer +// * curDepth - int, level of subdirectories traversed to where symlink is + +// Returns nothing +FsEventsHandler.prototype._handleFsEventsSymlink = +function(linkPath, fullPath, transform, curDepth) { + // don't follow the same symlink more than once + if (this._symlinkPaths[fullPath]) return; + else this._symlinkPaths[fullPath] = true; + + this._readyCount++; + + fs.realpath(linkPath, function(error, linkTarget) { + if (this._handleError(error) || this._isIgnored(linkTarget)) { + return this._emitReady(); + } + + this._readyCount++; + + // add the linkTarget for watching with a wrapper for transform + // that causes emitted paths to incorporate the link's path + this._addToFsEvents(linkTarget || linkPath, function(path) { + var dotSlash = '.' + sysPath.sep; + var aliasedPath = linkPath; + if (linkTarget && linkTarget !== dotSlash) { + aliasedPath = path.replace(linkTarget, linkPath); + } else if (path !== dotSlash) { + aliasedPath = sysPath.join(linkPath, path); + } + return transform(aliasedPath); + }, false, curDepth); + }.bind(this)); +}; + +// Private method: Handle added path with fsevents + +// * path - string, file/directory path or glob pattern +// * transform - function, converts working path to what the user expects +// * forceAdd - boolean, ensure add is emitted +// * priorDepth - int, level of subdirectories already traversed + +// Returns nothing +FsEventsHandler.prototype._addToFsEvents = +function(path, transform, forceAdd, priorDepth) { + + // applies transform if provided, otherwise returns same value + var processPath = typeof transform === 'function' ? + transform : function(val) { return val; }; + + var emitAdd = function(newPath, stats) { + var pp = processPath(newPath); + var isDir = stats.isDirectory(); + var dirObj = this._getWatchedDir(sysPath.dirname(pp)); + var base = sysPath.basename(pp); + + // ensure empty dirs get tracked + if (isDir) this._getWatchedDir(pp); + + if (dirObj.has(base)) return; + dirObj.add(base); + + if (!this.options.ignoreInitial || forceAdd === true) { + this._emit(isDir ? 'addDir' : 'add', pp, stats); + } + }.bind(this); + + var wh = this._getWatchHelpers(path); + + // evaluate what is at the path we're being asked to watch + fs[wh.statMethod](wh.watchPath, function(error, stats) { + if (this._handleError(error) || this._isIgnored(wh.watchPath, stats)) { + this._emitReady(); + return this._emitReady(); + } + + if (stats.isDirectory()) { + // emit addDir unless this is a glob parent + if (!wh.globFilter) emitAdd(processPath(path), stats); + + // don't recurse further if it would exceed depth setting + if (priorDepth && priorDepth > this.options.depth) return; + + // scan the contents of the dir + readdirp({ + root: wh.watchPath, + entryType: 'all', + fileFilter: wh.filterPath, + directoryFilter: wh.filterDir, + lstat: true, + depth: this.options.depth - (priorDepth || 0) + }).on('data', function(entry) { + // need to check filterPath on dirs b/c filterDir is less restrictive + if (entry.stat.isDirectory() && !wh.filterPath(entry)) return; + + var joinedPath = sysPath.join(wh.watchPath, entry.path); + var fullPath = entry.fullPath; + + if (wh.followSymlinks && entry.stat.isSymbolicLink()) { + // preserve the current depth here since it can't be derived from + // real paths past the symlink + var curDepth = this.options.depth === undefined ? + undefined : depth(joinedPath, sysPath.resolve(wh.watchPath)) + 1; + + this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth); + } else { + emitAdd(joinedPath, entry.stat); + } + }.bind(this)).on('error', function() { + // Ignore readdirp errors + }).on('end', this._emitReady); + } else { + emitAdd(wh.watchPath, stats); + this._emitReady(); + } + }.bind(this)); + + if (this.options.persistent && forceAdd !== true) { + var initWatch = function(error, realPath) { + if (this.closed) return; + var closer = this._watchWithFsEvents( + wh.watchPath, + sysPath.resolve(realPath || wh.watchPath), + processPath, + wh.globFilter + ); + if (closer) this._closers[path] = closer; + }.bind(this); + + if (typeof transform === 'function') { + // realpath has already been resolved + initWatch(); + } else { + fs.realpath(wh.watchPath, initWatch); + } + } +}; + +module.exports = FsEventsHandler; +module.exports.canUse = canUse; diff --git a/node_modules/chokidar/lib/nodefs-handler.js b/node_modules/chokidar/lib/nodefs-handler.js new file mode 100644 index 0000000..c287732 --- /dev/null +++ b/node_modules/chokidar/lib/nodefs-handler.js @@ -0,0 +1,488 @@ +'use strict'; + +var fs = require('fs'); +var sysPath = require('path'); +var readdirp = require('readdirp'); +var isBinaryPath = require('is-binary-path'); + +// fs.watch helpers + +// object to hold per-process fs.watch instances +// (may be shared across chokidar FSWatcher instances) +var FsWatchInstances = Object.create(null); + +// Private function: Instantiates the fs.watch interface + +// * path - string, path to be watched +// * options - object, options to be passed to fs.watch +// * listener - function, main event handler +// * errHandler - function, handler which emits info about errors +// * emitRaw - function, handler which emits raw event data + +// Returns new fsevents instance +function createFsWatchInstance(path, options, listener, errHandler, emitRaw) { + var handleEvent = function(rawEvent, evPath) { + listener(path); + emitRaw(rawEvent, evPath, {watchedPath: path}); + + // emit based on events occuring for files from a directory's watcher in + // case the file's watcher misses it (and rely on throttling to de-dupe) + if (evPath && path !== evPath) { + fsWatchBroadcast( + sysPath.resolve(path, evPath), 'listeners', sysPath.join(path, evPath) + ); + } + }; + try { + return fs.watch(path, options, handleEvent); + } catch (error) { + errHandler(error); + } +} + +// Private function: Helper for passing fs.watch event data to a +// collection of listeners + +// * fullPath - string, absolute path bound to the fs.watch instance +// * type - string, listener type +// * val[1..3] - arguments to be passed to listeners + +// Returns nothing +function fsWatchBroadcast(fullPath, type, val1, val2, val3) { + if (!FsWatchInstances[fullPath]) return; + FsWatchInstances[fullPath][type].forEach(function(listener) { + listener(val1, val2, val3); + }); +} + +// Private function: Instantiates the fs.watch interface or binds listeners +// to an existing one covering the same file system entry + +// * path - string, path to be watched +// * fullPath - string, absolute path +// * options - object, options to be passed to fs.watch +// * handlers - object, container for event listener functions + +// Returns close function +function setFsWatchListener(path, fullPath, options, handlers) { + var listener = handlers.listener; + var errHandler = handlers.errHandler; + var rawEmitter = handlers.rawEmitter; + var container = FsWatchInstances[fullPath]; + var watcher; + if (!options.persistent) { + watcher = createFsWatchInstance( + path, options, listener, errHandler, rawEmitter + ); + return watcher.close.bind(watcher); + } + if (!container) { + watcher = createFsWatchInstance( + path, + options, + fsWatchBroadcast.bind(null, fullPath, 'listeners'), + errHandler, // no need to use broadcast here + fsWatchBroadcast.bind(null, fullPath, 'rawEmitters') + ); + if (!watcher) return; + var broadcastErr = fsWatchBroadcast.bind(null, fullPath, 'errHandlers'); + watcher.on('error', function(error) { + container.watcherUnusable = true; // documented since Node 10.4.1 + // Workaround for https://github.com/joyent/node/issues/4337 + if (process.platform === 'win32' && error.code === 'EPERM') { + fs.open(path, 'r', function(err, fd) { + if (!err) fs.close(fd, function(err) { + if (!err) broadcastErr(error); + }); + }); + } else { + broadcastErr(error); + } + }); + container = FsWatchInstances[fullPath] = { + listeners: [listener], + errHandlers: [errHandler], + rawEmitters: [rawEmitter], + watcher: watcher + }; + } else { + container.listeners.push(listener); + container.errHandlers.push(errHandler); + container.rawEmitters.push(rawEmitter); + } + var listenerIndex = container.listeners.length - 1; + + // removes this instance's listeners and closes the underlying fs.watch + // instance if there are no more listeners left + return function close() { + delete container.listeners[listenerIndex]; + delete container.errHandlers[listenerIndex]; + delete container.rawEmitters[listenerIndex]; + if (!Object.keys(container.listeners).length) { + if (!container.watcherUnusable) { // check to protect against issue #730 + container.watcher.close(); + } + delete FsWatchInstances[fullPath]; + } + }; +} + +// fs.watchFile helpers + +// object to hold per-process fs.watchFile instances +// (may be shared across chokidar FSWatcher instances) +var FsWatchFileInstances = Object.create(null); + +// Private function: Instantiates the fs.watchFile interface or binds listeners +// to an existing one covering the same file system entry + +// * path - string, path to be watched +// * fullPath - string, absolute path +// * options - object, options to be passed to fs.watchFile +// * handlers - object, container for event listener functions + +// Returns close function +function setFsWatchFileListener(path, fullPath, options, handlers) { + var listener = handlers.listener; + var rawEmitter = handlers.rawEmitter; + var container = FsWatchFileInstances[fullPath]; + var listeners = []; + var rawEmitters = []; + if ( + container && ( + container.options.persistent < options.persistent || + container.options.interval > options.interval + ) + ) { + // "Upgrade" the watcher to persistence or a quicker interval. + // This creates some unlikely edge case issues if the user mixes + // settings in a very weird way, but solving for those cases + // doesn't seem worthwhile for the added complexity. + listeners = container.listeners; + rawEmitters = container.rawEmitters; + fs.unwatchFile(fullPath); + container = false; + } + if (!container) { + listeners.push(listener); + rawEmitters.push(rawEmitter); + container = FsWatchFileInstances[fullPath] = { + listeners: listeners, + rawEmitters: rawEmitters, + options: options, + watcher: fs.watchFile(fullPath, options, function(curr, prev) { + container.rawEmitters.forEach(function(rawEmitter) { + rawEmitter('change', fullPath, {curr: curr, prev: prev}); + }); + var currmtime = curr.mtime.getTime(); + if (curr.size !== prev.size || currmtime > prev.mtime.getTime() || currmtime === 0) { + container.listeners.forEach(function(listener) { + listener(path, curr); + }); + } + }) + }; + } else { + container.listeners.push(listener); + container.rawEmitters.push(rawEmitter); + } + var listenerIndex = container.listeners.length - 1; + + // removes this instance's listeners and closes the underlying fs.watchFile + // instance if there are no more listeners left + return function close() { + delete container.listeners[listenerIndex]; + delete container.rawEmitters[listenerIndex]; + if (!Object.keys(container.listeners).length) { + fs.unwatchFile(fullPath); + delete FsWatchFileInstances[fullPath]; + } + }; +} + +// fake constructor for attaching nodefs-specific prototype methods that +// will be copied to FSWatcher's prototype +function NodeFsHandler() {} + +// Private method: Watch file for changes with fs.watchFile or fs.watch. + +// * path - string, path to file or directory. +// * listener - function, to be executed on fs change. + +// Returns close function for the watcher instance +NodeFsHandler.prototype._watchWithNodeFs = +function(path, listener) { + var directory = sysPath.dirname(path); + var basename = sysPath.basename(path); + var parent = this._getWatchedDir(directory); + parent.add(basename); + var absolutePath = sysPath.resolve(path); + var options = {persistent: this.options.persistent}; + if (!listener) listener = Function.prototype; // empty function + + var closer; + if (this.options.usePolling) { + options.interval = this.enableBinaryInterval && isBinaryPath(basename) ? + this.options.binaryInterval : this.options.interval; + closer = setFsWatchFileListener(path, absolutePath, options, { + listener: listener, + rawEmitter: this.emit.bind(this, 'raw') + }); + } else { + closer = setFsWatchListener(path, absolutePath, options, { + listener: listener, + errHandler: this._handleError.bind(this), + rawEmitter: this.emit.bind(this, 'raw') + }); + } + return closer; +}; + +// Private method: Watch a file and emit add event if warranted + +// * file - string, the file's path +// * stats - object, result of fs.stat +// * initialAdd - boolean, was the file added at watch instantiation? +// * callback - function, called when done processing as a newly seen file + +// Returns close function for the watcher instance +NodeFsHandler.prototype._handleFile = +function(file, stats, initialAdd, callback) { + var dirname = sysPath.dirname(file); + var basename = sysPath.basename(file); + var parent = this._getWatchedDir(dirname); + + // if the file is already being watched, do nothing + if (parent.has(basename)) return callback(); + + // kick off the watcher + var closer = this._watchWithNodeFs(file, function(path, newStats) { + if (!this._throttle('watch', file, 5)) return; + if (!newStats || newStats && newStats.mtime.getTime() === 0) { + fs.stat(file, function(error, newStats) { + // Fix issues where mtime is null but file is still present + if (error) { + this._remove(dirname, basename); + } else { + this._emit('change', file, newStats); + } + }.bind(this)); + // add is about to be emitted if file not already tracked in parent + } else if (parent.has(basename)) { + this._emit('change', file, newStats); + } + }.bind(this)); + + // emit an add event if we're supposed to + if (!(initialAdd && this.options.ignoreInitial)) { + if (!this._throttle('add', file, 0)) return; + this._emit('add', file, stats); + } + + if (callback) callback(); + return closer; +}; + +// Private method: Handle symlinks encountered while reading a dir + +// * entry - object, entry object returned by readdirp +// * directory - string, path of the directory being read +// * path - string, path of this item +// * item - string, basename of this item + +// Returns true if no more processing is needed for this entry. +NodeFsHandler.prototype._handleSymlink = +function(entry, directory, path, item) { + var full = entry.fullPath; + var dir = this._getWatchedDir(directory); + + if (!this.options.followSymlinks) { + // watch symlink directly (don't follow) and detect changes + this._readyCount++; + fs.realpath(path, function(error, linkPath) { + if (dir.has(item)) { + if (this._symlinkPaths[full] !== linkPath) { + this._symlinkPaths[full] = linkPath; + this._emit('change', path, entry.stat); + } + } else { + dir.add(item); + this._symlinkPaths[full] = linkPath; + this._emit('add', path, entry.stat); + } + this._emitReady(); + }.bind(this)); + return true; + } + + // don't follow the same symlink more than once + if (this._symlinkPaths[full]) return true; + else this._symlinkPaths[full] = true; +}; + +// Private method: Read directory to add / remove files from `@watched` list +// and re-read it on change. + +// * dir - string, fs path. +// * stats - object, result of fs.stat +// * initialAdd - boolean, was the file added at watch instantiation? +// * depth - int, depth relative to user-supplied path +// * target - string, child path actually targeted for watch +// * wh - object, common watch helpers for this path +// * callback - function, called when dir scan is complete + +// Returns close function for the watcher instance +NodeFsHandler.prototype._handleDir = +function(dir, stats, initialAdd, depth, target, wh, callback) { + var parentDir = this._getWatchedDir(sysPath.dirname(dir)); + var tracked = parentDir.has(sysPath.basename(dir)); + if (!(initialAdd && this.options.ignoreInitial) && !target && !tracked) { + if (!wh.hasGlob || wh.globFilter(dir)) this._emit('addDir', dir, stats); + } + + // ensure dir is tracked (harmless if redundant) + parentDir.add(sysPath.basename(dir)); + this._getWatchedDir(dir); + + var read = function(directory, initialAdd, done) { + // Normalize the directory name on Windows + directory = sysPath.join(directory, ''); + + if (!wh.hasGlob) { + var throttler = this._throttle('readdir', directory, 1000); + if (!throttler) return; + } + + var previous = this._getWatchedDir(wh.path); + var current = []; + + readdirp({ + root: directory, + entryType: 'all', + fileFilter: wh.filterPath, + directoryFilter: wh.filterDir, + depth: 0, + lstat: true + }).on('data', function(entry) { + var item = entry.path; + var path = sysPath.join(directory, item); + current.push(item); + + if (entry.stat.isSymbolicLink() && + this._handleSymlink(entry, directory, path, item)) return; + + // Files that present in current directory snapshot + // but absent in previous are added to watch list and + // emit `add` event. + if (item === target || !target && !previous.has(item)) { + this._readyCount++; + + // ensure relativeness of path is preserved in case of watcher reuse + path = sysPath.join(dir, sysPath.relative(dir, path)); + + this._addToNodeFs(path, initialAdd, wh, depth + 1); + } + }.bind(this)).on('end', function() { + var wasThrottled = throttler ? throttler.clear() : false; + if (done) done(); + + // Files that absent in current directory snapshot + // but present in previous emit `remove` event + // and are removed from @watched[directory]. + previous.children().filter(function(item) { + return item !== directory && + current.indexOf(item) === -1 && + // in case of intersecting globs; + // a path may have been filtered out of this readdir, but + // shouldn't be removed because it matches a different glob + (!wh.hasGlob || wh.filterPath({ + fullPath: sysPath.resolve(directory, item) + })); + }).forEach(function(item) { + this._remove(directory, item); + }, this); + + // one more time for any missed in case changes came in extremely quickly + if (wasThrottled) read(directory, false); + }.bind(this)).on('error', this._handleError.bind(this)); + }.bind(this); + + var closer; + + if (this.options.depth == null || depth <= this.options.depth) { + if (!target) read(dir, initialAdd, callback); + closer = this._watchWithNodeFs(dir, function(dirPath, stats) { + // if current directory is removed, do nothing + if (stats && stats.mtime.getTime() === 0) return; + + read(dirPath, false); + }); + } else { + callback(); + } + return closer; +}; + +// Private method: Handle added file, directory, or glob pattern. +// Delegates call to _handleFile / _handleDir after checks. + +// * path - string, path to file or directory. +// * initialAdd - boolean, was the file added at watch instantiation? +// * depth - int, depth relative to user-supplied path +// * target - string, child path actually targeted for watch +// * callback - function, indicates whether the path was found or not + +// Returns nothing +NodeFsHandler.prototype._addToNodeFs = +function(path, initialAdd, priorWh, depth, target, callback) { + if (!callback) callback = Function.prototype; + var ready = this._emitReady; + if (this._isIgnored(path) || this.closed) { + ready(); + return callback(null, false); + } + + var wh = this._getWatchHelpers(path, depth); + if (!wh.hasGlob && priorWh) { + wh.hasGlob = priorWh.hasGlob; + wh.globFilter = priorWh.globFilter; + wh.filterPath = priorWh.filterPath; + wh.filterDir = priorWh.filterDir; + } + + // evaluate what is at the path we're being asked to watch + fs[wh.statMethod](wh.watchPath, function(error, stats) { + if (this._handleError(error)) return callback(null, path); + if (this._isIgnored(wh.watchPath, stats)) { + ready(); + return callback(null, false); + } + + var initDir = function(dir, target) { + return this._handleDir(dir, stats, initialAdd, depth, target, wh, ready); + }.bind(this); + + var closer; + if (stats.isDirectory()) { + closer = initDir(wh.watchPath, target); + } else if (stats.isSymbolicLink()) { + var parent = sysPath.dirname(wh.watchPath); + this._getWatchedDir(parent).add(wh.watchPath); + this._emit('add', wh.watchPath, stats); + closer = initDir(parent, path); + + // preserve this symlink's target path + fs.realpath(path, function(error, targetPath) { + this._symlinkPaths[sysPath.resolve(path)] = targetPath; + ready(); + }.bind(this)); + } else { + closer = this._handleFile(wh.watchPath, stats, initialAdd, ready); + } + + if (closer) this._closers[path] = closer; + callback(null, false); + }.bind(this)); +}; + +module.exports = NodeFsHandler; diff --git a/node_modules/chokidar/package.json b/node_modules/chokidar/package.json new file mode 100644 index 0000000..1fdf259 --- /dev/null +++ b/node_modules/chokidar/package.json @@ -0,0 +1,92 @@ +{ + "_args": [ + [ + "chokidar@2.0.4", + "/home/dstaesse/git/website" + ] + ], + "_development": true, + "_from": "chokidar@2.0.4", + "_id": "chokidar@2.0.4", + "_inBundle": false, + "_integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "_location": "/chokidar", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "chokidar@2.0.4", + "name": "chokidar", + "escapedName": "chokidar", + "rawSpec": "2.0.4", + "saveSpec": null, + "fetchSpec": "2.0.4" + }, + "_requiredBy": [ + "/postcss-cli" + ], + "_resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "_spec": "2.0.4", + "_where": "/home/dstaesse/git/website", + "author": { + "name": "Paul Miller", + "url": "http://paulmillr.com" + }, + "bugs": { + "url": "http://github.com/paulmillr/chokidar/issues" + }, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + }, + "description": "A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.", + "devDependencies": { + "chai": "^3.2.0", + "coveralls": "^3.0.1", + "graceful-fs": "4.1.4", + "mocha": "^5.2.0", + "nyc": "^11.8.0", + "rimraf": "^2.4.3", + "sinon": "^1.10.3", + "sinon-chai": "^2.6.0" + }, + "files": [ + "index.js", + "lib/" + ], + "homepage": "https://github.com/paulmillr/chokidar", + "keywords": [ + "fs", + "watch", + "watchFile", + "watcher", + "watching", + "file", + "fsevents" + ], + "license": "MIT", + "name": "chokidar", + "optionalDependencies": { + "fsevents": "^1.2.2" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/paulmillr/chokidar.git" + }, + "scripts": { + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc mocha --exit" + }, + "version": "2.0.4" +} |