Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7229a29

Browse filesBrowse files
jazellyjakecastelli
authored andcommitted
watch: check parent and child path properly
Co-authored-by: Jake Yuesong Li <jake.yuesong@gmail.com> PR-URL: #57425 Fixes: #57422 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent bb09b4d commit 7229a29
Copy full SHA for 7229a29

File tree

Expand file treeCollapse file tree

2 files changed

+40
-4
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+40
-4
lines changed

‎lib/internal/watch_mode/files_watcher.js

Copy file name to clipboardExpand all lines: lib/internal/watch_mode/files_watcher.js
+11-3Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const {
77
SafeMap,
88
SafeSet,
99
SafeWeakMap,
10+
StringPrototypeEndsWith,
1011
StringPrototypeStartsWith,
1112
} = primordials;
1213

@@ -18,12 +19,19 @@ const EventEmitter = require('events');
1819
const { addAbortListener } = require('internal/events/abort_listener');
1920
const { watch } = require('fs');
2021
const { fileURLToPath } = require('internal/url');
21-
const { resolve, dirname } = require('path');
22+
const { resolve, dirname, sep } = require('path');
2223
const { setTimeout, clearTimeout } = require('timers');
2324

2425
const supportsRecursiveWatching = process.platform === 'win32' ||
2526
process.platform === 'darwin';
2627

28+
const isParentPath = (parentCandidate, childCandidate) => {
29+
const parent = resolve(parentCandidate);
30+
const child = resolve(childCandidate);
31+
const normalizedParent = StringPrototypeEndsWith(parent, sep) ? parent : parent + sep;
32+
return StringPrototypeStartsWith(child, normalizedParent);
33+
};
34+
2735
class FilesWatcher extends EventEmitter {
2836
#watchers = new SafeMap();
2937
#filteredFiles = new SafeSet();
@@ -58,7 +66,7 @@ class FilesWatcher extends EventEmitter {
5866
}
5967

6068
for (const { 0: watchedPath, 1: watcher } of this.#watchers.entries()) {
61-
if (watcher.recursive && StringPrototypeStartsWith(path, watchedPath)) {
69+
if (watcher.recursive && isParentPath(watchedPath, path)) {
6270
return true;
6371
}
6472
}
@@ -68,7 +76,7 @@ class FilesWatcher extends EventEmitter {
6876

6977
#removeWatchedChildren(path) {
7078
for (const { 0: watchedPath, 1: watcher } of this.#watchers.entries()) {
71-
if (path !== watchedPath && StringPrototypeStartsWith(watchedPath, path)) {
79+
if (path !== watchedPath && isParentPath(path, watchedPath)) {
7280
this.#unwatch(watcher);
7381
this.#watchers.delete(watchedPath);
7482
}

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

Copy file name to clipboardExpand all lines: test/parallel/test-watch-mode-files_watcher.mjs
+29-1Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import path from 'node:path';
66
import assert from 'node:assert';
77
import process from 'node:process';
88
import { describe, it, beforeEach, afterEach } from 'node:test';
9-
import { writeFileSync, mkdirSync } from 'node:fs';
9+
import { writeFileSync, mkdirSync, appendFileSync } from 'node:fs';
10+
import { createInterface } from 'node:readline';
1011
import { setTimeout } from 'node:timers/promises';
1112
import { once } from 'node:events';
1213
import { spawn } from 'node:child_process';
@@ -51,6 +52,33 @@ describe('watch mode file watcher', () => {
5152
assert.strictEqual(changesCount, 1);
5253
});
5354

55+
it('should watch changed files with same prefix path string', async () => {
56+
mkdirSync(tmpdir.resolve('subdir'));
57+
mkdirSync(tmpdir.resolve('sub'));
58+
const file1 = tmpdir.resolve('subdir', 'file1.mjs');
59+
const file2 = tmpdir.resolve('sub', 'file2.mjs');
60+
writeFileSync(file2, 'export const hello = () => { return "hello world"; };');
61+
writeFileSync(file1, 'import { hello } from "../sub/file2.mjs"; console.log(hello());');
62+
63+
const child = spawn(process.execPath,
64+
['--watch', file1],
65+
{ stdio: ['ignore', 'pipe', 'ignore'] });
66+
let completeCount = 0;
67+
for await (const line of createInterface(child.stdout)) {
68+
if (!line.startsWith('Completed running')) {
69+
continue;
70+
}
71+
completeCount++;
72+
if (completeCount === 1) {
73+
appendFileSync(file1, '\n // append 1');
74+
}
75+
// The file is reloaded due to file watching
76+
if (completeCount === 2) {
77+
child.kill();
78+
}
79+
}
80+
});
81+
5482
it('should debounce changes', async () => {
5583
const file = tmpdir.resolve('file2');
5684
writeFileSync(file, 'written');

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.