Skip to content

Commit 6556212

Browse 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 a540689 commit 6556212

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

lib/internal/watch_mode/files_watcher.js

Lines 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

Lines 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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy