Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A better workaround for atomic writes #791

Merged
merged 2 commits into from
Mar 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ chokidar.watch('file', {
},

ignorePermissionErrors: false,
atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
atomic: true
});

```
Expand Down Expand Up @@ -221,12 +221,8 @@ 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`).
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.
Uses a workaround to handle file changes by editors that use
"atomic writes" instead of writing directly to the source file.

### Methods & Events

Expand Down
42 changes: 19 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ function FSWatcher(_opts) {
opts.interval = parseInt(envInterval);
}

// Editor atomic write normalization enabled by default with fs.watch
// Editor atomic write handling 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;

Expand Down Expand Up @@ -176,25 +175,6 @@ 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));
Expand Down Expand Up @@ -355,8 +335,6 @@ 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;
Expand Down Expand Up @@ -620,6 +598,24 @@ 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] === '!') {
Expand Down