diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 8b48aa5c041..b4c428d5e5a 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -60,15 +60,12 @@ namespace ts { watcher: FileWatcher; /** ref count keeping this directory watch alive */ refCount: number; - /** map of refcount for the subDirectory */ - subDirectoryMap?: Map; } interface DirectoryOfFailedLookupWatch { dir: string; dirPath: Path; ignore?: true; - subDirectory?: Path; } export const maxNumberOfFilesToIterateForInvalidation = 256; @@ -403,20 +400,21 @@ namespace ts { } // Use some ancestor of the root directory - let subDirectory: Path | undefined; + let subDirectoryPath: Path | undefined, subDirectory: string | undefined; if (rootPath !== undefined) { while (!isInDirectoryPath(dirPath, rootPath)) { const parentPath = getDirectoryPath(dirPath); if (parentPath === dirPath) { break; } - subDirectory = dirPath.slice(parentPath.length + directorySeparator.length) as Path; + subDirectoryPath = dirPath; + subDirectory = dir; dirPath = parentPath; dir = getDirectoryPath(dir); } } - return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath); + return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath }, dirPath); } function isPathWithDefaultFailedLookupExtension(path: Path) { @@ -439,7 +437,7 @@ namespace ts { let setAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { // If the failed lookup location path is not one of the supported extensions, // store it in the custom path @@ -451,7 +449,7 @@ namespace ts { setAtRoot = true; } else { - setDirectoryWatcher(dir, dirPath, subDirectory); + setDirectoryWatcher(dir, dirPath); } } } @@ -461,20 +459,13 @@ namespace ts { } } - function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) { - let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); + function setDirectoryWatcher(dir: string, dirPath: Path) { + const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); if (dirWatcher) { dirWatcher.refCount++; } else { - dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 }; - directoryWatchesOfFailedLookups.set(dirPath, dirWatcher); - } - - if (subDirectory) { - const subDirectoryMap = dirWatcher.subDirectoryMap || (dirWatcher.subDirectoryMap = createMap()); - const existing = subDirectoryMap.get(subDirectory) || 0; - subDirectoryMap.set(subDirectory, existing + 1); + directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 }); } } @@ -492,7 +483,7 @@ namespace ts { let removeAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { const refCount = customFailedLookupPaths.get(failedLookupLocationPath); if (refCount) { @@ -509,7 +500,7 @@ namespace ts { removeAtRoot = true; } else { - removeDirectoryWatcher(dirPath, subDirectory); + removeDirectoryWatcher(dirPath); } } } @@ -518,30 +509,12 @@ namespace ts { } } - function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) { + function removeDirectoryWatcher(dirPath: string) { const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath)!; - if (subDirectory) { - const existing = dirWatcher.subDirectoryMap!.get(subDirectory)!; - if (existing === 1) { - dirWatcher.subDirectoryMap!.delete(subDirectory); - } - else { - dirWatcher.subDirectoryMap!.set(subDirectory, existing - 1); - } - } // Do not close the watcher yet since it might be needed by other failed lookup locations. dirWatcher.refCount--; } - function inWatchedSubdirectory(dirPath: Path, fileOrDirectoryPath: Path) { - const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); - if (!dirWatcher || !dirWatcher.subDirectoryMap) return false; - return forEachKey(dirWatcher.subDirectoryMap, subDirectory => { - const fullSubDirectory = `${dirPath}/${subDirectory}` as Path; - return fullSubDirectory === fileOrDirectoryPath || isInDirectoryPath(fullSubDirectory, fileOrDirectoryPath); - }); - } - function createDirectoryWatcher(directory: string, dirPath: Path) { return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => { const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory); @@ -550,13 +523,8 @@ namespace ts { cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } - // If the files are added to project root or node_modules directory, always run through the invalidation process - // Otherwise run through invalidation only if adding to the immediate directory - if (!allFilesHaveInvalidatedResolution && - (dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) { - if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) { - resolutionHost.onInvalidatedResolution(); - } + if (!allFilesHaveInvalidatedResolution && invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) { + resolutionHost.onInvalidatedResolution(); } }, WatchDirectoryFlags.Recursive); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index bd157ac5763..e6fffbc5e79 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -2533,7 +2533,7 @@ declare module "fs" { createWatchOfConfigFile("tsconfig.json", host); checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1); checkWatchedDirectories(host, emptyArray, /*recursive*/ false); - checkWatchedDirectoriesDetailed(host, [mainPackageRoot, projectRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true); + checkWatchedDirectoriesDetailed(host, [mainPackageRoot, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true); }); }); } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 88fe374c398..4518a740a85 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -7839,7 +7839,7 @@ new C();` const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile]; const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, withPathMapping ? packages : recognizersDateTime, ...getTypeRootsFromLocation(recognizersDateTime)]; - const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)]; + const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...(withPathMapping ? [recognizersText] : emptyArray), ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)]; function verifyProjectWithResolvedModule(session: TestSession) { const projectService = session.getProjectService();