Skip to content

Commit ea2352a

Browse files
committed
Handle effective type roots and type ref resolution sharing cache
// TODO: update incremental tests to actually verify the cache
1 parent 69c37d3 commit ea2352a

31 files changed

+754
-1702
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 257 additions & 59 deletions
Large diffs are not rendered by default.

src/compiler/program.ts

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import {
4949
createSymlinkCache,
5050
createTypeChecker,
5151
createTypeReferenceDirectiveResolutionCache,
52+
createTypeRootsCacheKey,
5253
CustomTransformers,
5354
Debug,
5455
DeclarationWithTypeParameterChildren,
@@ -114,6 +115,7 @@ import {
114115
getDeclarationDiagnostics as ts_getDeclarationDiagnostics,
115116
getDefaultLibFileName,
116117
getDirectoryPath,
118+
getEffectiveTypeRoots as ts_getEffectiveTypeRoots,
117119
getEmitDeclarations,
118120
getEmitModuleKind,
119121
getEmitModuleResolutionKind,
@@ -311,6 +313,7 @@ import {
311313
TypeChecker,
312314
typeDirectiveIsEqualTo,
313315
TypeReferenceDirectiveResolutionCache,
316+
TypeRootsCacheKeyOrSpecifiedTypeRoots,
314317
unprefixedNodeCoreModules,
315318
VariableDeclaration,
316319
VariableStatement,
@@ -1725,6 +1728,9 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
17251728
let projectReferenceRedirects: Map<Path, ResolvedProjectReference | false> | undefined;
17261729
let mapFromFileToProjectReferenceRedirects: Map<Path, Path> | undefined;
17271730
let mapFromToProjectReferenceRedirectSource: Map<Path, SourceOfProjectReferenceRedirect> | undefined;
1731+
let typeRootsCacheKeys: Map<Path | undefined, TypeRootsCacheKeyOrSpecifiedTypeRoots> | undefined;
1732+
let processingTypeRootsCacheKeys: Map<Path | undefined, TypeRootsCacheKeyOrSpecifiedTypeRoots> | undefined;
1733+
let effectiveRoots: Map<Path | undefined, readonly string[] | false> | undefined;
17281734

17291735
const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
17301736
!options.disableSourceOfProjectReferenceRedirect;
@@ -1738,6 +1744,9 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
17381744
forEachResolvedProjectReference,
17391745
});
17401746
const readFile = host.readFile.bind(host) as typeof host.readFile;
1747+
const hostGetEffectiveTypeRoots = host.getEffectiveTypeRoots;
1748+
const hostGetTypeRootsCacheKey = host.getTypeRootsCacheKey;
1749+
host.getEffectiveTypeRoots = getEffectiveTypeRoots;
17411750

17421751
tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
17431752
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
@@ -1796,6 +1805,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
17961805
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
17971806
automaticTypeDirectiveNames,
17981807
getAutomaticTypeDirectiveContainingFile(options, currentDirectory),
1808+
getTypeRootsCacheKey,
17991809
);
18001810
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
18011811
// under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
@@ -1884,6 +1894,10 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
18841894
resolvedLibProcessing = undefined;
18851895
resolvedModulesProcessing = undefined;
18861896
resolvedTypeReferenceDirectiveNamesProcessing = undefined;
1897+
effectiveRoots = undefined;
1898+
processingTypeRootsCacheKeys = undefined;
1899+
host.getEffectiveTypeRoots = hostGetEffectiveTypeRoots;
1900+
host.getTypeRootsCacheKey = hostGetTypeRootsCacheKey;
18871901

18881902
const program: Program = {
18891903
getRootFileNames: () => rootNames,
@@ -1968,6 +1982,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
19681982
structureIsReused,
19691983
writeFile,
19701984
getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation),
1985+
getTypeRootsCacheKeys: () => typeRootsCacheKeys,
19711986
};
19721987

19731988
onProgramCreateComplete();
@@ -2117,6 +2132,51 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
21172132
return result;
21182133
}
21192134

2135+
function getEffectiveTypeRoots(options: CompilerOptions, redirect: ResolvedProjectReference | undefined) {
2136+
let result = effectiveRoots?.get(redirect?.sourceFile.path);
2137+
if (result === undefined) {
2138+
(effectiveRoots ??= new Map()).set(
2139+
redirect?.sourceFile.path,
2140+
result = (
2141+
hostGetEffectiveTypeRoots ?
2142+
hostGetEffectiveTypeRoots(options, redirect) :
2143+
ts_getEffectiveTypeRoots(options, host)
2144+
) ?? false,
2145+
);
2146+
}
2147+
return result || undefined;
2148+
}
2149+
2150+
function getTypeRootsCacheKey(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2151+
let result = typeRootsCacheKeys?.get(redirect?.sourceFile.path);
2152+
if (result === undefined) {
2153+
(typeRootsCacheKeys ??= new Map()).set(
2154+
redirect?.sourceFile.path,
2155+
result = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path) ??
2156+
getTypeRootsCacheKeyWorker(options, redirect) ??
2157+
false,
2158+
);
2159+
}
2160+
return result;
2161+
}
2162+
2163+
function getProcessingTypeRootsCacheKey(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2164+
let result = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path);
2165+
if (result === undefined) {
2166+
(processingTypeRootsCacheKeys ??= new Map()).set(
2167+
redirect?.sourceFile.path,
2168+
result = getTypeRootsCacheKeyWorker(options, redirect) ?? false,
2169+
);
2170+
}
2171+
return result;
2172+
}
2173+
2174+
function getTypeRootsCacheKeyWorker(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2175+
return hostGetTypeRootsCacheKey ?
2176+
hostGetTypeRootsCacheKey(options, redirect) :
2177+
createTypeRootsCacheKey(options, redirect, host);
2178+
}
2179+
21202180
function getRedirectReferenceForResolution(file: SourceFile) {
21212181
const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
21222182
if (redirect || !isDeclarationFileName(file.originalFileName)) return redirect;
@@ -2215,9 +2275,21 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
22152275
});
22162276
}
22172277

2218-
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly FileReference[], containingFile: SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2219-
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly string[], containingFile: string): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2220-
function resolveTypeReferenceDirectiveNamesReusingOldState<T extends string | FileReference>(typeDirectiveNames: readonly T[], containingFile: string | SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
2278+
function resolveTypeReferenceDirectiveNamesReusingOldState(
2279+
typeDirectiveNames: readonly FileReference[],
2280+
containingFile: SourceFile,
2281+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2282+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2283+
function resolveTypeReferenceDirectiveNamesReusingOldState(
2284+
typeDirectiveNames: readonly string[],
2285+
containingFile: string,
2286+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2287+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2288+
function resolveTypeReferenceDirectiveNamesReusingOldState<T extends string | FileReference>(
2289+
typeDirectiveNames: readonly T[],
2290+
containingFile: string | SourceFile,
2291+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2292+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
22212293
const containingSourceFile = !isString(containingFile) ? containingFile : undefined;
22222294
return resolveNamesReusingOldState({
22232295
entries: typeDirectiveNames,
@@ -2236,6 +2308,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
22362308
containingSourceFile ?
22372309
containingSourceFile === oldProgram?.getSourceFile(containingSourceFile.fileName) && !hasInvalidatedResolutions(containingSourceFile.path) :
22382310
!hasInvalidatedResolutions(toPath(containingFile as string)),
2311+
getTypeRootsCacheKey,
22392312
});
22402313
}
22412314

@@ -2262,6 +2335,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
22622335
getResolved: (oldResolution: Resolution) => ResolutionWithResolvedFileName | undefined;
22632336
canReuseResolutionsInFile: () => boolean;
22642337
resolveToOwnAmbientModule?: true;
2338+
getTypeRootsCacheKey?: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots;
22652339
}
22662340

22672341
function resolveNamesReusingOldState<Entry, SourceFileOrString, SourceFileOrUndefined extends SourceFile | undefined, Resolution>({
@@ -2276,6 +2350,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
22762350
getResolved,
22772351
canReuseResolutionsInFile,
22782352
resolveToOwnAmbientModule,
2353+
getTypeRootsCacheKey,
22792354
}: ResolveNamesReusingOldStateInput<Entry, SourceFileOrString, SourceFileOrUndefined, Resolution>): readonly Resolution[] {
22802355
if (!entries.length) {
22812356
onReusedResolutions?.(
@@ -2286,7 +2361,10 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
22862361
);
22872362
return emptyArray;
22882363
}
2364+
// Ensure typeRootsCacheKey is cached
2365+
getTypeRootsCacheKey?.(redirectedReference?.commandLine.options ?? options, redirectedReference);
22892366
if (structureIsReused === StructureIsReused.Not && (!resolveToOwnAmbientModule || !containingSourceFile!.ambientModuleNames.length)) {
2367+
host.getTypeRootsCacheKey = getTypeRootsCacheKey;
22902368
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
22912369
// the best we can do is fallback to the default logic.
22922370
return resolutionWorker(
@@ -2363,6 +2441,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
23632441
);
23642442
return result!;
23652443
}
2444+
host.getTypeRootsCacheKey = getTypeRootsCacheKey;
23662445
const resolutions = resolutionWorker(
23672446
unknownEntries,
23682447
containingFile,
@@ -2572,7 +2651,11 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
25722651
);
25732652
if (resolutionsChanged) structureIsReused = StructureIsReused.SafeModules;
25742653
const typesReferenceDirectives = newSourceFile.typeReferenceDirectives;
2575-
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typesReferenceDirectives, newSourceFile);
2654+
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
2655+
typesReferenceDirectives,
2656+
newSourceFile,
2657+
getProcessingTypeRootsCacheKey,
2658+
);
25762659
(resolvedTypeReferenceDirectiveNamesProcessing ??= new Map()).set(newSourceFile.path, typeReferenceResolutions);
25772660
// ensure that types resolutions are still correct
25782661
const typeReferenceResolutionsChanged = hasChangesInResolutions(
@@ -2650,6 +2733,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
26502733
resolvedTypeReferenceDirectiveNames = oldProgram.resolvedTypeReferenceDirectiveNames;
26512734
resolvedLibReferences = oldProgram.resolvedLibReferences;
26522735
packageMap = oldProgram.getCurrentPackagesMap();
2736+
typeRootsCacheKeys = oldProgram.getTypeRootsCacheKeys();
26532737

26542738
return StructureIsReused.Completely;
26552739
}
@@ -3929,7 +4013,13 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
39294013
}
39304014

39314015
const resolutions = resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path) ||
3932-
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file);
4016+
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file, getTypeRootsCacheKey);
4017+
if (resolutions.length && resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path)) {
4018+
// Ensure type reference key is cached from processing to actual
4019+
const redirect = getRedirectReferenceForResolution(file);
4020+
const value = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path);
4021+
if (value !== undefined) (typeRootsCacheKeys ??= new Map()).set(redirect?.sourceFile.path, value);
4022+
}
39334023
const resolutionsInFile = createModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>();
39344024
(resolvedTypeReferenceDirectiveNames ??= new Map()).set(file.path, resolutionsInFile);
39354025
for (let index = 0; index < typeDirectives.length; index++) {

src/compiler/resolutionCache.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
trace,
7575
TypeReferenceDirectiveResolutionCache,
7676
typeReferenceResolutionNameAndModeGetter,
77+
TypeRootsCacheKeyOrSpecifiedTypeRoots,
7778
WatchDirectoryFlags,
7879
} from "./_namespaces/ts.js";
7980

@@ -360,6 +361,7 @@ function resolveModuleNameUsingGlobalCache(
360361
mode,
361362
getDirectoryPath(containingFile),
362363
redirectedReference,
364+
undefined,
363365
primary.globalCacheResolution.globalResult,
364366
primary,
365367
);
@@ -658,16 +660,25 @@ export function createResolutionCache(
658660

659661
function compactCaches(newProgram: Program | undefined) {
660662
const availableOptions = new Set<CompilerOptions>();
663+
const availableTypeCacheKeys = new Map<CompilerOptions, Set<TypeRootsCacheKeyOrSpecifiedTypeRoots>>();
661664
if (newProgram) {
662665
availableOptions.add(newProgram.getCompilerOptions());
666+
const key = newProgram.getTypeRootsCacheKeys()?.get(/*key*/ undefined);
667+
if (key !== undefined) availableTypeCacheKeys.set(newProgram.getCompilerOptions(), new Set([key]));
663668
newProgram.forEachResolvedProjectReference(ref => {
664669
availableOptions.add(ref.commandLine.options);
670+
const key = newProgram.getTypeRootsCacheKeys()?.get(ref.sourceFile.path);
671+
if (key !== undefined) {
672+
const existing = availableTypeCacheKeys.get(ref.commandLine.options);
673+
if (existing) existing.add(key);
674+
else availableTypeCacheKeys.set(ref.commandLine.options, new Set([key]));
675+
}
665676
});
666677
}
667678
moduleResolutionCache.compact(availableOptions, /*skipOptionsToRedirectsKeyCleanup*/ true);
668-
typeReferenceDirectiveResolutionCache.compact(availableOptions);
679+
typeReferenceDirectiveResolutionCache.compact(availableOptions, /*skipOptionsToRedirectsKeyCleanup*/ false, availableTypeCacheKeys);
669680
libraryResolutionCache.compact();
670-
sharedCache.compactCaches(availableOptions, cache);
681+
sharedCache.compactCaches(availableOptions, availableTypeCacheKeys, cache);
671682
}
672683

673684
function gcModuleOrTypeRefCache(

0 commit comments

Comments
 (0)