Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: paulmillr/chokidar
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3.4.0
Choose a base ref
...
head repository: paulmillr/chokidar
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3.4.1
Choose a head ref
  • 13 commits
  • 8 files changed
  • 4 contributors

Commits on Apr 27, 2020

  1. add safe-edit handling for Linux, fixes #591

    will re-add the watcher on inode change in `fs.watch` mode on Linux
    
    bonus: fixed strange, pretty infrequent race in `fs.watchFile` test case `should emit matching dir events`
    pkit committed Apr 27, 2020
    Copy the full SHA
    38d6ca3 View commit details

Commits on Apr 28, 2020

  1. Merge pull request #1003 from pkit/safe_edit

    add `safe-edit` handling for Linux, fixes #591
    paulmillr authored Apr 28, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b565e6f View commit details

Commits on May 3, 2020

  1. Fix invalid unwatch doc.

    paulmillr authored May 3, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0f7c71c View commit details

Commits on May 11, 2020

  1. Bump eslint from 6.8.0 to 7.0.0

    Bumps [eslint](https://github.com/eslint/eslint) from 6.8.0 to 7.0.0.
    - [Release notes](https://github.com/eslint/eslint/releases)
    - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
    - [Commits](eslint/eslint@v6.8.0...v7.0.0)
    
    Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
    dependabot-preview[bot] authored May 11, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    467ccb6 View commit details
  2. Merge pull request #1008 from paulmillr/dependabot/npm_and_yarn/eslin…

    …t-7.0.0
    
    Bump eslint from 6.8.0 to 7.0.0
    paulmillr authored May 11, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e7349ee View commit details

Commits on May 13, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9e45997 View commit details
  2. Merge pull request #1009 from paulmillr/dependabot/npm_and_yarn/types…

    …/node-14.0.1
    
    Bump @types/node from 13.13.6 to 14.0.1
    paulmillr authored May 13, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3bc3eca View commit details

Commits on Jun 20, 2020

  1. Update README.md

    paulmillr authored Jun 20, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    89c618d View commit details

Commits on Jul 12, 2020

  1. Add failing test case for watcher close affecting other watchers

    If the first watcher that was created gets closed all other watchers will no longer receive events.
    
    Ran into this scenario after we upgraded Next.js to use the latest chokidar that came with the latest stable watchpack. With chokidar v2 (which watchpack previously depended on) this test passes fine. Using v3 it stops reporting.
    timneutkens committed Jul 12, 2020
    Copy the full SHA
    b020f94 View commit details
  2. Copy the full SHA
    d071498 View commit details
  3. Copy the full SHA
    a9c54de View commit details

Commits on Jul 14, 2020

  1. Merge pull request #1018 from timneutkens/add/failing-test-for-closed…

    …-watcher
    
    Ensure closing watchers does not affect other watchers
    paulmillr authored Jul 14, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    bfcde1c View commit details

Commits on Jul 16, 2020

  1. Release 3.4.1.

    paulmillr committed Jul 16, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    paulmillr Paul Miller
    Copy the full SHA
    06c74ca View commit details
Showing with 89 additions and 17 deletions.
  1. +3 −0 .github/full_changelog.md
  2. +8 −2 README.md
  3. +11 −3 index.js
  4. +1 −0 lib/constants.js
  5. +3 −4 lib/fsevents-handler.js
  6. +13 −5 lib/nodefs-handler.js
  7. +3 −3 package.json
  8. +47 −0 test.js
3 changes: 3 additions & 0 deletions .github/full_changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### Chokidar 3.4.1 (Jul 16, 2020)
* Fixes a bug when after a watcher was closed, files were not watched properly on new watch.

### Chokidar 3.4.0 (Apr 26, 2020)
* Support for directory-based symlinks.
* Fix a case on macos when replacing a file with a dir of the same name
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -253,9 +253,9 @@ 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`. `raw` is internal, use it carefully.
* `.unwatch(path / paths)`: **async** Stop watching files, directories, or glob patterns.
* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
Takes an array of strings or just one string. Use with `await` to ensure bugs don't happen.
* `.close()`: Removes all listeners from watched files. Asynchronous, returns Promise.
* `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise.
* `.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
@@ -295,6 +295,12 @@ For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md
- **v1 (Apr 7, 2015):** Glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
- **v0.1 (Apr 20, 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)

## Also

Why was chokidar named this way? What's the meaning behind it?

>Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four).
## License

MIT (c) Paul Miller (<https://paulmillr.com>), see [LICENSE](LICENSE) file.
14 changes: 11 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -876,16 +876,24 @@ _remove(directory, item, isDirectory) {
}

/**
*
* Closes all watchers for a path
* @param {Path} path
*/
_closePath(path) {
this._closeFile(path)
const dir = sysPath.dirname(path);
this._getWatchedDir(dir).remove(sysPath.basename(path));
}

/**
* Closes only file-specific watchers
* @param {Path} path
*/
_closeFile(path) {
const closers = this._closers.get(path);
if (!closers) return;
closers.forEach(closer => closer());
this._closers.delete(path);
const dir = sysPath.dirname(path);
this._getWatchedDir(dir).remove(sysPath.basename(path));
}

/**
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
@@ -59,3 +59,4 @@ exports.IDENTITY_FN = val => val;

exports.isWindows = platform === 'win32';
exports.isMacos = platform === 'darwin';
exports.isLinux = platform === 'linux';
7 changes: 3 additions & 4 deletions lib/fsevents-handler.js
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ const createFSEventsInstance = (path, callback) => {
* @param {Function} rawEmitter - passes data to listeners of the 'raw' event
* @returns {Function} closer
*/
function setFSEventsListener(path, realPath, listener, rawEmitter, fsw) {
function setFSEventsListener(path, realPath, listener, rawEmitter) {
let watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path;
const parentPath = sysPath.dirname(watchPath);
let cont = FSEventsWatchers.get(watchPath);
@@ -146,7 +146,7 @@ function setFSEventsListener(path, realPath, listener, rawEmitter, fsw) {
listeners: new Set([filteredListener]),
rawEmitter,
watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
if (fsw.closed) return;
if (!cont.listeners.size) return;
const info = fsevents.getInfo(fullPath, flags);
cont.listeners.forEach(list => {
list(fullPath, flags, info);
@@ -353,8 +353,7 @@ _watchWithFsEvents(watchPath, realPath, transform, globFilter) {
watchPath,
realPath,
watchCallback,
this.fsw._emitRaw,
this.fsw
this.fsw._emitRaw
);

this.fsw._emitReady();
18 changes: 13 additions & 5 deletions lib/nodefs-handler.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ const { promisify } = require('util');
const isBinaryPath = require('is-binary-path');
const {
isWindows,
isLinux,
EMPTY_FN,
EMPTY_STR,
KEY_LISTENERS,
@@ -356,8 +357,7 @@ _handleFile(file, stats, initialAdd) {
// if the file is already being watched, do nothing
if (parent.has(basename)) return;

// kick off the watcher
const closer = this._watchWithNodeFs(file, async (path, newStats) => {
const listener = async (path, newStats) => {
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return;
if (!newStats || newStats.mtimeMs === 0) {
try {
@@ -369,12 +369,18 @@ _handleFile(file, stats, initialAdd) {
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV_CHANGE, file, newStats);
}
prevStats = newStats;
if (isLinux && prevStats.ino !== newStats.ino) {
this.fsw._closeFile(path)
prevStats = newStats;
this.fsw._addPathCloser(path, this._watchWithNodeFs(file, listener));
} else {
prevStats = newStats;
}
} catch (error) {
// Fix issues where mtime is null but file is still present
this.fsw._remove(dirname, basename);
}
// add is about to be emitted if file not already tracked in parent
// add is about to be emitted if file not already tracked in parent
} else if (parent.has(basename)) {
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
@@ -384,7 +390,9 @@ _handleFile(file, stats, initialAdd) {
}
prevStats = newStats;
}
});
}
// kick off the watcher
const closer = this._watchWithNodeFs(file, listener);

// emit an add event if we're supposed to
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "chokidar",
"description": "A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.",
"version": "3.4.0",
"version": "3.4.1",
"homepage": "https://github.com/paulmillr/chokidar",
"author": "Paul Miller (https://paulmillr.com)",
"contributors": [
@@ -25,10 +25,10 @@
"fsevents": "~2.1.2"
},
"devDependencies": {
"@types/node": "^13",
"@types/node": "^14",
"chai": "^4.2",
"dtslint": "^3.3.0",
"eslint": "^6.6.0",
"eslint": "^7.0.0",
"mocha": "^7.0.0",
"nyc": "^15.0.0",
"rimraf": "^3.0.0",
47 changes: 47 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -600,6 +600,27 @@ const runTests = (baseopts) => {
spy.should.have.always.been.calledWith(EV_ADD, testPath);
});

it('should detect safe-edit', async () => {
const testPath = getFixturePath('change.txt');
const safePath = getFixturePath('tmp.txt');
await write(testPath, dateNow());
const watcher = chokidar_watch(testPath, options);
const spy = await aspy(watcher, EV_ALL);

await delay();
await write(safePath, dateNow());
await fs_rename(safePath, testPath);
await delay(100);
await write(safePath, dateNow());
await fs_rename(safePath, testPath);
await delay(100);
await write(safePath, dateNow());
await fs_rename(safePath, testPath);
await waitFor([spy]);
spy.withArgs(EV_CHANGE, testPath).should.have.been.calledThrice;
});


// PR 682 is failing.
describe.skip('Skipping gh-682: should detect unlink', () => {
it('should detect unlink while watching a non-existent second file in another directory', async () => {
@@ -1007,6 +1028,7 @@ const runTests = (baseopts) => {
const watcher = chokidar_watch(watchPaths, options);
const spy = await aspy(watcher, EV_ALL);

await waitFor([spy.withArgs(EV_ADD_DIR)]);
spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('subdir'));
spy.withArgs(EV_ADD_DIR).should.have.been.calledOnce;
fs.mkdirSync(deepDir, PERM_ARR);
@@ -1962,6 +1984,31 @@ const runTests = (baseopts) => {
})();
});
});
it('should not ignore further events on close with existing watchers', async () => {
return new Promise((resolve) => {
const watcher1 = chokidar_watch(currentDir);
const watcher2 = chokidar_watch(currentDir);
// The EV_ADD event should be called on the second watcher even if the first watcher is closed
watcher2.on(EV_ADD, () => {
watcher2.on(EV_ADD, (path) => {
if (path.endsWith('add.txt')) {
resolve();
}
})
});
(async () => {
await waitForWatcher(watcher1);
await waitForWatcher(watcher2);
// Watcher 1 is closed to ensure events only happen on watcher 2
await watcher1.close();
// Write a new file into the fixtures to test the EV_ADD event
await write(getFixturePath('add.txt'), 'hello');
// Ensures EV_ADD is called. Immediately removing the file causes it to be skipped
await delay(200);
await fs_unlink(getFixturePath('add.txt'));
})()
})
});
it('should not prevent the process from exiting', async () => {
const scriptFile = getFixturePath('script.js');
const scriptContent = `