diff --git a/README.md b/README.md index 19908f5d..8bd39c7c 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,10 @@ instantiating the watching as chokidar discovers these file paths (before the `r * `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. +* `reportErrorOnDanglingSymlinks` (default: `false`) When `followSymlinks: true`, Chokidar silently waits for paths under +dangling symlinks to be created. The user might instead want to confine Chokidar to wait for explicitly specified paths only. +When set to `true`, the dangling symlink is reported as an error instead and the absence of underlying symlinked path + is ignored. * `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 diff --git a/index.js b/index.js index 506914ef..70dc95b3 100644 --- a/index.js +++ b/index.js @@ -287,6 +287,9 @@ constructor(_opts) { if (opts.atomic) this._pendingUnlinks = new Map(); if (undef(opts, 'followSymlinks')) opts.followSymlinks = true; + + if (undef(opts, 'reportErrorOnDanglingSymlinks')) opts.reportErrorOnDanglingSymlinks = false; + if (!opts.followSymlinks) opts.reportErrorOnDanglingSymlinks = false; if (undef(opts, 'awaitWriteFinish')) opts.awaitWriteFinish = false; if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {}; @@ -320,7 +323,7 @@ constructor(_opts) { } // You’re frozen when your heart’s not open. - Object.freeze(opts); + Object.freeze(opts); console.log(opts); } // Public methods @@ -589,6 +592,21 @@ _handleError(error) { return error || this.closed; } +/** + * Special handler for dangling symlink errors + * @param {Error} error + * @returns {Boolean} True if Dangling conditions are met + */ +_handleDanglingSymlinkError(error) { + const code = error && error.code; + if (error && this.options.reportErrorOnDanglingSymlinks && code === 'ENOENT') { + error.message = `Dangling Symlink: ${error.message}`; + this.emit('error', error); + return true; + } +} + + /** * Helper utility for throttling * @param {ThrottleType} actionType type being throttled diff --git a/lib/nodefs-handler.js b/lib/nodefs-handler.js index b71e4c32..1a36569d 100644 --- a/lib/nodefs-handler.js +++ b/lib/nodefs-handler.js @@ -515,8 +515,8 @@ async _addToNodeFs(path, initialAdd, priorWh, depth, target) { // evaluate what is at the path we're being asked to watch try { - const stats = await statMethods[wh.statMethod](wh.watchPath); - if (this.fsw._isIgnored(wh.watchPath, stats)) { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw._isIgnored(wh.watchPath, stats)) { ready(); return false; } @@ -547,8 +547,16 @@ async _addToNodeFs(path, initialAdd, priorWh, depth, target) { this.fsw._addPathCloser(path, closer); return false; - } catch (error) { - if (this.fsw._handleError(error)) return path; + } catch (error) { + if (this.fsw._handleError(error)) { + // If path is a symlink, test if the error is due to a dangling symlink + // Based on the options user might want to report it and move on + // (rather than having to wait for the path to be recreated) + if (this.fsw._symlinkPaths.has(wh.watchPath)) { + if (this.fsw._handleDanglingSymlinkError(error)) ready(); + } + return path; + } } } diff --git a/types/index.d.ts b/types/index.d.ts index 5138507f..efc2a6fc 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -93,6 +93,15 @@ export interface WatchOptions { */ followSymlinks?: boolean; + /** + * When `followSymlinks: true`, Chokidar silently waits for paths under dangling symlinks to be + * created. The user might instead want to confine themselves to watch explicitly specified paths. + * + * If set to `true`, the dangling symlink is reported as an error and Chokidar ignores + * the absence of the underlying path. + */ + reportErrorOnDanglingSymlinks?: boolean; + /** * The base directory from which watch `paths` are to be derived. Paths emitted with events will * be relative to this.