From 1a2fda0f568b9e88d7e0f12e9dfda5a1cc6d545a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 4 May 2018 15:38:26 -0700 Subject: [PATCH] Add support to update module resolutions on watches from mono repo like setup with path mapping Fixes #22349 --- src/compiler/resolutionCache.ts | 50 +++++++++++++++---- .../unittests/tsserverProjectSystem.ts | 16 +++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 35e056546c05f..d94bc6d4ae75a 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -59,12 +59,15 @@ 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; @@ -393,18 +396,20 @@ namespace ts { } // Use some ancestor of the root directory + let subDirectory: Path | 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; dirPath = parentPath; dir = getDirectoryPath(dir); } } - return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath); + return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath); } function isPathWithDefaultFailedLookupExtension(path: Path) { @@ -427,7 +432,7 @@ namespace ts { let setAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { // If the failed lookup location path is not one of the supported extensions, // store it in the custom path @@ -439,7 +444,7 @@ namespace ts { setAtRoot = true; } else { - setDirectoryWatcher(dir, dirPath); + setDirectoryWatcher(dir, dirPath, subDirectory); } } } @@ -449,13 +454,20 @@ namespace ts { } } - function setDirectoryWatcher(dir: string, dirPath: Path) { - const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); + function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) { + let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath); if (dirWatcher) { dirWatcher.refCount++; } else { - directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 }); + 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); } } @@ -473,7 +485,7 @@ namespace ts { let removeAtRoot = false; for (const failedLookupLocation of failedLookupLocations) { const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation); - const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); + const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath); if (!ignore) { const refCount = customFailedLookupPaths.get(failedLookupLocationPath); if (refCount) { @@ -490,7 +502,7 @@ namespace ts { removeAtRoot = true; } else { - removeDirectoryWatcher(dirPath); + removeDirectoryWatcher(dirPath, subDirectory); } } } @@ -499,12 +511,30 @@ namespace ts { } } - function removeDirectoryWatcher(dirPath: string) { + function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) { 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); @@ -516,7 +546,7 @@ namespace ts { // 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) { + (dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) { if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) { resolutionHost.onInvalidatedResolution(); } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 3404eb5a767fc..f54bd56f1bae2 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -7861,7 +7861,13 @@ new C();` host.reloadFS(filesAfterCompilation); host.runQueuedTimeoutCallbacks(); - verifyProjectWithResolvedModule(session); + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } }); it("when project recompiles after deleting generated folders", () => { @@ -7878,7 +7884,13 @@ new C();` host.ensureFileOrFolder(recongnizerTextDistTypingFile); host.runQueuedTimeoutCallbacks(); - verifyProjectWithResolvedModule(session); + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } }); }); }