Skip to content

Commit ce2e752

Browse files
committed
Resolutions cache stays for lifetime..
1 parent 04b994f commit ce2e752

File tree

35 files changed

+533
-369
lines changed

35 files changed

+533
-369
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 106 additions & 31 deletions
Large diffs are not rendered by default.

src/compiler/program.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ import {
275275
removeSuffix,
276276
resolutionExtensionIsTSOrJson,
277277
ResolutionMode,
278-
ResolutionWithFailedLookupLocations,
279278
resolveConfigFileProjectName,
280279
ResolvedConfigFileName,
281280
ResolvedModuleFull,
@@ -1968,6 +1967,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
19681967
getResolvedProjectReferenceByPath,
19691968
forEachResolvedProjectReference,
19701969
isSourceOfProjectReferenceRedirect,
1970+
getRedirectReferenceForResolution,
19711971
getRedirectReferenceForResolutionFromSourceOfProject,
19721972
emitBuildInfo,
19731973
fileExists,
@@ -4156,7 +4156,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
41564156
resolveModuleNamesReusingOldState(moduleNames, file);
41574157
Debug.assert(resolutions.length === moduleNames.length);
41584158
const optionsForFile = redirectedReference?.commandLine.options || options;
4159-
const resolutionsInFile = createModeAwareCache<ResolutionWithFailedLookupLocations>();
4159+
const resolutionsInFile = createModeAwareCache<ResolvedModuleWithFailedLookupLocations>();
41604160
(resolvedModules ??= new Map()).set(file.path, resolutionsInFile);
41614161
for (let index = 0; index < moduleNames.length; index++) {
41624162
const resolution = resolutions[index].resolvedModule;

src/compiler/resolutionCache.ts

Lines changed: 165 additions & 135 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4817,6 +4817,7 @@ export interface Program extends ScriptReferenceHost {
48174817
getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
48184818
/** @internal */ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
48194819
/** @internal */ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
4820+
/** @internal */ getRedirectReferenceForResolution(file: SourceFile): ResolvedProjectReference | undefined;
48204821
/** @internal */ getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path): ResolvedProjectReference | undefined;
48214822
/** @internal */ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
48224823
/** @internal */ getBuildInfo?(): BuildInfo;

src/compiler/watchPublic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
538538
host.resolveLibrary.bind(host);
539539
compilerHost.getModuleResolutionCache = host.resolveModuleNameLiterals || host.resolveModuleNames ?
540540
maybeBind(host, host.getModuleResolutionCache) :
541-
(() => resolutionCache.getModuleResolutionCache());
541+
(() => resolutionCache.moduleResolutionCache);
542542
const userProvidedResolution = !!host.resolveModuleNameLiterals || !!host.resolveTypeReferenceDirectiveReferences ||
543543
!!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
544544
// All resolutions are invalid if user provided resolutions and didnt supply hasInvalidatedResolutions

src/harness/incrementalUtils.ts

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export function verifyResolutionCache(
210210
const expected = ts.createResolutionCache(resolutionHostCacheHost, actual.rootDirForResolution);
211211
expected.startCachingPerDirectoryResolution();
212212

213-
type ExpectedResolution = ts.CachedResolvedModuleWithFailedLookupLocations & ts.CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations;
213+
type ExpectedResolution = ts.ResolvedModuleWithFailedLookupLocations & ts.ResolvedTypeReferenceDirectiveWithFailedLookupLocations;
214214

215215
const expectedToResolution = new Map<ExpectedResolution, ts.ResolutionWithFailedLookupLocations>();
216216
const resolutionToExpected = new Map<ts.ResolutionWithFailedLookupLocations, ExpectedResolution>();
@@ -228,6 +228,8 @@ export function verifyResolutionCache(
228228
resolutions,
229229
getResolvedModuleFileName,
230230
expected.resolvedModuleNames,
231+
expected.moduleResolutionCache,
232+
() => actualProgram.getRedirectReferenceForResolution(actualProgram.getSourceFileByPath(path)!),
231233
(name, mode) => actualProgram.getResolvedModule(actualProgram.getSourceFileByPath(path)!, name, mode),
232234
)
233235
);
@@ -238,24 +240,36 @@ export function verifyResolutionCache(
238240
resolutions,
239241
getResolvedTypeRefFileName,
240242
expected.resolvedTypeReferenceDirectives,
243+
expected.typeReferenceDirectiveResolutionCache,
244+
() =>
245+
path !== inferredTypesPath ?
246+
actualProgram.getRedirectReferenceForResolution(actualProgram.getSourceFileByPath(path)!) :
247+
undefined,
241248
(name, mode) =>
242249
path !== inferredTypesPath ?
243250
actualProgram.getResolvedTypeReferenceDirective(actualProgram.getSourceFileByPath(path)!, name, mode) :
244251
actualProgram.getAutomaticTypeDirectiveResolutions().get(name, mode),
245252
)
246253
);
247254
actual.resolvedLibraries.forEach((resolved, libFileName) => {
255+
const libResolvedFrom = ts.getInferredLibraryNameResolveFrom(actualProgram.getCompilerOptions(), currentDirectory, libFileName);
248256
const expectedResolution = collectResolution(
249257
"Libs",
250-
resolutionHostCacheHost.toPath(
251-
ts.getInferredLibraryNameResolveFrom(actualProgram.getCompilerOptions(), currentDirectory, libFileName),
252-
),
258+
resolutionHostCacheHost.toPath(libResolvedFrom),
253259
resolved,
254260
getResolvedModuleFileName(resolved),
255261
ts.getLibraryNameFromLibFileName(libFileName),
256262
/*mode*/ undefined,
257263
);
258264
expected.resolvedLibraries.set(libFileName, expectedResolution);
265+
ts.setPerDirectoryAndNonRelativeNameCacheResult(
266+
expected.libraryResolutionCache,
267+
libFileName,
268+
undefined,
269+
ts.getDirectoryPath(libResolvedFrom),
270+
undefined,
271+
expectedResolution,
272+
);
259273
});
260274
// Check for resolutions in program but not in cache to empty resolutions
261275
if (!userResolvedModuleNames) {
@@ -337,6 +351,11 @@ export function verifyResolutionCache(
337351
`${projectName}:: Expected ResolutionsResolvedWithoutGlobalCache count ${expected.countResolutionsResolvedWithoutGlobalCache()} but got ${actual.countResolutionsResolvedWithoutGlobalCache()}`,
338352
);
339353

354+
// Verify that caches are same:
355+
verifyModuleOrTypeResolutionCache(expected.moduleResolutionCache, actual.moduleResolutionCache, "moduleResolutionCache");
356+
verifyModuleOrTypeResolutionCache(expected.typeReferenceDirectiveResolutionCache, actual.typeReferenceDirectiveResolutionCache, "typeReferenceDirectiveResolutionCache");
357+
verifyModuleOrTypeResolutionCache(expected.libraryResolutionCache, actual.libraryResolutionCache, "libraryResolutionCache");
358+
340359
// Stop watching resolutions to verify everything gets closed.
341360
expected.startCachingPerDirectoryResolution();
342361
actual.resolvedModuleNames.forEach((_resolutions, path) => expected.removeResolutionsOfFile(path));
@@ -353,6 +372,9 @@ export function verifyResolutionCache(
353372
ts.Debug.assert(expected.fileWatchesOfAffectingLocations.size === 0, `${projectName}:: fileWatchesOfAffectingLocations should be released`);
354373
ts.Debug.assert(expected.countResolutionsResolvedWithGlobalCache() === 0, `${projectName}:: ResolutionsResolvedWithGlobalCache should be cleared`);
355374
ts.Debug.assert(expected.countResolutionsResolvedWithoutGlobalCache() === 0, `${projectName}:: ResolutionsResolvedWithoutGlobalCache should be cleared`);
375+
verifyModuleOrTypeResolutionCacheIsEmpty(expected.moduleResolutionCache, "moduleResolutionCache");
376+
verifyModuleOrTypeResolutionCacheIsEmpty(expected.typeReferenceDirectiveResolutionCache, "typeReferenceDirectiveResolutionCache");
377+
verifyModuleOrTypeResolutionCacheIsEmpty(expected.libraryResolutionCache, "libraryResolutionCache");
356378

357379
function verifyResolutionIsInCache<T extends ts.ResolutionWithFailedLookupLocations>(
358380
cacheType: string,
@@ -384,6 +406,8 @@ export function verifyResolutionCache(
384406
cache: ts.ModeAwareCache<T> | undefined,
385407
getResolvedFileName: (resolution: T) => string | undefined,
386408
storeExpected: Map<ts.Path, ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations>>,
409+
moduleOrTypeRefCache: ts.ModuleOrTypeReferenceResolutionCache<T>,
410+
getRedirectReferenceForResolution: () => ts.ResolvedProjectReference | undefined,
387411
getProgramResolutions: (name: string, mode: ts.ResolutionMode) => T | undefined,
388412
) {
389413
ts.Debug.assert(
@@ -396,6 +420,14 @@ export function verifyResolutionCache(
396420
const expected = collectResolution(cacheType, fileName, resolved, resolvedFileName, name, mode);
397421
if (!expectedCache) storeExpected.set(fileName, expectedCache = ts.createModeAwareCache());
398422
expectedCache.set(name, mode, expected);
423+
ts.setPerDirectoryAndNonRelativeNameCacheResult(
424+
moduleOrTypeRefCache,
425+
name,
426+
mode,
427+
ts.getDirectoryPath(fileName),
428+
getRedirectReferenceForResolution(),
429+
expected as unknown as T,
430+
);
399431
// Resolution in cache should be same as that is in program
400432
ts.Debug.assert(
401433
resolved === getProgramResolutions(name, mode),
@@ -526,6 +558,90 @@ export function verifyResolutionCache(
526558
ts.Debug.assert(expected === actual, `${projectName}:: ${caption}`);
527559
}, "dirPathToSymlinkPackageRefCount");
528560
}
561+
562+
function verifyModuleOrTypeResolutionCache(
563+
expected: ts.ModuleOrTypeReferenceResolutionCache<ts.ResolutionWithFailedLookupLocations>,
564+
actual: ts.ModuleOrTypeReferenceResolutionCache<ts.ResolutionWithFailedLookupLocations>,
565+
cacheType: string,
566+
) {
567+
verfiyCacheWithRedirects(
568+
expected.directoryToModuleNameMap,
569+
actual.directoryToModuleNameMap,
570+
verifyDirectoryToModuleNameMap,
571+
`${cacheType}:: directoryToModuleNameMap`,
572+
);
573+
verfiyCacheWithRedirects(
574+
expected.moduleNameToDirectoryMap,
575+
actual.moduleNameToDirectoryMap,
576+
verifyModuleNameToDirectoryMap,
577+
`${cacheType}:: moduleNameToDirectoryMap`,
578+
);
579+
}
580+
581+
function verifyDirectoryToModuleNameMap(
582+
expected: ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations> | undefined,
583+
actual: ts.ModeAwareCache<ts.ResolutionWithFailedLookupLocations> | undefined,
584+
caption: string,
585+
) {
586+
verifyModeAwareCache(
587+
expected,
588+
actual,
589+
verfiyResolution,
590+
caption,
591+
);
592+
}
593+
594+
function verifyModuleNameToDirectoryMap(
595+
expected: ts.PerNonRelativeNameCache<ts.ResolutionWithFailedLookupLocations> | undefined,
596+
actual: ts.PerNonRelativeNameCache<ts.ResolutionWithFailedLookupLocations> | undefined,
597+
caption: string,
598+
) {
599+
verifyMap(
600+
expected?.directoryPathMap,
601+
actual?.directoryPathMap,
602+
verfiyResolution,
603+
caption,
604+
);
605+
}
606+
607+
function verfiyResolution(
608+
expected: ts.ResolutionWithFailedLookupLocations | undefined,
609+
actual: ts.ResolutionWithFailedLookupLocations | undefined,
610+
caption: string,
611+
) {
612+
ts.Debug.assert(
613+
expectedToResolution.get(expected as ExpectedResolution) === actual,
614+
`${projectName}:: ${caption} Expected resolution need to match in actual`,
615+
);
616+
}
617+
618+
function verifyModuleOrTypeResolutionCacheIsEmpty(
619+
cache: ts.ModuleOrTypeReferenceResolutionCache<ts.ResolutionWithFailedLookupLocations>,
620+
cacheType: string,
621+
) {
622+
verifyCacheWithRedirectsIsEmpty(
623+
cache.directoryToModuleNameMap,
624+
`${cacheType}:: directoryToModuleNameMap`,
625+
);
626+
verifyCacheWithRedirectsIsEmpty(
627+
cache.moduleNameToDirectoryMap,
628+
`${cacheType}:: moduleNameToDirectoryMap`,
629+
);
630+
}
631+
632+
function verifyCacheWithRedirectsIsEmpty<K, V>(
633+
cache: ts.CacheWithRedirects<K, V>,
634+
cacheType: string,
635+
) {
636+
ts.Debug.assert(
637+
cache.getOwnMap().size === 0,
638+
`${projectName}:: ${cacheType}:: ownMap should be empty`,
639+
);
640+
ts.Debug.assert(
641+
cache.redirectsKeyToMap.size === 0,
642+
`${projectName}:: ${cacheType}:: redirectsKeyToMap should be empty`,
643+
);
644+
}
529645
}
530646

531647
function verifyMap<Key extends string, Expected, Actual>(
@@ -565,6 +681,31 @@ function verifyArray(
565681
return verifySet(expected && new Set(expected), actual && new Set(actual), caption);
566682
}
567683

684+
function verifyModeAwareCache<T>(
685+
expected: ts.ModeAwareCache<T> | undefined,
686+
actual: ts.ModeAwareCache<T> | undefined,
687+
verifyValue: (expected: T | undefined, actual: T | undefined, caption: string) => void,
688+
caption: string,
689+
) {
690+
expected?.forEach((expected, key, mode) => verifyValue(expected, actual?.get(key, mode), `${caption}:: ${key}:: ${mode}`));
691+
actual?.forEach((actual, key, mode) => verifyValue(expected?.get(key, mode), actual, `${caption}:: ${key}:: ${mode}`));
692+
}
693+
694+
function verfiyCacheWithRedirects<K extends string, V>(
695+
expected: ts.CacheWithRedirects<K, V>,
696+
actual: ts.CacheWithRedirects<K, V>,
697+
verifyValue: (expected: V | undefined, actual: V | undefined, caption: string) => void,
698+
cacheType: string,
699+
) {
700+
verifyMap(expected.getOwnMap(), actual.getOwnMap(), verifyValue, `${cacheType}:: ownMap`);
701+
verifyMap(
702+
expected.redirectsKeyToMap,
703+
actual.redirectsKeyToMap,
704+
(expected, actual, key) => verifyMap(expected, actual, verifyValue, key),
705+
`${cacheType}:: redirectsKeyToMap`,
706+
);
707+
}
708+
568709
function verifyProgram(service: ts.server.ProjectService, project: ts.server.Project) {
569710
if (service.serverMode === ts.LanguageServiceMode.Syntactic) return;
570711
const options = project.getCompilerOptions();

src/server/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
836836

837837
/** @internal */
838838
getModuleResolutionCache(): ModuleResolutionCache | undefined {
839-
return this.resolutionCache.getModuleResolutionCache();
839+
return this.resolutionCache.moduleResolutionCache;
840840
}
841841

842842
/** @internal */

tests/baselines/reference/tscWatch/moduleResolution/late-discovered-dependency-symlink.js

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,7 @@ Output::
203203
204204
Reusing resolution of module 'package-b' from '/workspace/packageC/index.ts' of old program, it was successfully resolved to '/workspace/packageB/index.d.ts'.
205205
======== Resolving module 'package-b' from '/workspace/packageC/package.json'. ========
206-
Module resolution kind is not specified, using 'Node10'.
207-
Loading module 'package-b' from 'node_modules' folder, target file types: TypeScript, Declaration.
208-
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
209-
File '/workspace/packageC/node_modules/package-b/package.json' exists according to earlier cached lookups.
210-
File '/workspace/packageC/node_modules/package-b.ts' does not exist.
211-
File '/workspace/packageC/node_modules/package-b.tsx' does not exist.
212-
File '/workspace/packageC/node_modules/package-b.d.ts' does not exist.
213-
'package.json' does not have a 'typings' field.
214-
'package.json' does not have a 'types' field.
215-
'package.json' does not have a 'main' field.
216-
File '/workspace/packageC/node_modules/package-b/index.ts' does not exist.
217-
File '/workspace/packageC/node_modules/package-b/index.tsx' does not exist.
218-
File '/workspace/packageC/node_modules/package-b/index.d.ts' exists - use it as a name resolution result.
219-
Resolving real path for '/workspace/packageC/node_modules/package-b/index.d.ts', result '/workspace/packageB/index.d.ts'.
206+
Resolution for module 'package-b' was found in cache from location '/workspace/packageC'.
220207
======== Module name 'package-b' was successfully resolved to '/workspace/packageB/index.d.ts'. ========
221208
======== Resolving module 'package-a' from '/workspace/packageC/package.json'. ========
222209
Module resolution kind is not specified, using 'Node10'.

tests/baselines/reference/tscWatch/moduleResolution/module-resolutions-from-file-are-partially-used.js

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -277,26 +277,7 @@ File '/user/username/package.json' does not exist according to earlier cached lo
277277
File '/user/package.json' does not exist according to earlier cached lookups.
278278
File '/package.json' does not exist according to earlier cached lookups.
279279
======== Resolving module 'pkg' from '/user/username/projects/myproject/a.ts'. ========
280-
Explicitly specified module resolution kind: 'Node16'.
281-
Resolving in ESM mode with conditions 'import', 'types', 'node'.
282-
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.
283-
File '/user/username/projects/package.json' does not exist according to earlier cached lookups.
284-
File '/user/username/package.json' does not exist according to earlier cached lookups.
285-
File '/user/package.json' does not exist according to earlier cached lookups.
286-
File '/package.json' does not exist according to earlier cached lookups.
287-
Loading module 'pkg' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration.
288-
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
289-
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
290-
Entering conditional exports.
291-
Matched 'exports' condition 'import'.
292-
Using 'exports' subpath '.' with target './import.js'.
293-
File name '/user/username/projects/myproject/node_modules/pkg/import.js' has a '.js' extension - stripping it.
294-
File '/user/username/projects/myproject/node_modules/pkg/import.ts' does not exist.
295-
File '/user/username/projects/myproject/node_modules/pkg/import.tsx' does not exist.
296-
File '/user/username/projects/myproject/node_modules/pkg/import.d.ts' exists - use it as a name resolution result.
297-
Resolved under condition 'import'.
298-
Exiting conditional exports.
299-
Resolving real path for '/user/username/projects/myproject/node_modules/pkg/import.d.ts', result '/user/username/projects/myproject/node_modules/pkg/import.d.ts'.
280+
Resolution for module 'pkg' was found in cache from location '/user/username/projects/myproject'.
300281
======== Module name 'pkg' was successfully resolved to '/user/username/projects/myproject/node_modules/pkg/import.d.ts' with Package ID 'pkg/import.d.ts@0.0.1'. ========
301282
File '/user/username/projects/myproject/node_modules/pkg/package.json' exists according to earlier cached lookups.
302283
File '/user/username/projects/myproject/package.json' does not exist according to earlier cached lookups.

0 commit comments

Comments
 (0)