Skip to content

Commit 4e76dec

Browse files
authored
Merge pull request #22976 from Microsoft/childWatches
Do not watch child directories of the sym link folders
2 parents 3e34cad + d8bf95f commit 4e76dec

File tree

3 files changed

+77
-21
lines changed

3 files changed

+77
-21
lines changed

src/compiler/sys.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,10 @@ namespace ts {
334334
/*@internal*/
335335
export interface RecursiveDirectoryWatcherHost {
336336
watchDirectory: HostWatchDirectory;
337-
getAccessileSortedChildDirectories(path: string): ReadonlyArray<string>;
337+
getAccessibleSortedChildDirectories(path: string): ReadonlyArray<string>;
338338
directoryExists(dir: string): boolean;
339339
filePathComparer: Comparer<string>;
340+
realpath(s: string): string;
340341
}
341342

342343
/**
@@ -392,9 +393,14 @@ namespace ts {
392393
function watchChildDirectories(parentDir: string, existingChildWatches: ChildWatches, callback: DirectoryWatcherCallback): ChildWatches {
393394
let newChildWatches: DirectoryWatcher[] | undefined;
394395
enumerateInsertsAndDeletes<string, DirectoryWatcher>(
395-
host.directoryExists(parentDir) ? host.getAccessileSortedChildDirectories(parentDir) : emptyArray,
396+
host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => {
397+
const childFullName = getNormalizedAbsolutePath(child, parentDir);
398+
// Filter our the symbolic link directories since those arent included in recursive watch
399+
// which is same behaviour when recursive: true is passed to fs.watch
400+
return host.filePathComparer(childFullName, host.realpath(childFullName)) === Comparison.EqualTo ? childFullName : undefined;
401+
}) : emptyArray,
396402
existingChildWatches,
397-
(child, childWatcher) => host.filePathComparer(getNormalizedAbsolutePath(child, parentDir), childWatcher.dirName),
403+
(child, childWatcher) => host.filePathComparer(child, childWatcher.dirName),
398404
createAndAddChildDirectoryWatcher,
399405
closeFileWatcher,
400406
addChildDirectoryWatcher
@@ -406,7 +412,7 @@ namespace ts {
406412
* Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
407413
*/
408414
function createAndAddChildDirectoryWatcher(childName: string) {
409-
const result = createDirectoryWatcher(getNormalizedAbsolutePath(childName, parentDir), callback);
415+
const result = createDirectoryWatcher(childName, callback);
410416
addChildDirectoryWatcher(result);
411417
}
412418

@@ -601,14 +607,7 @@ namespace ts {
601607
exit(exitCode?: number): void {
602608
process.exit(exitCode);
603609
},
604-
realpath(path: string): string {
605-
try {
606-
return _fs.realpathSync(path);
607-
}
608-
catch {
609-
return path;
610-
}
611-
},
610+
realpath,
612611
debugMode: some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
613612
tryEnableSourceMapsForHost() {
614613
try {
@@ -699,8 +698,9 @@ namespace ts {
699698
const watchDirectoryRecursively = createRecursiveDirectoryWatcher({
700699
filePathComparer: useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
701700
directoryExists,
702-
getAccessileSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
703-
watchDirectory
701+
getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
702+
watchDirectory,
703+
realpath
704704
});
705705

706706
return (directoryName, callback, recursive) => {
@@ -1043,6 +1043,15 @@ namespace ts {
10431043
return filter<string>(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory));
10441044
}
10451045

1046+
function realpath(path: string): string {
1047+
try {
1048+
return _fs.realpathSync(path);
1049+
}
1050+
catch {
1051+
return path;
1052+
}
1053+
}
1054+
10461055
function getModifiedTime(path: string) {
10471056
try {
10481057
return _fs.statSync(path).mtime;

src/harness/unittests/tscWatchMode.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2358,6 +2358,50 @@ declare module "fs" {
23582358
it("uses non recursive dynamic polling when renaming file in subfolder", () => {
23592359
verifyRenamingFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling);
23602360
});
2361+
2362+
it("when there are symlinks to folders in recursive folders", () => {
2363+
const cwd = "/home/user/projects/myproject";
2364+
const file1: FileOrFolder = {
2365+
path: `${cwd}/src/file.ts`,
2366+
content: `import * as a from "a"`
2367+
};
2368+
const tsconfig: FileOrFolder = {
2369+
path: `${cwd}/tsconfig.json`,
2370+
content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`
2371+
};
2372+
const realA: FileOrFolder = {
2373+
path: `${cwd}/node_modules/reala/index.d.ts`,
2374+
content: `export {}`
2375+
};
2376+
const realB: FileOrFolder = {
2377+
path: `${cwd}/node_modules/realb/index.d.ts`,
2378+
content: `export {}`
2379+
};
2380+
const symLinkA: FileOrFolder = {
2381+
path: `${cwd}/node_modules/a`,
2382+
symLink: `${cwd}/node_modules/reala`
2383+
};
2384+
const symLinkB: FileOrFolder = {
2385+
path: `${cwd}/node_modules/b`,
2386+
symLink: `${cwd}/node_modules/realb`
2387+
};
2388+
const symLinkBInA: FileOrFolder = {
2389+
path: `${cwd}/node_modules/reala/node_modules/b`,
2390+
symLink: `${cwd}/node_modules/b`
2391+
};
2392+
const symLinkAInB: FileOrFolder = {
2393+
path: `${cwd}/node_modules/realb/node_modules/a`,
2394+
symLink: `${cwd}/node_modules/a`
2395+
};
2396+
const files = [file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
2397+
const environmentVariables = createMap<string>();
2398+
environmentVariables.set("TSC_WATCHDIRECTORY", TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory);
2399+
const host = createWatchedSystem(files, { environmentVariables, currentDirectory: cwd });
2400+
createWatchOfConfigFile("tsconfig.json", host);
2401+
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
2402+
checkWatchedDirectories(host, [cwd, `${cwd}/node_modules`, `${cwd}/node_modules/@types`, `${cwd}/node_modules/reala`, `${cwd}/node_modules/realb`,
2403+
`${cwd}/node_modules/reala/node_modules`, `${cwd}/node_modules/realb/node_modules`, `${cwd}/src`], /*recursive*/ false);
2404+
});
23612405
});
23622406
});
23632407
}

src/harness/virtualFileSystemWithWatch.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,28 +312,31 @@ interface Array<T> {}`
312312
const watchDirectory: HostWatchDirectory = (directory, cb) => this.watchFile(directory, () => cb(directory), PollingInterval.Medium);
313313
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
314314
directoryExists: path => this.directoryExists(path),
315-
getAccessileSortedChildDirectories: path => this.getDirectories(path),
315+
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
316316
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
317-
watchDirectory
317+
watchDirectory,
318+
realpath: s => this.realpath(s)
318319
});
319320
}
320321
else if (tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory) {
321322
const watchDirectory: HostWatchDirectory = (directory, cb) => this.watchDirectory(directory, fileName => cb(fileName), /*recursive*/ false);
322323
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
323324
directoryExists: path => this.directoryExists(path),
324-
getAccessileSortedChildDirectories: path => this.getDirectories(path),
325+
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
325326
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
326-
watchDirectory
327+
watchDirectory,
328+
realpath: s => this.realpath(s)
327329
});
328330
}
329331
else if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) {
330332
const watchFile = createDynamicPriorityPollingWatchFile(this);
331333
const watchDirectory: HostWatchDirectory = (directory, cb) => watchFile(directory, () => cb(directory), PollingInterval.Medium);
332334
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
333335
directoryExists: path => this.directoryExists(path),
334-
getAccessileSortedChildDirectories: path => this.getDirectories(path),
336+
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
335337
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
336-
watchDirectory
338+
watchDirectory,
339+
realpath: s => this.realpath(s)
337340
});
338341
}
339342
}
@@ -649,7 +652,7 @@ interface Array<T> {}`
649652

650653
const realpath = this.realpath(path);
651654
if (path !== realpath) {
652-
return this.getRealFsEntry(isFsEntry, realpath as Path);
655+
return this.getRealFsEntry(isFsEntry, this.toPath(realpath));
653656
}
654657

655658
return undefined;

0 commit comments

Comments
 (0)