Skip to content

Commit 7b7f6a7

Browse files
authored
Code refactoring for module resolution api (#51675)
* Refactoring so CacheWithRedirects has Key and Value type parameters * ModuleResolutionCache or TypeRefDirectiveCache will look in directory before solving, so ResolutionCache doesnt need this check * Test showing module resolution is not shared because resolution cache doesnt update own options * Enable traceResolution on some of the project reference tests * Simplify CacheWithRedirects and ensure the options are set in all common scenarios so cache can be shared between redirects
1 parent 9089d53 commit 7b7f6a7

18 files changed

+1497
-345
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import {
9999
createFileDiagnostic,
100100
createGetCanonicalFileName,
101101
createGetSymbolWalker,
102+
createModeAwareCacheKey,
102103
createPrinter,
103104
createPropertyNameNodeForIdentifierOrLiteral,
104105
createScanner,
@@ -337,8 +338,8 @@ import {
337338
hasAccessorModifier,
338339
hasAmbientModifier,
339340
hasContextSensitiveParameters,
340-
hasDecorators,
341341
HasDecorators,
342+
hasDecorators,
342343
hasDynamicName,
343344
hasEffectiveModifier,
344345
hasEffectiveModifiers,
@@ -347,8 +348,8 @@ import {
347348
hasExtension,
348349
HasIllegalDecorators,
349350
HasIllegalModifiers,
350-
hasInitializer,
351351
HasInitializer,
352+
hasInitializer,
352353
hasJSDocNodes,
353354
hasJSDocParameterTags,
354355
hasJsonModuleEmitEnabled,
@@ -7406,7 +7407,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
74067407
}
74077408
const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
74087409
const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
7409-
const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
7410+
const cacheKey = createModeAwareCacheKey(contextFile.path, resolutionMode);
74107411
const links = getSymbolLinks(symbol);
74117412
let specifier = links.specifierCache && links.specifierCache.get(cacheKey);
74127413
if (!specifier) {
@@ -7435,10 +7436,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
74357436
links.specifierCache.set(cacheKey, specifier);
74367437
}
74377438
return specifier;
7438-
7439-
function getSpecifierCacheKey(path: string, mode: ResolutionMode | undefined) {
7440-
return mode === undefined ? path : `${mode}|${path}`;
7441-
}
74427439
}
74437440

74447441
function symbolToEntityNameNode(symbol: Symbol): EntityName {

src/compiler/moduleNameResolver.ts

Lines changed: 95 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
changeAnyExtension,
66
CharacterCodes,
77
combinePaths,
8+
CommandLineOption,
89
comparePaths,
910
Comparison,
1011
CompilerOptions,
@@ -35,6 +36,7 @@ import {
3536
getBaseFileName,
3637
GetCanonicalFileName,
3738
getCommonSourceDirectory,
39+
getCompilerOptionValue,
3840
getDirectoryPath,
3941
GetEffectiveTypeRootsHost,
4042
getEmitModuleKind,
@@ -66,14 +68,13 @@ import {
6668
ModuleKind,
6769
ModuleResolutionHost,
6870
ModuleResolutionKind,
71+
moduleResolutionOptionDeclarations,
6972
noop,
7073
noopPush,
7174
normalizePath,
7275
normalizeSlashes,
73-
optionsHaveModuleResolutionChanges,
7476
PackageId,
7577
packageIdToString,
76-
ParsedCommandLine,
7778
Path,
7879
pathIsRelative,
7980
Pattern,
@@ -718,58 +719,97 @@ export interface PerModuleNameCache {
718719
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
719720
}
720721

722+
function compilerOptionValueToString(value: unknown): string {
723+
if (value === null || typeof value !== "object") { // eslint-disable-line no-null/no-null
724+
return "" + value;
725+
}
726+
if (isArray(value)) {
727+
return `[${value.map(e => compilerOptionValueToString(e))?.join(",")}]`;
728+
}
729+
let str = "{";
730+
for (const key in value) {
731+
if (hasProperty(value, key)) {
732+
str += `${key}: ${compilerOptionValueToString((value as any)[key])}`;
733+
}
734+
}
735+
return str + "}";
736+
}
737+
721738
/** @internal */
722-
export interface CacheWithRedirects<T> {
723-
getOwnMap: () => Map<string, T>;
724-
redirectsMap: Map<Path, Map<string, T>>;
725-
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<string, T>;
739+
export function getKeyForCompilerOptions(options: CompilerOptions, affectingOptionDeclarations: readonly CommandLineOption[]) {
740+
return affectingOptionDeclarations.map(option => compilerOptionValueToString(getCompilerOptionValue(options, option))).join("|") + (options.pathsBasePath ? `|${options.pathsBasePath}` : undefined);
741+
}
742+
743+
/** @internal */
744+
export interface CacheWithRedirects<K, V> {
745+
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<K, V>;
746+
update(newOptions: CompilerOptions): void;
726747
clear(): void;
727-
setOwnOptions(newOptions: CompilerOptions): void;
728-
setOwnMap(newOwnMap: Map<string, T>): void;
729748
}
730749

731750
/** @internal */
732-
export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> {
733-
let ownMap: Map<string, T> = new Map();
734-
const redirectsMap = new Map<Path, Map<string, T>>();
751+
export function createCacheWithRedirects<K, V>(ownOptions: CompilerOptions | undefined): CacheWithRedirects<K, V> {
752+
type RedirectsCacheKey = string & { __compilerOptionsKey: any; };
753+
const redirectsMap = new Map<CompilerOptions, Map<K, V>>();
754+
const optionsToRedirectsKey = new Map<CompilerOptions, RedirectsCacheKey>();
755+
const redirectsKeyToMap = new Map<RedirectsCacheKey, Map<K, V>>();
756+
let ownMap = new Map<K, V>();
757+
if (ownOptions) redirectsMap.set(ownOptions, ownMap);
735758
return {
736-
getOwnMap,
737-
redirectsMap,
738759
getOrCreateMapOfCacheRedirects,
760+
update,
739761
clear,
740-
setOwnOptions,
741-
setOwnMap
742762
};
743763

744-
function getOwnMap() {
745-
return ownMap;
746-
}
747-
748-
function setOwnOptions(newOptions: CompilerOptions) {
749-
options = newOptions;
764+
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<K, V> {
765+
return redirectedReference ?
766+
getOrCreateMap(redirectedReference.commandLine.options) :
767+
ownMap;
750768
}
751769

752-
function setOwnMap(newOwnMap: Map<string, T>) {
753-
ownMap = newOwnMap;
770+
function update(newOptions: CompilerOptions) {
771+
if (ownOptions !== newOptions) {
772+
if (ownOptions) ownMap = getOrCreateMap(newOptions); // set new map for new options as ownMap
773+
else redirectsMap.set(newOptions, ownMap); // Use existing map if oldOptions = undefined
774+
ownOptions = newOptions;
775+
}
754776
}
755777

756-
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
757-
if (!redirectedReference) {
758-
return ownMap;
759-
}
760-
const path = redirectedReference.sourceFile.path;
761-
let redirects = redirectsMap.get(path);
762-
if (!redirects) {
763-
// Reuse map if redirected reference map uses same resolution
764-
redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? new Map() : ownMap;
765-
redirectsMap.set(path, redirects);
778+
function getOrCreateMap(redirectOptions: CompilerOptions): Map<K, V> {
779+
let result = redirectsMap.get(redirectOptions);
780+
if (result) return result;
781+
const key = getRedirectsCacheKey(redirectOptions);
782+
result = redirectsKeyToMap.get(key);
783+
if (!result) {
784+
if (ownOptions) {
785+
const ownKey = getRedirectsCacheKey(ownOptions);
786+
if (ownKey === key) result = ownMap;
787+
else if (!redirectsKeyToMap.has(ownKey)) redirectsKeyToMap.set(ownKey, ownMap);
788+
}
789+
redirectsKeyToMap.set(key, result ??= new Map());
766790
}
767-
return redirects;
791+
redirectsMap.set(redirectOptions, result);
792+
return result;
768793
}
769794

770795
function clear() {
796+
const ownKey = ownOptions && optionsToRedirectsKey.get(ownOptions);
771797
ownMap.clear();
772798
redirectsMap.clear();
799+
optionsToRedirectsKey.clear();
800+
redirectsKeyToMap.clear();
801+
if (ownOptions) {
802+
if (ownKey) optionsToRedirectsKey.set(ownOptions, ownKey);
803+
redirectsMap.set(ownOptions, ownMap);
804+
}
805+
}
806+
807+
function getRedirectsCacheKey(options: CompilerOptions) {
808+
let result = optionsToRedirectsKey.get(options);
809+
if (!result) {
810+
optionsToRedirectsKey.set(options, result = getKeyForCompilerOptions(options, moduleResolutionOptionDeclarations) as RedirectsCacheKey);
811+
}
812+
return result;
773813
}
774814
}
775815

@@ -794,7 +834,7 @@ function createPackageJsonInfoCache(currentDirectory: string, getCanonicalFileNa
794834
}
795835
}
796836

797-
function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T {
837+
function getOrCreateCache<K, V>(cacheWithRedirects: CacheWithRedirects<K, V>, redirectedReference: ResolvedProjectReference | undefined, key: K, create: () => V): V {
798838
const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
799839
let result = cache.get(key);
800840
if (!result) {
@@ -804,35 +844,7 @@ function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirect
804844
return result;
805845
}
806846

807-
function updateRedirectsMap<T>(
808-
options: CompilerOptions,
809-
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>,
810-
moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache>
811-
) {
812-
if (!options.configFile) return;
813-
if (directoryToModuleNameMap.redirectsMap.size === 0) {
814-
// The own map will be for projectCompilerOptions
815-
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size === 0);
816-
Debug.assert(directoryToModuleNameMap.getOwnMap().size === 0);
817-
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.getOwnMap().size === 0);
818-
directoryToModuleNameMap.redirectsMap.set(options.configFile.path, directoryToModuleNameMap.getOwnMap());
819-
moduleNameToDirectoryMap?.redirectsMap.set(options.configFile.path, moduleNameToDirectoryMap.getOwnMap());
820-
}
821-
else {
822-
// Set correct own map
823-
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size > 0);
824-
const ref: ResolvedProjectReference = {
825-
sourceFile: options.configFile,
826-
commandLine: { options } as ParsedCommandLine
827-
};
828-
directoryToModuleNameMap.setOwnMap(directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref));
829-
moduleNameToDirectoryMap?.setOwnMap(moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref));
830-
}
831-
directoryToModuleNameMap.setOwnOptions(options);
832-
moduleNameToDirectoryMap?.setOwnOptions(options);
833-
}
834-
835-
function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>): PerDirectoryResolutionCache<T> {
847+
function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects<Path, ModeAwareCache<T>>): PerDirectoryResolutionCache<T> {
836848
return {
837849
getOrCreateCacheForDirectory,
838850
clear,
@@ -844,19 +856,24 @@ function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanon
844856
}
845857

846858
function update(options: CompilerOptions) {
847-
updateRedirectsMap(options, directoryToModuleNameMap);
859+
directoryToModuleNameMap.update(options);
848860
}
849861

850862
function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
851863
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
852-
return getOrCreateCache<ModeAwareCache<T>>(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache());
864+
return getOrCreateCache(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache());
853865
}
854866
}
855867

868+
/** @internal */
869+
export type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; };
870+
/** @internal */
871+
export function createModeAwareCacheKey(specifier: string, mode: ResolutionMode) {
872+
return (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey;
873+
}
856874
/** @internal */
857875
export function createModeAwareCache<T>(): ModeAwareCache<T> {
858876
const underlying = new Map<ModeAwareCacheKey, T>();
859-
type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; };
860877
const memoizedReverseKeys = new Map<ModeAwareCacheKey, [specifier: string, mode: ResolutionMode]>();
861878

862879
const cache: ModeAwareCache<T> = {
@@ -887,7 +904,7 @@ export function createModeAwareCache<T>(): ModeAwareCache<T> {
887904
return cache;
888905

889906
function getUnderlyingCacheKey(specifier: string, mode: ResolutionMode) {
890-
const result = (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey;
907+
const result = createModeAwareCacheKey(specifier, mode);
891908
memoizedReverseKeys.set(result, [specifier, mode]);
892909
return result;
893910
}
@@ -919,24 +936,10 @@ export function createModuleResolutionCache(
919936
currentDirectory: string,
920937
getCanonicalFileName: (s: string) => string,
921938
options?: CompilerOptions
922-
): ModuleResolutionCache;
923-
/** @internal */
924-
export function createModuleResolutionCache(
925-
currentDirectory: string,
926-
getCanonicalFileName: GetCanonicalFileName,
927-
options: undefined,
928-
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>,
929-
moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>,
930-
): ModuleResolutionCache;
931-
export function createModuleResolutionCache(
932-
currentDirectory: string,
933-
getCanonicalFileName: GetCanonicalFileName,
934-
options?: CompilerOptions,
935-
directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>,
936-
moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache>,
937939
): ModuleResolutionCache {
938-
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
939-
moduleNameToDirectoryMap ||= createCacheWithRedirects(options);
940+
const directoryToModuleNameMap = createCacheWithRedirects<Path, ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>(options);
941+
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap);
942+
const moduleNameToDirectoryMap = createCacheWithRedirects<ModeAwareCacheKey, PerModuleNameCache>(options);
940943
const packageJsonInfoCache = createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);
941944

942945
return {
@@ -956,20 +959,21 @@ export function createModuleResolutionCache(
956959

957960
function clearAllExceptPackageJsonInfoCache() {
958961
perDirectoryResolutionCache.clear();
959-
moduleNameToDirectoryMap!.clear();
962+
moduleNameToDirectoryMap.clear();
960963
}
961964

962965
function update(options: CompilerOptions) {
963-
updateRedirectsMap(options, directoryToModuleNameMap!, moduleNameToDirectoryMap);
966+
directoryToModuleNameMap.update(options);
967+
moduleNameToDirectoryMap.update(options);
964968
}
965969

966970
function getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ResolutionMode, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
967971
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
968-
return getOrCreateCache(moduleNameToDirectoryMap!, redirectedReference, mode === undefined ? nonRelativeModuleName : `${mode}|${nonRelativeModuleName}`, createPerModuleNameCache);
972+
return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, createModeAwareCacheKey(nonRelativeModuleName, mode), createPerModuleNameCache);
969973
}
970974

971975
function createPerModuleNameCache(): PerModuleNameCache {
972-
const directoryPathMap = new Map<string, ResolvedModuleWithFailedLookupLocations>();
976+
const directoryPathMap = new Map<Path, ResolvedModuleWithFailedLookupLocations>();
973977

974978
return { get, set };
975979

@@ -1046,23 +1050,9 @@ export function createTypeReferenceDirectiveResolutionCache(
10461050
getCanonicalFileName: (s: string) => string,
10471051
options?: CompilerOptions,
10481052
packageJsonInfoCache?: PackageJsonInfoCache,
1049-
): TypeReferenceDirectiveResolutionCache;
1050-
/** @internal */
1051-
export function createTypeReferenceDirectiveResolutionCache(
1052-
currentDirectory: string,
1053-
getCanonicalFileName: GetCanonicalFileName,
1054-
options: undefined,
1055-
packageJsonInfoCache: PackageJsonInfoCache | undefined,
1056-
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>,
1057-
): TypeReferenceDirectiveResolutionCache;
1058-
export function createTypeReferenceDirectiveResolutionCache(
1059-
currentDirectory: string,
1060-
getCanonicalFileName: GetCanonicalFileName,
1061-
options?: CompilerOptions,
1062-
packageJsonInfoCache?: PackageJsonInfoCache | undefined,
1063-
directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>,
10641053
): TypeReferenceDirectiveResolutionCache {
1065-
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
1054+
const directoryToModuleNameMap = createCacheWithRedirects<Path, ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>(options);
1055+
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap);
10661056
packageJsonInfoCache ||= createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);
10671057

10681058
return {

0 commit comments

Comments
 (0)