Skip to content

Commit d2f6ba3

Browse files
committed
Make sure reused resolutions from file are accounted if all resolutions are reused/are resolved to ambient module names
1 parent eebd932 commit d2f6ba3

15 files changed

+364
-160
lines changed

src/compiler/program.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,8 @@ function getTypeReferenceResolutionName<T extends FileReference | string>(entry:
10271027
return !isString(entry) ? entry.fileName : entry;
10281028
}
10291029

1030-
const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
1030+
/** @internal */
1031+
export const typeReferenceResolutionNameAndModeGetter: ResolutionNameAndModeGetter<FileReference | string, SourceFile | undefined> = {
10311032
getName: getTypeReferenceResolutionName,
10321033
getMode: (entry, file) => getModeForFileReference(entry, file?.impliedNodeFormat),
10331034
};
@@ -1625,6 +1626,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
16251626
options: CompilerOptions,
16261627
containingSourceFile: SourceFile,
16271628
reusedNames: readonly StringLiteralLike[] | undefined,
1629+
ambientModuleNames: readonly StringLiteralLike[] | undefined,
16281630
) => readonly ResolvedModuleWithFailedLookupLocations[];
16291631
const hasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse;
16301632
if (host.resolveModuleNameLiterals) {
@@ -1832,6 +1834,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18321834
}
18331835
tracing?.pop();
18341836
}
1837+
else {
1838+
host.onReusedTypeReferenceDirectiveResolutions?.(/*reusedNames*/ undefined, /*containingSourceFile*/ undefined, /*redirectedReference*/ undefined, options);
1839+
}
18351840

18361841
// Do not process the default library if:
18371842
// - The '--noLib' flag is used.
@@ -2125,6 +2130,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
21252130
moduleNames: readonly StringLiteralLike[],
21262131
containingFile: SourceFile,
21272132
reusedNames: readonly StringLiteralLike[] | undefined,
2133+
ambientModuleNames: readonly StringLiteralLike[] | undefined,
21282134
): readonly ResolvedModuleWithFailedLookupLocations[] {
21292135
const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
21302136
const redirectedReference = getRedirectReferenceForResolution(containingFile);
@@ -2137,6 +2143,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
21372143
options,
21382144
containingFile,
21392145
reusedNames,
2146+
ambientModuleNames,
21402147
);
21412148
performance.mark("afterResolveModule");
21422149
performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
@@ -2253,6 +2260,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
22532260
redirectedReference: getRedirectReferenceForResolution(containingFile),
22542261
nameAndModeGetter: moduleResolutionNameAndModeGetter,
22552262
resolutionWorker: resolveModuleNamesWorker,
2263+
onReusedResolutions: maybeBind(host, host.onReusedModuleResolutions),
22562264
getResolutionFromOldProgram: (name, mode) => oldProgram?.getResolvedModule(containingFile, name, mode),
22572265
getResolved: getResolvedModuleFromResolution,
22582266
canReuseResolutionsInFile: () =>
@@ -2315,6 +2323,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23152323
redirectedReference: containingSourceFile && getRedirectReferenceForResolution(containingSourceFile),
23162324
nameAndModeGetter: typeReferenceResolutionNameAndModeGetter,
23172325
resolutionWorker: resolveTypeReferenceDirectiveNamesWorker,
2326+
onReusedResolutions: maybeBind(host, host.onReusedTypeReferenceDirectiveResolutions),
23182327
getResolutionFromOldProgram: (name, mode) =>
23192328
containingSourceFile ?
23202329
oldProgram?.getResolvedTypeReferenceDirective(containingSourceFile, name, mode) :
@@ -2337,7 +2346,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23372346
entries: readonly Entry[],
23382347
containingFile: SourceFileOrString,
23392348
reusedNames: readonly Entry[] | undefined,
2349+
ambientEntries: readonly Entry[] | undefined,
23402350
) => readonly Resolution[];
2351+
onReusedResolutions:
2352+
| ((
2353+
resuedEntries: readonly Entry[] | undefined,
2354+
containingSourceFile: SourceFileOrUndefined,
2355+
redirectedReference: ResolvedProjectReference | undefined,
2356+
options: CompilerOptions,
2357+
ambientEntries: readonly Entry[] | undefined,
2358+
) => void)
2359+
| undefined;
23412360
getResolutionFromOldProgram: (name: string, mode: ResolutionMode) => Resolution | undefined;
23422361
getResolved: (oldResolution: Resolution) => ResolutionWithResolvedFileName | undefined;
23432362
canReuseResolutionsInFile: () => boolean;
@@ -2351,19 +2370,24 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23512370
redirectedReference,
23522371
nameAndModeGetter,
23532372
resolutionWorker,
2373+
onReusedResolutions,
23542374
getResolutionFromOldProgram,
23552375
getResolved,
23562376
canReuseResolutionsInFile,
23572377
isEntryResolvingToAmbientModule,
23582378
}: ResolveNamesReusingOldStateInput<Entry, SourceFileOrString, SourceFileOrUndefined, Resolution>): readonly Resolution[] {
2359-
if (!entries.length) return emptyArray;
2379+
if (!entries.length) {
2380+
onReusedResolutions?.(entries, containingSourceFile, redirectedReference, options, /*ambientEntries*/ undefined);
2381+
return emptyArray;
2382+
}
23602383
if (structureIsReused === StructureIsReused.Not && (!isEntryResolvingToAmbientModule || !containingSourceFile!.ambientModuleNames.length)) {
23612384
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
23622385
// the best we can do is fallback to the default logic.
23632386
return resolutionWorker(
23642387
entries,
23652388
containingFile,
23662389
/*reusedNames*/ undefined,
2390+
/*ambientEntries*/ undefined,
23672391
);
23682392
}
23692393

@@ -2372,6 +2396,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23722396
let unknownEntryIndices: number[] | undefined;
23732397
let result: Resolution[] | undefined;
23742398
let reusedNames: Entry[] | undefined;
2399+
let ambientEntries: Entry[] | undefined;
23752400
const reuseResolutions = canReuseResolutionsInFile();
23762401
for (let i = 0; i < entries.length; i++) {
23772402
const entry = entries[i];
@@ -2403,6 +2428,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24032428
}
24042429
}
24052430
if (isEntryResolvingToAmbientModule?.(entry, containingFile)) {
2431+
(ambientEntries ??= []).push(entry);
24062432
(result ??= new Array(entries.length))[i] = emptyResolution;
24072433
}
24082434
else {
@@ -2412,8 +2438,16 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24122438
}
24132439
}
24142440

2415-
if (!unknownEntries) return result!;
2416-
const resolutions = resolutionWorker(unknownEntries, containingFile, reusedNames);
2441+
if (!unknownEntries) {
2442+
onReusedResolutions?.(reusedNames, containingSourceFile, redirectedReference, options, ambientEntries);
2443+
return result!;
2444+
}
2445+
const resolutions = resolutionWorker(
2446+
unknownEntries,
2447+
containingFile,
2448+
reusedNames,
2449+
ambientEntries,
2450+
);
24172451
if (!result) return resolutions;
24182452
resolutions.forEach((resolution, index) => result[unknownEntryIndices![index]] = resolution);
24192453
return result;
@@ -3986,7 +4020,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
39864020

39874021
function processTypeReferenceDirectives(file: SourceFile) {
39884022
const typeDirectives = file.typeReferenceDirectives;
3989-
if (!typeDirectives.length) return;
4023+
if (!typeDirectives.length) {
4024+
host.onReusedTypeReferenceDirectiveResolutions?.(/*reusedNames*/ undefined, file, getRedirectReferenceForResolution(file), options);
4025+
return;
4026+
}
39904027

39914028
const resolutions = resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path) ||
39924029
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file);
@@ -4111,13 +4148,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
41114148

41124149
function processImportedModules(file: SourceFile) {
41134150
collectExternalModuleReferences(file);
4151+
const redirectedReference = getRedirectReferenceForResolution(file);
41144152
if (file.imports.length || file.moduleAugmentations.length) {
41154153
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
41164154
const moduleNames = getModuleNames(file);
41174155
const resolutions = resolvedModulesProcessing?.get(file.path) ||
41184156
resolveModuleNamesReusingOldState(moduleNames, file);
41194157
Debug.assert(resolutions.length === moduleNames.length);
4120-
const optionsForFile = getRedirectReferenceForResolution(file)?.commandLine.options || options;
4158+
const optionsForFile = redirectedReference?.commandLine.options || options;
41214159
const resolutionsInFile = createModeAwareCache<ResolutionWithFailedLookupLocations>();
41224160
(resolvedModules ??= new Map()).set(file.path, resolutionsInFile);
41234161
for (let index = 0; index < moduleNames.length; index++) {
@@ -4175,6 +4213,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
41754213
}
41764214
}
41774215
}
4216+
else {
4217+
host.onReusedModuleResolutions?.(/*reusedNames*/ undefined, file, redirectedReference, options, /*ambientModuleNames*/ undefined);
4218+
}
41784219
}
41794220

41804221
function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {

src/compiler/resolutionCache.ts

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
clearMap,
44
closeFileWatcher,
55
closeFileWatcherOf,
6+
CompilerHostSupportingResolutionCache,
67
CompilerOptions,
78
createModeAwareCache,
89
createModuleResolutionCache,
@@ -20,6 +21,7 @@ import {
2021
FileWatcher,
2122
FileWatcherCallback,
2223
firstDefinedIterator,
24+
getAutomaticTypeDirectiveContainingFile,
2325
GetCanonicalFileName,
2426
getDirectoryPath,
2527
getEffectiveTypeRoots,
@@ -61,6 +63,7 @@ import {
6163
resolutionExtensionIsTSOrJson,
6264
ResolutionLoader,
6365
ResolutionMode,
66+
ResolutionNameAndModeGetter,
6467
ResolutionWithResolvedFileName,
6568
ResolvedModuleWithFailedLookupLocations,
6669
ResolvedProjectReference,
@@ -73,6 +76,7 @@ import {
7376
startsWith,
7477
StringLiteralLike,
7578
trace,
79+
typeReferenceResolutionNameAndModeGetter,
7680
updateResolutionField,
7781
WatchDirectoryFlags,
7882
} from "./_namespaces/ts.js";
@@ -95,7 +99,7 @@ export type CallbackOnNewResolution<T extends ResolutionWithFailedLookupLocation
9599
*
96100
* @internal
97101
*/
98-
export interface ResolutionCache {
102+
export interface ResolutionCache extends Required<CompilerHostSupportingResolutionCache> {
99103
rootDirForResolution: string;
100104
resolvedModuleNames: Map<Path, ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>>;
101105
resolvedTypeReferenceDirectives: Map<Path, ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>;
@@ -125,6 +129,7 @@ export interface ResolutionCache {
125129
options: CompilerOptions,
126130
containingSourceFile: SourceFile,
127131
reusedNames: readonly StringLiteralLike[] | undefined,
132+
ambientModuleNames: readonly StringLiteralLike[] | undefined,
128133
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
129134
): readonly ResolvedModuleWithFailedLookupLocations[];
130135
resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
@@ -683,6 +688,8 @@ export function createResolutionCache(
683688
finishCachingPerDirectoryResolution,
684689
resolveModuleNameLiterals,
685690
resolveTypeReferenceDirectiveReferences,
691+
onReusedModuleResolutions,
692+
onReusedTypeReferenceDirectiveResolutions,
686693
resolveLibrary,
687694
resolveSingleModuleNameWithoutWatching,
688695
removeResolutionsFromProjectReferenceRedirects,
@@ -867,6 +874,7 @@ export function createResolutionCache(
867874
redirectedReference: ResolvedProjectReference | undefined;
868875
options: CompilerOptions;
869876
reusedNames?: readonly Entry[];
877+
ambientEntries?: readonly Entry[];
870878
perFileCache: Map<Path, ModeAwareCache<T>>;
871879
loader: ResolutionLoader<Entry, T, SourceFile>;
872880
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
@@ -881,6 +889,7 @@ export function createResolutionCache(
881889
options,
882890
perFileCache,
883891
reusedNames,
892+
ambientEntries,
884893
loader,
885894
getResolutionWithResolvedFileName,
886895
deferWatchingNonRelativeResolution,
@@ -955,13 +964,66 @@ export function createResolutionCache(
955964
seenNamesInFile.set(name, mode, true);
956965
resolvedModules.push(resolution);
957966
}
967+
onReusedResolutions({
968+
reusedNames,
969+
containingSourceFile,
970+
redirectedReference,
971+
options,
972+
ambientEntries,
973+
path,
974+
resolutionsInFile,
975+
seenNamesInFile,
976+
nameAndModeGetter: loader.nameAndMode,
977+
getResolutionWithResolvedFileName,
978+
});
979+
return resolvedModules;
980+
}
981+
982+
interface OnReusedResolutionsInput<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName> {
983+
reusedNames: readonly Entry[] | undefined;
984+
containingSourceFile: SourceFile;
985+
redirectedReference: ResolvedProjectReference | undefined;
986+
options: CompilerOptions;
987+
ambientEntries?: readonly Entry[];
988+
path: Path;
989+
resolutionsInFile: ModeAwareCache<T> | undefined;
990+
seenNamesInFile?: ModeAwareCache<true>;
991+
nameAndModeGetter: ResolutionNameAndModeGetter<Entry, SourceFile>;
992+
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
993+
}
994+
function onReusedResolutions<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
995+
reusedNames,
996+
containingSourceFile,
997+
redirectedReference,
998+
options,
999+
path,
1000+
resolutionsInFile,
1001+
seenNamesInFile,
1002+
nameAndModeGetter,
1003+
getResolutionWithResolvedFileName,
1004+
ambientEntries,
1005+
}: OnReusedResolutionsInput<Entry, SourceFile, T, R>) {
1006+
if (!resolutionsInFile) return;
1007+
if (!seenNamesInFile) seenNamesInFile = createModeAwareCache();
9581008
reusedNames?.forEach(entry =>
9591009
seenNamesInFile.set(
960-
loader.nameAndMode.getName(entry),
961-
loader.nameAndMode.getMode(entry, containingSourceFile, redirectedReference?.commandLine.options || options),
1010+
nameAndModeGetter.getName(entry),
1011+
nameAndModeGetter.getMode(entry, containingSourceFile, redirectedReference?.commandLine.options || options),
9621012
true,
9631013
)
9641014
);
1015+
// For ambient module names, if its not invalidated keep it
1016+
ambientEntries?.forEach(entry => {
1017+
const name = nameAndModeGetter.getName(entry);
1018+
const mode = nameAndModeGetter.getMode(entry, containingSourceFile, redirectedReference?.commandLine.options || options);
1019+
if (!seenNamesInFile.has(name, mode)) {
1020+
const resolution = resolutionsInFile.get(name, mode);
1021+
// Keep this resolution from old time for ambient module names
1022+
if (resolution && !resolution.isInvalidated) {
1023+
seenNamesInFile.set(name, mode, true);
1024+
}
1025+
}
1026+
});
9651027
if (resolutionsInFile.size() !== seenNamesInFile.size()) {
9661028
// Stop watching and remove the unused name
9671029
resolutionsInFile.forEach((resolution, name, mode) => {
@@ -971,7 +1033,47 @@ export function createResolutionCache(
9711033
}
9721034
});
9731035
}
974-
return resolvedModules;
1036+
}
1037+
1038+
function onReusedModuleResolutions(
1039+
reusedNames: readonly StringLiteralLike[] | undefined,
1040+
containingSourceFile: SourceFile,
1041+
redirectedReference: ResolvedProjectReference | undefined,
1042+
options: CompilerOptions,
1043+
ambientModuleNames: readonly StringLiteralLike[] | undefined,
1044+
) {
1045+
onReusedResolutions({
1046+
reusedNames,
1047+
containingSourceFile,
1048+
redirectedReference,
1049+
options,
1050+
ambientEntries: ambientModuleNames,
1051+
path: containingSourceFile.path,
1052+
resolutionsInFile: resolvedModuleNames.get(containingSourceFile.path),
1053+
nameAndModeGetter: moduleResolutionNameAndModeGetter,
1054+
getResolutionWithResolvedFileName: getResolvedModuleFromResolution,
1055+
});
1056+
}
1057+
1058+
function onReusedTypeReferenceDirectiveResolutions<T extends FileReference | string>(
1059+
reusedNames: readonly T[] | undefined,
1060+
containingSourceFile: SourceFile | undefined,
1061+
redirectedReference: ResolvedProjectReference | undefined,
1062+
options: CompilerOptions,
1063+
) {
1064+
const path = containingSourceFile ?
1065+
containingSourceFile.path :
1066+
resolutionHost.toPath(getAutomaticTypeDirectiveContainingFile(resolutionHost.getCompilationSettings(), getCurrentDirectory()));
1067+
onReusedResolutions({
1068+
reusedNames,
1069+
containingSourceFile,
1070+
redirectedReference,
1071+
options,
1072+
path,
1073+
resolutionsInFile: resolvedTypeReferenceDirectives.get(path),
1074+
nameAndModeGetter: typeReferenceResolutionNameAndModeGetter,
1075+
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirectiveFromResolution,
1076+
});
9751077
}
9761078

9771079
function resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
@@ -1009,6 +1111,7 @@ export function createResolutionCache(
10091111
options: CompilerOptions,
10101112
containingSourceFile: SourceFile,
10111113
reusedNames: readonly StringLiteralLike[] | undefined,
1114+
ambientModuleNames: readonly StringLiteralLike[] | undefined,
10121115
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
10131116
): readonly ResolvedModuleWithFailedLookupLocations[] {
10141117
return resolveNamesWithLocalCache({
@@ -1018,6 +1121,7 @@ export function createResolutionCache(
10181121
redirectedReference,
10191122
options,
10201123
reusedNames,
1124+
ambientEntries: ambientModuleNames,
10211125
perFileCache: resolvedModuleNames,
10221126
loader: createModuleResolutionLoaderUsingGlobalCache(
10231127
containingFile,

0 commit comments

Comments
 (0)