Skip to content

Commit d71e161

Browse files
matthieusiebentargos
authored andcommitted
watch: allow listening for grouped changes
PR-URL: #52722 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent fa71543 commit d71e161

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

lib/internal/watch_mode/files_watcher.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ const { addAbortListener } = require('internal/events/abort_listener');
1919
const { watch } = require('fs');
2020
const { fileURLToPath } = require('internal/url');
2121
const { resolve, dirname } = require('path');
22-
const { setTimeout } = require('timers');
22+
const { setTimeout, clearTimeout } = require('timers');
2323

2424
const supportsRecursiveWatching = process.platform === 'win32' ||
2525
process.platform === 'darwin';
2626

2727
class FilesWatcher extends EventEmitter {
2828
#watchers = new SafeMap();
2929
#filteredFiles = new SafeSet();
30-
#debouncing = new SafeSet();
3130
#depencencyOwners = new SafeMap();
3231
#ownerDependencies = new SafeMap();
32+
#debounceOwners = new SafeSet();
33+
#debounceTimer;
3334
#debounce;
3435
#mode;
3536
#signal;
@@ -80,17 +81,20 @@ class FilesWatcher extends EventEmitter {
8081
}
8182

8283
#onChange(trigger) {
83-
if (this.#debouncing.has(trigger)) {
84-
return;
85-
}
8684
if (this.#mode === 'filter' && !this.#filteredFiles.has(trigger)) {
8785
return;
8886
}
89-
this.#debouncing.add(trigger);
9087
const owners = this.#depencencyOwners.get(trigger);
91-
setTimeout(() => {
92-
this.#debouncing.delete(trigger);
93-
this.emit('changed', { owners });
88+
if (owners) {
89+
for (const owner of owners) {
90+
this.#debounceOwners.add(owner);
91+
}
92+
}
93+
clearTimeout(this.#debounceTimer);
94+
this.#debounceTimer = setTimeout(() => {
95+
this.#debounceTimer = null;
96+
this.emit('changed', { owners: this.#debounceOwners });
97+
this.#debounceOwners.clear();
9498
}, this.#debounce).unref();
9599
}
96100

test/parallel/test-watch-mode-files_watcher.mjs

+23
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ describe('watch mode file watcher', () => {
7070
assert.ok(changesCount < 5);
7171
});
7272

73+
it('should debounce changes on multiple files', async () => {
74+
const files = [];
75+
for (let i = 0; i < 10; i++) {
76+
const file = tmpdir.resolve(`file-debounced-${i}`);
77+
writeFileSync(file, 'written');
78+
watcher.filterFile(file);
79+
files.push(file);
80+
}
81+
82+
files.forEach((file) => writeFileSync(file, '1'));
83+
files.forEach((file) => writeFileSync(file, '2'));
84+
files.forEach((file) => writeFileSync(file, '3'));
85+
files.forEach((file) => writeFileSync(file, '4'));
86+
87+
await setTimeout(200); // debounce * 2
88+
files.forEach((file) => writeFileSync(file, '5'));
89+
const changed = once(watcher, 'changed');
90+
files.forEach((file) => writeFileSync(file, 'after'));
91+
await changed;
92+
// Unfortunately testing that changesCount === 2 is flaky
93+
assert.ok(changesCount < 5);
94+
});
95+
7396
it('should ignore files in watched directory if they are not filtered',
7497
{ skip: !supportsRecursiveWatching }, async () => {
7598
watcher.on('changed', common.mustNotCall());

0 commit comments

Comments
 (0)