Skip to content
Open
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
9 changes: 6 additions & 3 deletions lib/internal/watch_mode/files_watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,14 @@ class FilesWatcher extends EventEmitter {

filterFile(file, owner) {
if (!file) return;
if (supportsRecursiveWatching) {
if (supportsRecursiveWatching && this.#mode === 'filter') {
// In filter mode, watch the parent directory with a single recursive
// FSWatcher - changes are then filtered by #filteredFiles in #onChange.
this.watchPath(dirname(file));
} else {
// Having multiple FSWatcher's seems to be slower
// than a single recursive FSWatcher
// In 'all' mode, watch the specific file directly so that unrelated
// files in the same directory do not trigger unnecessary restarts.
// Also used on platforms without recursive watching support.
this.watchPath(file, false);
}
this.#filteredFiles.add(file);
Expand Down
41 changes: 41 additions & 0 deletions test/parallel/test-watch-mode-files_watcher.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,47 @@ describe('watch mode file watcher', () => {
assert.strictEqual(changesCount, 1);
});

// Regression test for https://github.com/nodejs/node/issues/61906
// When --watch-path is used (mode: 'all'), filterFile() is called for
// --env-file entries. It must watch only that specific file, not the
// entire parent directory, so that touching an unrelated file in the
// same directory does not trigger a restart.
it('filterFile in "all" mode should not trigger on unrelated files',
{ skip: !supportsRecursiveWatching }, async () => {
watcher = new FilesWatcher({ debounce: 100, mode: 'all' });
watcher.on('changed', common.mustNotCall(
'unexpected restart triggered by unrelated file change'));

const envFile = tmpdir.resolve('env-no-trigger.env');
const unrelated = tmpdir.resolve('env-unrelated.txt');
writeFileSync(envFile, 'FOO=bar');
writeFileSync(unrelated, 'initial');

watcher.filterFile(envFile);

await setTimeout(common.platformTimeout(100)); // avoid throttling
writeFileSync(unrelated, 'changed');
// Wait long enough to confirm no restart was triggered
await setTimeout(1000);
});

it('filterFile in "all" mode should trigger when the watched file changes',
{ skip: !supportsRecursiveWatching }, async () => {
watcher = new FilesWatcher({ debounce: 100, mode: 'all' });
watcher.on('changed', () => changesCount++);

const envFile = tmpdir.resolve('env-trigger.env');
writeFileSync(envFile, 'FOO=bar');

watcher.filterFile(envFile);

const changed = once(watcher, 'changed');
await setTimeout(common.platformTimeout(100)); // avoid throttling
writeFileSync(envFile, 'FOO=newvalue');
await changed;
assert.strictEqual(changesCount, 1);
});

it('should ruse existing watcher if it exists',
{ skip: !supportsRecursiveWatching }, () => {
assert.deepStrictEqual(watcher.watchedPaths, []);
Expand Down
Loading