Skip to content

Commit 939e3e4

Browse files
committed
Instead of creating filter for subDirectories to watch in the ancestor directory of root, watch those subDirectories for failed lookup locations
Before this change, when failed lookup location didnt fall in root directory, we tried to find the ancestor directory of the rootDirectory to watch. We also created subDirectory map for the directories that are being watched so we dont go through invalidation if path is unwanted directory With this change, we will watch those subdirectories instead of root. On windows node supports file system level recursive watching so the earlier approach was better because we reduced number of watches created But on other os, since node doesnt support it, we create the watches for existing folders outselves, so earlier approach becomes expensive. This should be better compromize to satisfy both types of OS. Fixes microsoft#24434
1 parent c7091ab commit 939e3e4

3 files changed

Lines changed: 16 additions & 48 deletions

File tree

src/compiler/resolutionCache.ts

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,12 @@ namespace ts {
6060
watcher: FileWatcher;
6161
/** ref count keeping this directory watch alive */
6262
refCount: number;
63-
/** map of refcount for the subDirectory */
64-
subDirectoryMap?: Map<number>;
6563
}
6664

6765
interface DirectoryOfFailedLookupWatch {
6866
dir: string;
6967
dirPath: Path;
7068
ignore?: true;
71-
subDirectory?: Path;
7269
}
7370

7471
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -403,20 +400,21 @@ namespace ts {
403400
}
404401

405402
// Use some ancestor of the root directory
406-
let subDirectory: Path | undefined;
403+
let subDirectoryPath: Path | undefined, subDirectory: string | undefined;
407404
if (rootPath !== undefined) {
408405
while (!isInDirectoryPath(dirPath, rootPath)) {
409406
const parentPath = getDirectoryPath(dirPath);
410407
if (parentPath === dirPath) {
411408
break;
412409
}
413-
subDirectory = dirPath.slice(parentPath.length + directorySeparator.length) as Path;
410+
subDirectoryPath = dirPath;
411+
subDirectory = dir;
414412
dirPath = parentPath;
415413
dir = getDirectoryPath(dir);
416414
}
417415
}
418416

419-
return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath);
417+
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath }, dirPath);
420418
}
421419

422420
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -439,7 +437,7 @@ namespace ts {
439437
let setAtRoot = false;
440438
for (const failedLookupLocation of failedLookupLocations) {
441439
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
442-
const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
440+
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
443441
if (!ignore) {
444442
// If the failed lookup location path is not one of the supported extensions,
445443
// store it in the custom path
@@ -451,7 +449,7 @@ namespace ts {
451449
setAtRoot = true;
452450
}
453451
else {
454-
setDirectoryWatcher(dir, dirPath, subDirectory);
452+
setDirectoryWatcher(dir, dirPath);
455453
}
456454
}
457455
}
@@ -461,20 +459,13 @@ namespace ts {
461459
}
462460
}
463461

464-
function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) {
465-
let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
462+
function setDirectoryWatcher(dir: string, dirPath: Path) {
463+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
466464
if (dirWatcher) {
467465
dirWatcher.refCount++;
468466
}
469467
else {
470-
dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 };
471-
directoryWatchesOfFailedLookups.set(dirPath, dirWatcher);
472-
}
473-
474-
if (subDirectory) {
475-
const subDirectoryMap = dirWatcher.subDirectoryMap || (dirWatcher.subDirectoryMap = createMap());
476-
const existing = subDirectoryMap.get(subDirectory) || 0;
477-
subDirectoryMap.set(subDirectory, existing + 1);
468+
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
478469
}
479470
}
480471

@@ -492,7 +483,7 @@ namespace ts {
492483
let removeAtRoot = false;
493484
for (const failedLookupLocation of failedLookupLocations) {
494485
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
495-
const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
486+
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
496487
if (!ignore) {
497488
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
498489
if (refCount) {
@@ -509,7 +500,7 @@ namespace ts {
509500
removeAtRoot = true;
510501
}
511502
else {
512-
removeDirectoryWatcher(dirPath, subDirectory);
503+
removeDirectoryWatcher(dirPath);
513504
}
514505
}
515506
}
@@ -518,30 +509,12 @@ namespace ts {
518509
}
519510
}
520511

521-
function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) {
512+
function removeDirectoryWatcher(dirPath: string) {
522513
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath)!;
523-
if (subDirectory) {
524-
const existing = dirWatcher.subDirectoryMap!.get(subDirectory)!;
525-
if (existing === 1) {
526-
dirWatcher.subDirectoryMap!.delete(subDirectory);
527-
}
528-
else {
529-
dirWatcher.subDirectoryMap!.set(subDirectory, existing - 1);
530-
}
531-
}
532514
// Do not close the watcher yet since it might be needed by other failed lookup locations.
533515
dirWatcher.refCount--;
534516
}
535517

536-
function inWatchedSubdirectory(dirPath: Path, fileOrDirectoryPath: Path) {
537-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
538-
if (!dirWatcher || !dirWatcher.subDirectoryMap) return false;
539-
return forEachKey(dirWatcher.subDirectoryMap, subDirectory => {
540-
const fullSubDirectory = `${dirPath}/${subDirectory}` as Path;
541-
return fullSubDirectory === fileOrDirectoryPath || isInDirectoryPath(fullSubDirectory, fileOrDirectoryPath);
542-
});
543-
}
544-
545518
function createDirectoryWatcher(directory: string, dirPath: Path) {
546519
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
547520
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
@@ -550,13 +523,8 @@ namespace ts {
550523
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
551524
}
552525

553-
// If the files are added to project root or node_modules directory, always run through the invalidation process
554-
// Otherwise run through invalidation only if adding to the immediate directory
555-
if (!allFilesHaveInvalidatedResolution &&
556-
(dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) {
557-
if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
558-
resolutionHost.onInvalidatedResolution();
559-
}
526+
if (!allFilesHaveInvalidatedResolution && invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
527+
resolutionHost.onInvalidatedResolution();
560528
}
561529
}, WatchDirectoryFlags.Recursive);
562530
}

src/harness/unittests/tscWatchMode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2533,7 +2533,7 @@ declare module "fs" {
25332533
createWatchOfConfigFile("tsconfig.json", host);
25342534
checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1);
25352535
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
2536-
checkWatchedDirectoriesDetailed(host, [mainPackageRoot, projectRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
2536+
checkWatchedDirectoriesDetailed(host, [mainPackageRoot, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
25372537
});
25382538
});
25392539
}

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7839,7 +7839,7 @@ new C();`
78397839
const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile];
78407840

78417841
const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, withPathMapping ? packages : recognizersDateTime, ...getTypeRootsFromLocation(recognizersDateTime)];
7842-
const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)];
7842+
const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...(withPathMapping ? [recognizersText] : emptyArray), ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)];
78437843

78447844
function verifyProjectWithResolvedModule(session: TestSession) {
78457845
const projectService = session.getProjectService();

0 commit comments

Comments
 (0)