Skip to content

Commit 50628e7

Browse files
committed
Do not watch root folders for failed lookup locations and effective type roots
Fixes #19170
1 parent 3c45205 commit 50628e7

File tree

3 files changed

+148
-70
lines changed

3 files changed

+148
-70
lines changed

src/compiler/resolutionCache.ts

+58-14
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ namespace ts {
6464
interface DirectoryOfFailedLookupWatch {
6565
dir: string;
6666
dirPath: Path;
67+
ignore?: true;
6768
}
6869

6970
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -319,6 +320,33 @@ namespace ts {
319320
return endsWith(dirPath, "/node_modules");
320321
}
321322

323+
function isDirectoryAtleastAtLevelFromFSRoot(dirPath: Path, minLevels: number) {
324+
for (let searchIndex = getRootLength(dirPath); minLevels > 0; minLevels--) {
325+
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
326+
if (searchIndex === 0) {
327+
// Folder isnt at expected minimun levels
328+
return false;
329+
}
330+
}
331+
return true;
332+
}
333+
334+
function canWatchDirectory(dirPath: Path) {
335+
return isDirectoryAtleastAtLevelFromFSRoot(dirPath,
336+
// When root is "/" do not watch directories like:
337+
// "/", "/user", "/user/username", "/user/username/folderAtRoot"
338+
// When root is "c:/" do not watch directories like:
339+
// "c:/", "c:/folderAtRoot"
340+
dirPath.charCodeAt(0) === CharacterCodes.slash ? 3 : 1);
341+
}
342+
343+
function filterFSRootDirectoriesToWatch(watchPath: DirectoryOfFailedLookupWatch, dirPath: Path): DirectoryOfFailedLookupWatch {
344+
if (!canWatchDirectory(dirPath)) {
345+
watchPath.ignore = true;
346+
}
347+
return watchPath;
348+
}
349+
322350
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
323351
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
324352
return { dir: rootDir, dirPath: rootPath };
@@ -335,7 +363,7 @@ namespace ts {
335363

336364
// If the directory is node_modules use it to watch
337365
if (isNodeModulesDirectory(dirPath)) {
338-
return { dir, dirPath };
366+
return filterFSRootDirectoriesToWatch({ dir, dirPath }, getDirectoryPath(dirPath));
339367
}
340368

341369
// Use some ancestor of the root directory
@@ -350,7 +378,7 @@ namespace ts {
350378
}
351379
}
352380

353-
return { dir, dirPath };
381+
return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath);
354382
}
355383

356384
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -391,13 +419,15 @@ namespace ts {
391419
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
392420
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
393421
}
394-
const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
395-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
396-
if (dirWatcher) {
397-
dirWatcher.refCount++;
398-
}
399-
else {
400-
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
422+
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
423+
if (!ignore) {
424+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
425+
if (dirWatcher) {
426+
dirWatcher.refCount++;
427+
}
428+
else {
429+
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
430+
}
401431
}
402432
}
403433
}
@@ -422,10 +452,12 @@ namespace ts {
422452
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
423453
}
424454
}
425-
const { dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
426-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
427-
// Do not close the watcher yet since it might be needed by other failed lookup locations.
428-
dirWatcher.refCount--;
455+
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
456+
if (!ignore) {
457+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
458+
// Do not close the watcher yet since it might be needed by other failed lookup locations.
459+
dirWatcher.refCount--;
460+
}
429461
}
430462
}
431463

@@ -577,7 +609,8 @@ namespace ts {
577609
}
578610

579611
// we need to assume the directories exist to ensure that we can get all the type root directories that get included
580-
const typeRoots = getEffectiveTypeRoots(options, { directoryExists: returnTrue, getCurrentDirectory });
612+
// But filter directories that are at root level to say directory doesnt exist, so that we arent watching them
613+
const typeRoots = getEffectiveTypeRoots(options, { directoryExists: directoryExistsForTypeRootWatch, getCurrentDirectory });
581614
if (typeRoots) {
582615
mutateMap(
583616
typeRootsWatches,
@@ -592,5 +625,16 @@ namespace ts {
592625
closeTypeRootsWatch();
593626
}
594627
}
628+
629+
/**
630+
* Use this function to return if directory exists to get type roots to watch
631+
* If we return directory exists then only the paths will be added to type roots
632+
* Hence return true for all directories except root directories which are filtered from watching
633+
*/
634+
function directoryExistsForTypeRootWatch(nodeTypesDirectory: string) {
635+
const dir = getDirectoryPath(getDirectoryPath(nodeTypesDirectory));
636+
const dirPath = resolutionHost.toPath(dir);
637+
return dirPath === rootPath || canWatchDirectory(dirPath);
638+
}
595639
}
596640
}

src/harness/unittests/tscWatchMode.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ namespace ts.tscWatch {
254254
checkProgramRootFiles(watch(), [file1.path, file2.path]);
255255
checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]);
256256
const configDir = getDirectoryPath(configFile.path);
257-
checkWatchedDirectories(host, projectSystem.getTypeRootsFromLocation(configDir).concat(configDir), /*recursive*/ true);
257+
checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true);
258258
});
259259

260260
// TODO: if watching for config file creation
@@ -269,7 +269,7 @@ namespace ts.tscWatch {
269269
const host = createWatchedSystem([commonFile1, libFile, configFile]);
270270
const watch = createWatchModeWithConfigFile(configFile.path, host);
271271
const configDir = getDirectoryPath(configFile.path);
272-
checkWatchedDirectories(host, projectSystem.getTypeRootsFromLocation(configDir).concat(configDir), /*recursive*/ true);
272+
checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true);
273273

274274
checkProgramRootFiles(watch(), [commonFile1.path]);
275275

0 commit comments

Comments
 (0)