diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 9d8c3ca9e4ce0..b271fba447fe0 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -56,7 +56,9 @@ import { getOptionsNameMap, getOwnKeys, getRelativePathFromDirectory, - getResolvedFileNameForModuleNameToDirectorySet, + GetResolutionWithResolvedFileName, + getResolvedModuleOfResolution, + getResolvedTypeReferenceDirectiveOfResolution, getTsBuildInfoEmitOutputFilePath, handleNoEmitOptions, HostForComputeHash, @@ -90,6 +92,8 @@ import { ReadBuildProgramHost, ReadonlyCollection, ResolutionMode, + ResolutionWithFailedLookupLocations, + ResolutionWithResolvedFileName, ResolvedModuleFull, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, @@ -104,6 +108,7 @@ import { sourceFileMayBeEmitted, SourceMapEmitResult, toPath, + toPerDirectoryResolution, tryAddToSet, WriteFileCallback, WriteFileCallbackData, @@ -1472,8 +1477,8 @@ function getCacheResolutions(state: BuilderProgramState, getCanonicalFileName: G const dirToPackageJsonMap = new Map(); let perDirPackageJsonMap: Map | undefined; state.program!.getSourceFiles().forEach(f => { - modules = toPerDirectoryCache(state, getCanonicalFileName!, modules, f, f.resolvedModules, moduleNameToDirectoryMap); - typeRefs = toPerDirectoryCache(state, getCanonicalFileName!, typeRefs, f, f.resolvedTypeReferenceDirectiveNames); + modules = toPerDirectoryCache(state, getCanonicalFileName!, modules, getResolvedModuleOfResolution, f, f.resolvedModules, moduleNameToDirectoryMap); + typeRefs = toPerDirectoryCache(state, getCanonicalFileName!, typeRefs, getResolvedTypeReferenceDirectiveOfResolution, f, f.resolvedTypeReferenceDirectiveNames); if (f.packageJsonScope) { const dirPath = getDirectoryPath(f.resolvedPath); if (!dirToPackageJsonMap?.has(dirPath)) { @@ -1494,7 +1499,7 @@ function getCacheResolutions(state: BuilderProgramState, getCanonicalFileName: G if (automaticTypeDirectiveNames.length) { const currentDirectory = state.program!.getCurrentDirectory(); const containingPath = toPath(state.program!.getAutomaticTypeDirectiveContainingFile(), currentDirectory, getCanonicalFileName); - typeRefs = toPerDirectoryCache(state, getCanonicalFileName, typeRefs, containingPath, state.program!.getAutomaticTypeDirectiveResolutions()); + typeRefs = toPerDirectoryCache(state, getCanonicalFileName, typeRefs, getResolvedTypeReferenceDirectiveOfResolution, containingPath, state.program!.getAutomaticTypeDirectiveResolutions()); } return state.cacheResolutions = { modules, @@ -1506,70 +1511,32 @@ function getCacheResolutions(state: BuilderProgramState, getCanonicalFileName: G }; } -function toPerDirectoryCache( +function toPerDirectoryCache( state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName, - cacheWithRedirects: CacheWithRedirects> | undefined, - fOrPath: SourceFile | Path, - cache: ModeAwareCache | undefined, -): CacheWithRedirects> | undefined; -function toPerDirectoryCache( - state: BuilderProgramState, - getCanonicalFileName: GetCanonicalFileName, - cacheWithRedirects: CacheWithRedirects> | undefined, - fOrPath: SourceFile | Path, - cache: ModeAwareCache | undefined, - moduleNameToDirectoryMap: CacheWithRedirects>, -): CacheWithRedirects> | undefined; -function toPerDirectoryCache( - state: BuilderProgramState, - getCanonicalFileName: GetCanonicalFileName, - cacheWithRedirects: CacheWithRedirects> | undefined, - fOrPath: SourceFile | Path, - cache: ModeAwareCache | undefined, + perDirCache: CacheWithRedirects> | undefined, + getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName, + sourceFileOrPath: SourceFile | Path, + fileCacheFromProgram: ModeAwareCache | undefined, moduleNameToDirectoryMap?: CacheWithRedirects> | undefined, ): CacheWithRedirects> | undefined { - if (!cache?.size()) return cacheWithRedirects; - let dirPath: Path, redirectedReference: ResolvedProjectReference | undefined; - if (!isString(fOrPath)) { - redirectedReference = state.program!.getRedirectReferenceForResolution(fOrPath); - dirPath = getDirectoryPath(fOrPath.path); - } - else { - dirPath = getDirectoryPath(fOrPath); - } - let perDirResolutionCache = cacheWithRedirects?.getMapOfCacheRedirects(redirectedReference); - let dirCache = perDirResolutionCache?.get(dirPath); - cache.forEach((resolution, name, mode) => { - if (!(resolution as ResolvedModuleWithFailedLookupLocations).resolvedModule?.resolvedFileName && !(resolution as ResolvedTypeReferenceDirectiveWithFailedLookupLocations).resolvedTypeReferenceDirective?.resolvedFileName) return; - if (dirCache?.has(name, mode)) return; - // If there was already external module resolution that is same, set for child directory, dont set resolution for this directory - if (moduleNameToDirectoryMap && - !isExternalModuleNameRelative(name) && - moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)?.get(createModeAwareCacheKey(name, mode))?.get(dirPath)) { - return; - } - if (!dirCache) { - perDirResolutionCache ??= (cacheWithRedirects ??= createCacheWithRedirects(state.compilerOptions)).getOrCreateMapOfCacheRedirects(redirectedReference); - perDirResolutionCache.set(dirPath, dirCache = createModeAwareCache()); - } - dirCache.set(name, mode, resolution); - if (!moduleNameToDirectoryMap || isExternalModuleNameRelative(name)) return; - // put result in per-module name cache and delete everything that is not needed - const actualModuleNameToDirectoryMap = moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(redirectedReference); - const key = createModeAwareCacheKey(name, mode); - let directoryPathMap = actualModuleNameToDirectoryMap.get(key); - if (!directoryPathMap) actualModuleNameToDirectoryMap.set(key, directoryPathMap = new Map()); - moduleNameToDirectorySet( - directoryPathMap, - dirPath, - resolution as ResolvedModuleWithFailedLookupLocations, - getResolvedFileNameForModuleNameToDirectorySet, - dir => toPath(dir, state.program!.getCurrentDirectory(), getCanonicalFileName), - ancestorPath => perDirResolutionCache?.get(ancestorPath)?.delete(name, mode) - ); - }); - return cacheWithRedirects; + return toPerDirectoryResolution( + state.program!, + fileCacheFromProgram, + sourceFileOrPath, + perDirCache, + moduleNameToDirectoryMap, + noop, + (r, name, mode, redirectedReference, dirPath) => + // If this is not resolved, dont put in per dir Cache + !getResolutionWithResolvedFileName(r)?.resolvedFileName || + // If there was already external module resolution that is same, set for child directory, dont set resolution for this directory + (moduleNameToDirectoryMap && + !isExternalModuleNameRelative(name) && + moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)?.get(createModeAwareCacheKey(name, mode))?.get(dirPath)), + dir => toPath(dir, state.program!.getCurrentDirectory(), getCanonicalFileName), + (ancestorPath, name, mode, perDirResolutionCache) => perDirResolutionCache?.get(ancestorPath)?.delete(name, mode), + ); } /** @internal */ diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 5cbe482f9f45c..be1aa7f335810 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -156,7 +156,8 @@ export interface ResolutionWithFailedLookupLocations { setAtRoot?: boolean; } -interface ResolutionWithResolvedFileName { +/** @internal */ +export interface ResolutionWithResolvedFileName { resolvedFileName: string | undefined; packageId?: PackageId; } @@ -290,9 +291,65 @@ export function canWatchDirectoryOrFile(dirPath: Path) { return true; } -type GetResolutionWithResolvedFileName = +/** @internal */ +export type GetResolutionWithResolvedFileName = (resolution: T) => R | undefined; +/** @internal */ +export function getResolvedModuleOfResolution(resolution: ResolvedModuleWithFailedLookupLocations) { + return resolution.resolvedModule; +} +/** @internal */ +export function getResolvedTypeReferenceDirectiveOfResolution(resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations) { + return resolution.resolvedTypeReferenceDirective; +} +/** @internal */ +export function toPerDirectoryResolution( + newProgram: Program, + fileCacheFromProgram: ModeAwareCache | undefined, + sourceFileOrPath: SourceFile | Path, + perDirCache: CacheWithRedirects> | undefined, + moduleNameToDirectoryMap: CacheWithRedirects> | undefined, + withRedirects: (options: CompilerOptions, redirectedReference: ResolvedProjectReference | undefined) => U, + forEachResolution: (r: T, name: string, mode: ResolutionMode, redirectedReference: ResolvedProjectReference | undefined, dirPath: Path, optionsForFile: CompilerOptions, redirectResult: U) => V | undefined, + toPath: (dir: string) => Path, + ancestoryWorker: (ancestorPath: Path, name: string, mode: ResolutionMode, perDirResolutionCache: Map> | undefined) => void, +): CacheWithRedirects> | undefined { + if (!fileCacheFromProgram?.size()) return perDirCache; + const path = !isString(sourceFileOrPath) ? sourceFileOrPath.path : sourceFileOrPath; + const options = newProgram.getCompilerOptions(); + const redirectedReference = !isString(sourceFileOrPath) ? newProgram.getRedirectReferenceForResolution(sourceFileOrPath) : undefined; + const optionsForFile = redirectedReference?.commandLine.options || options; + const redirectsResult = withRedirects(options, redirectedReference); + const dirPath = getDirectoryPath(path); + let perDirResolutionCache = perDirCache?.getMapOfCacheRedirects(redirectedReference); + let cacheForDir = perDirResolutionCache?.get(dirPath); + fileCacheFromProgram.forEach((r, name, mode) => { + if (forEachResolution(r, name, mode, redirectedReference, dirPath, optionsForFile, redirectsResult)) return; + if (cacheForDir?.has(name, mode)) return; + if (!cacheForDir) { + perDirResolutionCache ??= (perDirCache ??= createCacheWithRedirects(options)).getOrCreateMap(optionsForFile, /*create*/ true); + perDirResolutionCache.set(dirPath, cacheForDir = createModeAwareCache()); + } + cacheForDir.set(name, mode, r); + if (!moduleNameToDirectoryMap || isExternalModuleNameRelative(name)) return; + + const actualModuleNameToDirectoryMap = moduleNameToDirectoryMap.getOrCreateMap(optionsForFile, /*create*/ true); + const modeAwareCacheKey = createModeAwareCacheKey(name, mode); + let directoryPathMap = actualModuleNameToDirectoryMap.get(modeAwareCacheKey); + if (!directoryPathMap) actualModuleNameToDirectoryMap.set(modeAwareCacheKey, directoryPathMap = new Map()); + moduleNameToDirectorySet( + directoryPathMap, + dirPath, + r as unknown as ResolvedModuleWithFailedLookupLocations, + getResolvedFileNameForModuleNameToDirectorySet, + toPath, + ancestorPath => ancestoryWorker(ancestorPath, name, mode, perDirResolutionCache), + ); + }); + return perDirCache; +} + /** @internal */ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; @@ -371,14 +428,6 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD clear, }; - function getResolvedModule(resolution: CachedResolvedModuleWithFailedLookupLocations) { - return resolution.resolvedModule; - } - - function getResolvedTypeReferenceDirective(resolution: CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations) { - return resolution.resolvedTypeReferenceDirective; - } - function isInDirectoryPath(dir: Path | undefined, file: Path) { if (dir === undefined || file.length <= dir.length) { return false; @@ -459,7 +508,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD resolvedModuleNames, newFile.resolvedModules, newFile, - getResolvedModule, + getResolvedModuleOfResolution, modules, moduleNameToDirectoryMap, ); @@ -468,7 +517,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD resolvedTypeReferenceDirectives, newFile.resolvedTypeReferenceDirectiveNames, newFile, - getResolvedTypeReferenceDirective, + getResolvedTypeReferenceDirectiveOfResolution, typeRefs, /*moduleNameToDirectoryMap*/ undefined, ); @@ -482,7 +531,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD resolvedTypeReferenceDirectives, newProgram.getAutomaticTypeDirectiveResolutions(), newProgramAutoTypeRefContainingFile, - getResolvedTypeReferenceDirective, + getResolvedTypeReferenceDirectiveOfResolution, typeRefs, /*moduleNameToDirectoryMap*/ undefined, ); @@ -558,47 +607,33 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD const path = !isString(sourceFileOrPath) ? sourceFileOrPath.path : sourceFileOrPath; let resolutionsInFile = perFileCache.get(path); const seenNamesInFile = createModeAwareCache(); - if (fileCacheFromProgram?.size()) { - // If keys dont match release existing resolutions - const options = newProgram.getCompilerOptions(); - const redirectedReference = !isString(sourceFileOrPath) ? newProgram.getRedirectReferenceForResolution(sourceFileOrPath) : undefined; - const optionsForFile = redirectedReference?.commandLine.options || options; - const key = resolutionsInFile && getKeyIfOptionsChange(resolutionsInFile, options, redirectedReference); - if (resolutionsInFile && key) { - resolutionsInFile.cache.forEach(r => stopWatchingResolution(r, path, getResolutionWithResolvedFileName)); - resolutionsInFile = undefined; - } - const dirPath = getDirectoryPath(path); - let perDirResolutionCache = perDirCache?.getMapOfCacheRedirects(redirectedReference); - let cacheForDir = perDirResolutionCache?.get(dirPath); - fileCacheFromProgram.forEach((r, name, mode) => { + perDirCache = toPerDirectoryResolution( + newProgram, + fileCacheFromProgram, + sourceFileOrPath, + perDirCache, + moduleNameToDirectoryMap, + (options, redirectedReference) => { + // If keys dont match release existing resolutions + const key = resolutionsInFile && getKeyIfOptionsChange(resolutionsInFile, options, redirectedReference); + if (resolutionsInFile && key) { + resolutionsInFile.cache.forEach(r => stopWatchingResolution(r, path, getResolutionWithResolvedFileName)); + resolutionsInFile = undefined; + } + return key; + }, + (r, name, mode, _redirectedReference, _dirPath, optionsForFile, key) => { seenNamesInFile.set(name, mode, true); const existing = resolutionsInFile?.cache.get(name, mode); if (existing && r !== existing) stopWatchingResolution(existing, path, getResolutionWithResolvedFileName); watchResolution(newProgram, name, r, path, getResolutionWithResolvedFileName); if (!resolutionsInFile) perFileCache.set(path, resolutionsInFile = { cache: createModeAwareCache(), options: optionsForFile, key }); resolutionsInFile.cache.set(name, mode, r); - if (cacheForDir?.has(name, mode)) return; - if (!cacheForDir) { - perDirResolutionCache ??= (perDirCache ??= createCacheWithRedirects(options)).getOrCreateMap(optionsForFile, /*create*/ true); - perDirResolutionCache.set(dirPath, cacheForDir = createModeAwareCache()); - } - cacheForDir.set(name, mode, r); - if (!moduleNameToDirectoryMap || isExternalModuleNameRelative(name)) return; - const actualModuleNameToDirectoryMap = moduleNameToDirectoryMap.getOrCreateMap(optionsForFile, /*create*/ true); - const modeAwareCacheKey = createModeAwareCacheKey(name, mode); - let directoryPathMap = actualModuleNameToDirectoryMap.get(modeAwareCacheKey); - if (!directoryPathMap) actualModuleNameToDirectoryMap.set(modeAwareCacheKey, directoryPathMap = new Map()); - moduleNameToDirectorySet( - directoryPathMap, - dirPath, - r as unknown as ResolvedModuleWithFailedLookupLocations, - getResolvedFileNameForModuleNameToDirectorySet, - dir => resolutionHost.toPath(dir), - noop, - ); - }); - } + }, + dir => resolutionHost.toPath(dir), + noop, + ); + if ((resolutionsInFile?.cache.size() || 0) !== seenNamesInFile.size()) { // Stop watching and remove the unused name resolutionsInFile!.cache.forEach((resolution, name, mode) => { @@ -842,7 +877,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD resolutionHost.getCompilerHost?.() || resolutionHost, typeReferenceDirectiveResolutionCache ), - getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective, + getResolutionWithResolvedFileName: getResolvedTypeReferenceDirectiveOfResolution, }); } @@ -867,7 +902,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD redirectedReference, options, ), - getResolutionWithResolvedFileName: getResolvedModule, + getResolutionWithResolvedFileName: getResolvedModuleOfResolution, logChanges: logChangesWhenResolvingModule, }); } @@ -1195,8 +1230,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD } function removeResolutionsOfFile(filePath: Path) { - removeResolutionsOfFileFromCache(resolvedModuleNames, filePath, getResolvedModule); - removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, getResolvedTypeReferenceDirective); + removeResolutionsOfFileFromCache(resolvedModuleNames, filePath, getResolvedModuleOfResolution); + removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, getResolvedTypeReferenceDirectiveOfResolution); } function invalidateResolutions(resolutions: ResolutionWithFailedLookupLocations[] | undefined, canInvalidate: (resolution: ResolutionWithFailedLookupLocations) => boolean | undefined) {