Skip to content

Commit 3f7357d

Browse files
authored
Merge pull request microsoft#30247 from Microsoft/recursiveDirectoryIgnore
Ignore the directories or files in node_modules starting with "." and any file or directory starting with ".git" even in the recursive directory watching logic
2 parents 825cde2 + ec2ee9f commit 3f7357d

File tree

5 files changed

+97
-7
lines changed

5 files changed

+97
-7
lines changed

src/compiler/resolutionCache.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ namespace ts {
7171
nonRecursive?: boolean;
7272
}
7373

74-
export function isPathInNodeModulesStartingWithDot(path: Path) {
75-
return stringContains(path, "/node_modules/.");
74+
export function isPathIgnored(path: Path) {
75+
return some(ignoredPaths, searchPath => stringContains(path, searchPath));
7676
}
7777

7878
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -696,7 +696,7 @@ namespace ts {
696696
}
697697
else {
698698
// If something to do with folder/file starting with "." in node_modules folder, skip it
699-
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return false;
699+
if (isPathIgnored(fileOrDirectoryPath)) return false;
700700

701701
// Some file or directory in the watching directory is created
702702
// Return early if it does not have any of the watching extension or not the custom failed lookup path

src/compiler/sys.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ namespace ts {
326326
: FileWatcherEventKind.Changed;
327327
}
328328

329+
/*@internal*/
330+
export const ignoredPaths = ["/node_modules/.", "/.git"];
331+
329332
/*@internal*/
330333
export interface RecursiveDirectoryWatcherHost {
331334
watchDirectory: HostWatchDirectory;
@@ -371,6 +374,8 @@ namespace ts {
371374
else {
372375
directoryWatcher = {
373376
watcher: host.watchDirectory(dirName, fileName => {
377+
if (isIgnoredPath(fileName)) return;
378+
374379
// Call the actual callback
375380
callbackCache.forEach((callbacks, rootDirName) => {
376381
if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) {
@@ -426,7 +431,7 @@ namespace ts {
426431
const childFullName = getNormalizedAbsolutePath(child, parentDir);
427432
// Filter our the symbolic link directories since those arent included in recursive watch
428433
// which is same behaviour when recursive: true is passed to fs.watch
429-
return filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
434+
return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
430435
}) : emptyArray,
431436
existingChildWatches,
432437
(child, childWatcher) => filePathComparer(child, childWatcher.dirName),
@@ -452,6 +457,16 @@ namespace ts {
452457
(newChildWatches || (newChildWatches = [])).push(childWatcher);
453458
}
454459
}
460+
461+
function isIgnoredPath(path: string) {
462+
return some(ignoredPaths, searchPath => isInPath(path, searchPath));
463+
}
464+
465+
function isInPath(path: string, searchPath: string) {
466+
if (stringContains(path, searchPath)) return true;
467+
if (host.useCaseSensitiveFileNames) return false;
468+
return stringContains(toCanonicalFilePath(path), searchPath);
469+
}
455470
}
456471

457472
// TODO: GH#18217 Methods on System are often used as if they are certainly defined

src/compiler/watch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ namespace ts {
987987
}
988988
nextSourceFileVersion(fileOrDirectoryPath);
989989

990-
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return;
990+
if (isPathIgnored(fileOrDirectoryPath)) return;
991991

992992
// If the the added or created file or directory is not supported file name, ignore the file
993993
// But when watched directory is added/removed, we need to reload the file list

src/server/editorServices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ namespace ts.server {
10021002
fileOrDirectory => {
10031003
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
10041004
project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
1005-
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return;
1005+
if (isPathIgnored(fileOrDirectoryPath)) return;
10061006
const configFilename = project.getConfigFilePath();
10071007

10081008
// If the the added or created file or directory is not supported file name, ignore the file
@@ -2071,7 +2071,7 @@ namespace ts.server {
20712071
watchDir,
20722072
(fileOrDirectory) => {
20732073
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
2074-
if (isPathInNodeModulesStartingWithDot(fileOrDirectoryPath)) return;
2074+
if (isPathIgnored(fileOrDirectoryPath)) return;
20752075

20762076
// Has extension
20772077
Debug.assert(result.refCount > 0);

src/testRunner/unittests/tsserver/watchEnvironment.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,79 @@ namespace ts.projectSystem {
132132
verifyRootedDirectoryWatch("c:/users/username/");
133133
});
134134
});
135+
136+
it(`unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem recursive watch directory implementation does not watch files/directories in node_modules starting with "."`, () => {
137+
const projectFolder = "/a/username/project";
138+
const projectSrcFolder = `${projectFolder}/src`;
139+
const configFile: File = {
140+
path: `${projectFolder}/tsconfig.json`,
141+
content: "{}"
142+
};
143+
const index: File = {
144+
path: `${projectSrcFolder}/index.ts`,
145+
content: `import {} from "file"`
146+
};
147+
const file1: File = {
148+
path: `${projectSrcFolder}/file1.ts`,
149+
content: ""
150+
};
151+
const nodeModulesExistingUnusedFile: File = {
152+
path: `${projectFolder}/node_modules/someFile.d.ts`,
153+
content: ""
154+
};
155+
156+
const fileNames = [index, file1, configFile, libFile].map(file => file.path);
157+
// All closed files(files other than index), project folder, project/src folder and project/node_modules/@types folder
158+
const expectedWatchedFiles = arrayToMap(fileNames.slice(1), identity, () => 1);
159+
const expectedWatchedDirectories = arrayToMap([projectFolder, projectSrcFolder, `${projectFolder}/${nodeModules}`, `${projectFolder}/${nodeModulesAtTypes}`], identity, () => 1);
160+
161+
const environmentVariables = createMap<string>();
162+
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
163+
const host = createServerHost([index, file1, configFile, libFile, nodeModulesExistingUnusedFile], { environmentVariables });
164+
const projectService = createProjectService(host);
165+
projectService.openClientFile(index.path);
166+
167+
const project = Debug.assertDefined(projectService.configuredProjects.get(configFile.path));
168+
verifyProject();
169+
170+
const nodeModulesIgnoredFileFromIgnoreDirectory: File = {
171+
path: `${projectFolder}/node_modules/.cache/someFile.d.ts`,
172+
content: ""
173+
};
174+
host.ensureFileOrFolder(nodeModulesIgnoredFileFromIgnoreDirectory);
175+
host.checkTimeoutQueueLength(0);
176+
verifyProject();
177+
178+
const nodeModulesIgnoredFile: File = {
179+
path: `${projectFolder}/node_modules/.cacheFile.ts`,
180+
content: ""
181+
};
182+
host.ensureFileOrFolder(nodeModulesIgnoredFile);
183+
host.checkTimeoutQueueLength(0);
184+
verifyProject();
185+
186+
const gitIgnoredFileFromIgnoreDirectory: File = {
187+
path: `${projectFolder}/.git/someFile.d.ts`,
188+
content: ""
189+
};
190+
host.ensureFileOrFolder(gitIgnoredFileFromIgnoreDirectory);
191+
host.checkTimeoutQueueLength(0);
192+
verifyProject();
193+
194+
const gitIgnoredFile: File = {
195+
path: `${projectFolder}/.gitCache.d.ts`,
196+
content: ""
197+
};
198+
host.ensureFileOrFolder(gitIgnoredFile);
199+
host.checkTimeoutQueueLength(0);
200+
verifyProject();
201+
202+
function verifyProject() {
203+
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
204+
checkWatchedFilesDetailed(host, expectedWatchedFiles);
205+
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ false);
206+
checkProjectActualFiles(project, fileNames);
207+
}
208+
});
209+
135210
}

0 commit comments

Comments
 (0)