From b79120bcd5be17b300559c6fa292ab25e5164070 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 22 Mar 2019 23:17:14 +0200 Subject: [PATCH] Revert "A better workaround for atomic writes (#791)" This reverts commit 8f56261bb08bf92e8e36ce4ae2e8286b68a90762. --- README.md | 10 +++++++--- index.js | 42 +++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2b65ed65..ab66c179 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ chokidar.watch('file', { }, ignorePermissionErrors: false, - atomic: true + atomic: true // or a custom 'atomicity delay', in milliseconds (default 100) }); ``` @@ -223,8 +223,12 @@ Use with caution. 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`). -Uses a workaround to handle file changes by editors that use -"atomic writes" instead of writing directly to the source file. +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 diff --git a/index.js b/index.js index 755cdc2d..fcf661a7 100644 --- a/index.js +++ b/index.js @@ -113,8 +113,9 @@ function FSWatcher(_opts) { opts.interval = parseInt(envInterval); } - // Editor atomic write handling enabled by default with fs.watch + // 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; @@ -175,6 +176,25 @@ FSWatcher.prototype._emit = function(event, path, val1, val2, val3) { 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)); @@ -335,6 +355,8 @@ FSWatcher.prototype._awaitWriteFinish = function(path, threshold, event, awfEmit // 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; @@ -598,24 +620,6 @@ FSWatcher.prototype.add = function(paths, _origAdd, _internal) { } }); - if (this.options.atomic) { - paths = paths.map(function(path) { - // If `path` is already a glob, we do not have to do anything. - if (isGlob(path)) { - return path; - } - else { - var splits = path.split(sysPath.sep); - if (splits.length && splits[splits.length - 1]) { - // We make the last segment of the path a glob pattern. - // This type of a glob pattern is equivalent to the original name. - splits[splits.length - 1] = '@(' + splits[splits.length - 1] + ')'; - } - return splits.join(sysPath.sep); - } - }); - } - // set aside negated glob strings paths = paths.filter(function(path) { if (path[0] === '!') {