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
This commit is contained in:
Sheetal Nandi
2022-12-01 10:17:58 -08:00
committed by GitHub
parent 9089d5390a
commit 7b7f6a75ea
18 changed files with 1494 additions and 342 deletions
+4 -7
View File
@@ -99,6 +99,7 @@ import {
createFileDiagnostic,
createGetCanonicalFileName,
createGetSymbolWalker,
createModeAwareCacheKey,
createPrinter,
createPropertyNameNodeForIdentifierOrLiteral,
createScanner,
@@ -337,8 +338,8 @@ import {
hasAccessorModifier,
hasAmbientModifier,
hasContextSensitiveParameters,
hasDecorators,
HasDecorators,
hasDecorators,
hasDynamicName,
hasEffectiveModifier,
hasEffectiveModifiers,
@@ -347,8 +348,8 @@ import {
hasExtension,
HasIllegalDecorators,
HasIllegalModifiers,
hasInitializer,
HasInitializer,
hasInitializer,
hasJSDocNodes,
hasJSDocParameterTags,
hasJsonModuleEmitEnabled,
@@ -7406,7 +7407,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
const cacheKey = createModeAwareCacheKey(contextFile.path, resolutionMode);
const links = getSymbolLinks(symbol);
let specifier = links.specifierCache && links.specifierCache.get(cacheKey);
if (!specifier) {
@@ -7435,10 +7436,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
links.specifierCache.set(cacheKey, specifier);
}
return specifier;
function getSpecifierCacheKey(path: string, mode: ResolutionMode | undefined) {
return mode === undefined ? path : `${mode}|${path}`;
}
}
function symbolToEntityNameNode(symbol: Symbol): EntityName {
+98 -108
View File
@@ -5,6 +5,7 @@ import {
changeAnyExtension,
CharacterCodes,
combinePaths,
CommandLineOption,
comparePaths,
Comparison,
CompilerOptions,
@@ -35,6 +36,7 @@ import {
getBaseFileName,
GetCanonicalFileName,
getCommonSourceDirectory,
getCompilerOptionValue,
getDirectoryPath,
GetEffectiveTypeRootsHost,
getEmitModuleKind,
@@ -66,14 +68,13 @@ import {
ModuleKind,
ModuleResolutionHost,
ModuleResolutionKind,
moduleResolutionOptionDeclarations,
noop,
noopPush,
normalizePath,
normalizeSlashes,
optionsHaveModuleResolutionChanges,
PackageId,
packageIdToString,
ParsedCommandLine,
Path,
pathIsRelative,
Pattern,
@@ -718,58 +719,97 @@ export interface PerModuleNameCache {
set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
}
/** @internal */
export interface CacheWithRedirects<T> {
getOwnMap: () => Map<string, T>;
redirectsMap: Map<Path, Map<string, T>>;
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<string, T>;
clear(): void;
setOwnOptions(newOptions: CompilerOptions): void;
setOwnMap(newOwnMap: Map<string, T>): void;
function compilerOptionValueToString(value: unknown): string {
if (value === null || typeof value !== "object") { // eslint-disable-line no-null/no-null
return "" + value;
}
if (isArray(value)) {
return `[${value.map(e => compilerOptionValueToString(e))?.join(",")}]`;
}
let str = "{";
for (const key in value) {
if (hasProperty(value, key)) {
str += `${key}: ${compilerOptionValueToString((value as any)[key])}`;
}
}
return str + "}";
}
/** @internal */
export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> {
let ownMap: Map<string, T> = new Map();
const redirectsMap = new Map<Path, Map<string, T>>();
export function getKeyForCompilerOptions(options: CompilerOptions, affectingOptionDeclarations: readonly CommandLineOption[]) {
return affectingOptionDeclarations.map(option => compilerOptionValueToString(getCompilerOptionValue(options, option))).join("|") + (options.pathsBasePath ? `|${options.pathsBasePath}` : undefined);
}
/** @internal */
export interface CacheWithRedirects<K, V> {
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<K, V>;
update(newOptions: CompilerOptions): void;
clear(): void;
}
/** @internal */
export function createCacheWithRedirects<K, V>(ownOptions: CompilerOptions | undefined): CacheWithRedirects<K, V> {
type RedirectsCacheKey = string & { __compilerOptionsKey: any; };
const redirectsMap = new Map<CompilerOptions, Map<K, V>>();
const optionsToRedirectsKey = new Map<CompilerOptions, RedirectsCacheKey>();
const redirectsKeyToMap = new Map<RedirectsCacheKey, Map<K, V>>();
let ownMap = new Map<K, V>();
if (ownOptions) redirectsMap.set(ownOptions, ownMap);
return {
getOwnMap,
redirectsMap,
getOrCreateMapOfCacheRedirects,
update,
clear,
setOwnOptions,
setOwnMap
};
function getOwnMap() {
return ownMap;
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<K, V> {
return redirectedReference ?
getOrCreateMap(redirectedReference.commandLine.options) :
ownMap;
}
function setOwnOptions(newOptions: CompilerOptions) {
options = newOptions;
}
function setOwnMap(newOwnMap: Map<string, T>) {
ownMap = newOwnMap;
}
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
if (!redirectedReference) {
return ownMap;
function update(newOptions: CompilerOptions) {
if (ownOptions !== newOptions) {
if (ownOptions) ownMap = getOrCreateMap(newOptions); // set new map for new options as ownMap
else redirectsMap.set(newOptions, ownMap); // Use existing map if oldOptions = undefined
ownOptions = newOptions;
}
const path = redirectedReference.sourceFile.path;
let redirects = redirectsMap.get(path);
if (!redirects) {
// Reuse map if redirected reference map uses same resolution
redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? new Map() : ownMap;
redirectsMap.set(path, redirects);
}
function getOrCreateMap(redirectOptions: CompilerOptions): Map<K, V> {
let result = redirectsMap.get(redirectOptions);
if (result) return result;
const key = getRedirectsCacheKey(redirectOptions);
result = redirectsKeyToMap.get(key);
if (!result) {
if (ownOptions) {
const ownKey = getRedirectsCacheKey(ownOptions);
if (ownKey === key) result = ownMap;
else if (!redirectsKeyToMap.has(ownKey)) redirectsKeyToMap.set(ownKey, ownMap);
}
redirectsKeyToMap.set(key, result ??= new Map());
}
return redirects;
redirectsMap.set(redirectOptions, result);
return result;
}
function clear() {
const ownKey = ownOptions && optionsToRedirectsKey.get(ownOptions);
ownMap.clear();
redirectsMap.clear();
optionsToRedirectsKey.clear();
redirectsKeyToMap.clear();
if (ownOptions) {
if (ownKey) optionsToRedirectsKey.set(ownOptions, ownKey);
redirectsMap.set(ownOptions, ownMap);
}
}
function getRedirectsCacheKey(options: CompilerOptions) {
let result = optionsToRedirectsKey.get(options);
if (!result) {
optionsToRedirectsKey.set(options, result = getKeyForCompilerOptions(options, moduleResolutionOptionDeclarations) as RedirectsCacheKey);
}
return result;
}
}
@@ -794,7 +834,7 @@ function createPackageJsonInfoCache(currentDirectory: string, getCanonicalFileNa
}
}
function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T {
function getOrCreateCache<K, V>(cacheWithRedirects: CacheWithRedirects<K, V>, redirectedReference: ResolvedProjectReference | undefined, key: K, create: () => V): V {
const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let result = cache.get(key);
if (!result) {
@@ -804,35 +844,7 @@ function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirect
return result;
}
function updateRedirectsMap<T>(
options: CompilerOptions,
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>,
moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache>
) {
if (!options.configFile) return;
if (directoryToModuleNameMap.redirectsMap.size === 0) {
// The own map will be for projectCompilerOptions
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size === 0);
Debug.assert(directoryToModuleNameMap.getOwnMap().size === 0);
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.getOwnMap().size === 0);
directoryToModuleNameMap.redirectsMap.set(options.configFile.path, directoryToModuleNameMap.getOwnMap());
moduleNameToDirectoryMap?.redirectsMap.set(options.configFile.path, moduleNameToDirectoryMap.getOwnMap());
}
else {
// Set correct own map
Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size > 0);
const ref: ResolvedProjectReference = {
sourceFile: options.configFile,
commandLine: { options } as ParsedCommandLine
};
directoryToModuleNameMap.setOwnMap(directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref));
moduleNameToDirectoryMap?.setOwnMap(moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref));
}
directoryToModuleNameMap.setOwnOptions(options);
moduleNameToDirectoryMap?.setOwnOptions(options);
}
function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>): PerDirectoryResolutionCache<T> {
function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects<Path, ModeAwareCache<T>>): PerDirectoryResolutionCache<T> {
return {
getOrCreateCacheForDirectory,
clear,
@@ -844,19 +856,24 @@ function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanon
}
function update(options: CompilerOptions) {
updateRedirectsMap(options, directoryToModuleNameMap);
directoryToModuleNameMap.update(options);
}
function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
return getOrCreateCache<ModeAwareCache<T>>(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache());
return getOrCreateCache(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache());
}
}
/** @internal */
export type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; };
/** @internal */
export function createModeAwareCacheKey(specifier: string, mode: ResolutionMode) {
return (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey;
}
/** @internal */
export function createModeAwareCache<T>(): ModeAwareCache<T> {
const underlying = new Map<ModeAwareCacheKey, T>();
type ModeAwareCacheKey = string & { __modeAwareCacheKey: any; };
const memoizedReverseKeys = new Map<ModeAwareCacheKey, [specifier: string, mode: ResolutionMode]>();
const cache: ModeAwareCache<T> = {
@@ -887,7 +904,7 @@ export function createModeAwareCache<T>(): ModeAwareCache<T> {
return cache;
function getUnderlyingCacheKey(specifier: string, mode: ResolutionMode) {
const result = (mode === undefined ? specifier : `${mode}|${specifier}`) as ModeAwareCacheKey;
const result = createModeAwareCacheKey(specifier, mode);
memoizedReverseKeys.set(result, [specifier, mode]);
return result;
}
@@ -919,24 +936,10 @@ export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions
): ModuleResolutionCache;
/** @internal */
export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options: undefined,
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>,
moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>,
): ModuleResolutionCache;
export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options?: CompilerOptions,
directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>,
moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache>,
): ModuleResolutionCache {
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
moduleNameToDirectoryMap ||= createCacheWithRedirects(options);
const directoryToModuleNameMap = createCacheWithRedirects<Path, ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>(options);
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap);
const moduleNameToDirectoryMap = createCacheWithRedirects<ModeAwareCacheKey, PerModuleNameCache>(options);
const packageJsonInfoCache = createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);
return {
@@ -956,20 +959,21 @@ export function createModuleResolutionCache(
function clearAllExceptPackageJsonInfoCache() {
perDirectoryResolutionCache.clear();
moduleNameToDirectoryMap!.clear();
moduleNameToDirectoryMap.clear();
}
function update(options: CompilerOptions) {
updateRedirectsMap(options, directoryToModuleNameMap!, moduleNameToDirectoryMap);
directoryToModuleNameMap.update(options);
moduleNameToDirectoryMap.update(options);
}
function getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ResolutionMode, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
return getOrCreateCache(moduleNameToDirectoryMap!, redirectedReference, mode === undefined ? nonRelativeModuleName : `${mode}|${nonRelativeModuleName}`, createPerModuleNameCache);
return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, createModeAwareCacheKey(nonRelativeModuleName, mode), createPerModuleNameCache);
}
function createPerModuleNameCache(): PerModuleNameCache {
const directoryPathMap = new Map<string, ResolvedModuleWithFailedLookupLocations>();
const directoryPathMap = new Map<Path, ResolvedModuleWithFailedLookupLocations>();
return { get, set };
@@ -1046,23 +1050,9 @@ export function createTypeReferenceDirectiveResolutionCache(
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache,
): TypeReferenceDirectiveResolutionCache;
/** @internal */
export function createTypeReferenceDirectiveResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options: undefined,
packageJsonInfoCache: PackageJsonInfoCache | undefined,
directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>,
): TypeReferenceDirectiveResolutionCache;
export function createTypeReferenceDirectiveResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache | undefined,
directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>,
): TypeReferenceDirectiveResolutionCache {
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
const directoryToModuleNameMap = createCacheWithRedirects<Path, ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>(options);
const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap);
packageJsonInfoCache ||= createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);
return {
+8 -10
View File
@@ -814,17 +814,16 @@ export function loadWithTypeDirectiveCache<T>(names: string[] | readonly FileRef
return [];
}
const resolutions: T[] = [];
const cache = new Map<string, T>();
const cache = createModeAwareCache<T>();
for (const name of names) {
let result: T;
const mode = getModeForFileReference(name, containingFileMode);
const strName = getResolutionName(name);
const cacheKey = mode !== undefined ? `${mode}|${strName}` : strName;
if (cache.has(cacheKey)) {
result = cache.get(cacheKey)!;
if (cache.has(strName, mode)) {
result = cache.get(strName, mode)!;
}
else {
cache.set(cacheKey, result = loader(strName, containingFile, redirectedReference, mode));
cache.set(strName, mode, result = loader(strName, containingFile, redirectedReference, mode));
}
resolutions.push(result);
}
@@ -944,7 +943,7 @@ export function loadWithModeAwareCache<T>(names: readonly StringLiteralLike[] |
return [];
}
const resolutions: T[] = [];
const cache = new Map<string, T>();
const cache = createModeAwareCache<T>();
let i = 0;
for (const entry of resolutionInfo ? resolutionInfo.names : names) {
let result: T;
@@ -953,12 +952,11 @@ export function loadWithModeAwareCache<T>(names: readonly StringLiteralLike[] |
getModeForResolutionAtIndex(containingFile, i);
i++;
const name = isString(entry) ? entry : entry.text;
const cacheKey = mode !== undefined ? `${mode}|${name}` : name;
if (cache.has(cacheKey)) {
result = cache.get(cacheKey)!;
if (cache.has(name, mode)) {
result = cache.get(name, mode)!;
}
else {
cache.set(cacheKey, result = loader(name, mode, containingFileName, redirectedReference));
cache.set(name, mode, result = loader(name, mode, containingFileName, redirectedReference));
}
resolutions.push(result);
}
+12 -59
View File
@@ -2,14 +2,12 @@ import * as ts from "./_namespaces/ts";
import {
arrayToMap,
CachedDirectoryStructureHost,
CacheWithRedirects,
CharacterCodes,
clearMap,
closeFileWatcher,
closeFileWatcherOf,
CompilerOptions,
contains,
createCacheWithRedirects,
createModeAwareCache,
createModuleResolutionCache,
createMultiMap,
@@ -65,7 +63,6 @@ import {
parseNodeModuleFromPath,
Path,
pathContainsNodeModules,
PerModuleNameCache,
Program,
removeSuffix,
removeTrailingDirectorySeparator,
@@ -292,24 +289,18 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// The key in the map is source file's path.
// The values are Map of resolutions with key being name lookedup.
const resolvedModuleNames = new Map<Path, ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
/*options*/ undefined,
perDirectoryResolvedModuleNames,
nonRelativeModuleNameCache,
resolutionHost.getCompilationSettings(),
);
const resolvedTypeReferenceDirectives = new Map<Path, ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
/*options*/ undefined,
resolutionHost.getCompilationSettings(),
moduleResolutionCache.getPackageJsonInfoCache(),
perDirectoryResolvedTypeReferenceDirectives
);
/**
@@ -387,6 +378,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
affectingPathChecksForFile = undefined;
moduleResolutionCache.clear();
typeReferenceDirectiveResolutionCache.clear();
moduleResolutionCache.update(resolutionHost.getCompilationSettings());
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
impliedFormatPackageJsons.clear();
hasChangedAutomaticTypeDirectiveNames = false;
}
@@ -514,8 +507,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
names: readonly string[] | readonly FileReference[];
containingFile: string;
redirectedReference: ResolvedProjectReference | undefined;
cache: Map<Path, ModeAwareCache<T>>;
perDirectoryCacheWithRedirects: CacheWithRedirects<ModeAwareCache<T>>;
perFileCache: Map<Path, ModeAwareCache<T>>;
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile, resolutionMode?: ResolutionMode) => T;
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
shouldRetryResolution: (t: T) => boolean;
@@ -527,19 +519,12 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
names, containingFile, redirectedReference,
cache, perDirectoryCacheWithRedirects,
perFileCache,
loader, getResolutionWithResolvedFileName,
shouldRetryResolution, reusedNames, resolutionInfo, logChanges, containingSourceFile, containingSourceFileMode
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!;
const dirPath = getDirectoryPath(path);
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let perDirectoryResolution = perDirectoryCache.get(dirPath);
if (!perDirectoryResolution) {
perDirectoryResolution = createModeAwareCache();
perDirectoryCache.set(dirPath, perDirectoryResolution);
}
const resolutionsInFile = perFileCache.get(path) || perFileCache.set(path, createModeAwareCache()).get(path)!;
const resolvedModules: (R | undefined)[] = [];
const compilerOptions = resolutionHost.getCompilationSettings();
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
@@ -575,39 +560,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// If the name is unresolved import that was invalidated, recalculate
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
const existingResolution = resolution;
const resolutionInDirectory = perDirectoryResolution.get(name, mode);
if (resolutionInDirectory) {
resolution = resolutionInDirectory;
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
if (isTraceEnabled(compilerOptions, host)) {
const resolved = getResolutionWithResolvedFileName(resolution);
trace(
host,
loader === resolveModuleName as unknown ?
resolved?.resolvedFileName ?
resolved.packageId ?
Diagnostics.Reusing_resolution_of_module_0_from_1_found_in_cache_from_location_2_it_was_successfully_resolved_to_3_with_Package_ID_4 :
Diagnostics.Reusing_resolution_of_module_0_from_1_found_in_cache_from_location_2_it_was_successfully_resolved_to_3 :
Diagnostics.Reusing_resolution_of_module_0_from_1_found_in_cache_from_location_2_it_was_not_resolved :
resolved?.resolvedFileName ?
resolved.packageId ?
Diagnostics.Reusing_resolution_of_type_reference_directive_0_from_1_found_in_cache_from_location_2_it_was_successfully_resolved_to_3_with_Package_ID_4 :
Diagnostics.Reusing_resolution_of_type_reference_directive_0_from_1_found_in_cache_from_location_2_it_was_successfully_resolved_to_3 :
Diagnostics.Reusing_resolution_of_type_reference_directive_0_from_1_found_in_cache_from_location_2_it_was_not_resolved,
name,
containingFile,
getDirectoryPath(containingFile),
resolved?.resolvedFileName,
resolved?.packageId && packageIdToString(resolved.packageId)
);
}
}
else {
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference, containingSourceFile, mode);
perDirectoryResolution.set(name, mode, resolution);
if (resolutionHost.onDiscoveredSymlink && resolutionIsSymlink(resolution)) {
resolutionHost.onDiscoveredSymlink();
}
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference, containingSourceFile, mode);
if (resolutionHost.onDiscoveredSymlink && resolutionIsSymlink(resolution)) {
resolutionHost.onDiscoveredSymlink();
}
resolutionsInFile.set(name, mode, resolution);
watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, path, getResolutionWithResolvedFileName);
@@ -703,8 +658,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
names: typeDirectiveNames,
containingFile,
redirectedReference,
cache: resolvedTypeReferenceDirectives,
perDirectoryCacheWithRedirects: perDirectoryResolvedTypeReferenceDirectives,
perFileCache: resolvedTypeReferenceDirectives,
loader: resolveTypeReferenceDirective,
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective,
shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined,
@@ -725,8 +679,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
names: moduleNames,
containingFile,
redirectedReference,
cache: resolvedModuleNames,
perDirectoryCacheWithRedirects: perDirectoryResolvedModuleNames,
perFileCache: resolvedModuleNames,
loader: resolveModuleName,
getResolutionWithResolvedFileName: getResolvedModule,
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
+2 -1
View File
@@ -4,6 +4,7 @@ import {
EmitHelperFactory,
MapLike,
ModeAwareCache,
ModeAwareCacheKey,
ModuleResolutionCache,
MultiMap,
NodeFactoryFlags,
@@ -5503,7 +5504,7 @@ export interface SymbolLinks {
enumKind?: EnumKind; // Enum declaration classification
originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol`
lateSymbol?: Symbol; // Late-bound symbol for a computed property
specifierCache?: Map<string, string>; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings
specifierCache?: Map<ModeAwareCacheKey, string>; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings
extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in
extendedContainersByFile?: Map<NodeId, Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
+2 -21
View File
@@ -8,17 +8,14 @@ import {
ensureScriptKind,
firstDefinedIterator,
forEachEntry,
getCompilerOptionValue,
getEmitScriptTarget,
getImpliedNodeFormatForFile,
getKeyForCompilerOptions,
getOrUpdate,
getSetExternalModuleIndicator,
hasProperty,
identity,
isArray,
IScriptSnapshot,
isDeclarationFileName,
map,
MinimalResolutionCacheHost,
Path,
ResolutionMode,
@@ -407,24 +404,8 @@ export function createDocumentRegistryInternal(useCaseSensitiveFileNames?: boole
};
}
function compilerOptionValueToString(value: unknown): string {
if (value === null || typeof value !== "object") { // eslint-disable-line no-null/no-null
return "" + value;
}
if (isArray(value)) {
return `[${map(value, e => compilerOptionValueToString(e))?.join(",")}]`;
}
let str = "{";
for (const key in value) {
if (hasProperty(value, key)) {
str += `${key}: ${compilerOptionValueToString((value as any)[key])}`;
}
}
return str + "}";
}
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
return sourceFileAffectingCompilerOptions.map(option => compilerOptionValueToString(getCompilerOptionValue(settings, option))).join("|") + (settings.pathsBasePath ? `|${settings.pathsBasePath}` : undefined) as DocumentRegistryBucketKey;
return getKeyForCompilerOptions(settings, sourceFileAffectingCompilerOptions) as DocumentRegistryBucketKey;
}
function getDocumentRegistryBucketKeyWithMode(key: DocumentRegistryBucketKey, mode: ResolutionMode) {
@@ -30,7 +30,7 @@ describe("unittests:: tsc-watch:: projects with references: invoking when refere
],
{ currentDirectory: `/user/username/projects/sample1` }
),
commandLineArgs: ["-w", "-p", "tests"],
commandLineArgs: ["-w", "-p", "tests", "--traceResolution", "--explainFiles"],
changes: [
{
caption: "local edit in logic ts, and build logic",
@@ -91,7 +91,7 @@ describe("unittests:: tsc-watch:: projects with references: invoking when refere
],
{ currentDirectory: `/user/username/projects/transitiveReferences` }
),
commandLineArgs: ["-w", "-p", "tsconfig.c.json"],
commandLineArgs: ["-w", "-p", "tsconfig.c.json", "--traceResolution", "--explainFiles"],
changes: [
{
caption: "non local edit b ts, and build b",
@@ -179,7 +179,7 @@ describe("unittests:: tsc-watch:: projects with references: invoking when refere
],
{ currentDirectory: `/user/username/projects/transitiveReferences` }
),
commandLineArgs: ["-w", "-p", "tsconfig.c.json"],
commandLineArgs: ["-w", "-p", "tsconfig.c.json", "--traceResolution", "--explainFiles"],
changes: ts.emptyArray,
baselineDependencies: true,
});
@@ -234,7 +234,7 @@ X;`,
],
{ currentDirectory: `/user/username/projects/transitiveReferences` }
),
commandLineArgs: ["-w", "-p", "c"],
commandLineArgs: ["-w", "-p", "c", "--traceResolution", "--explainFiles"],
changes: [
{
caption: "non local edit b ts, and build b",
@@ -353,7 +353,7 @@ X;`,
],
{ currentDirectory: `/user/username/projects/transitiveReferences` }
),
commandLineArgs: ["-w", "-p", "c"],
commandLineArgs: ["-w", "-p", "c", "--traceResolution", "--explainFiles"],
changes: [
{
caption: "non local edit b ts, and build b",
@@ -439,7 +439,7 @@ X;`,
],
{ currentDirectory: `/user/username/projects/sample1` }
),
commandLineArgs: ["-w", "-p", "logic"],
commandLineArgs: ["-w", "-p", "logic", "--traceResolution", "--explainFiles"],
changes: [
{
caption: "change declration map in core",
@@ -1,10 +1,14 @@
import * as ts from "../../_namespaces/ts";
import * as Utils from "../../_namespaces/Utils";
import {
createServerHost,
File,
libFile,
TestServerHost,
} from "../virtualFileSystemWithWatch";
import {
compilerOptionsToConfigJson,
} from "../tsc/helpers";
import {
baselineTsserverLogs,
checkNumberOfProjects,
@@ -630,3 +634,73 @@ export const x = 10;`
});
});
});
describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem with project references", () => {
it("sharing across references", () => {
const host = createServerHost({
"/src/projects/node_modules/moduleX/index.d.ts": "export const x = 10;",
"/src/projects/common/tsconfig.json": JSON.stringify({
compilerOptions: compilerOptionsToConfigJson({
composite: true,
traceResolution: true,
}),
}),
"/src/projects/common/moduleA.ts": "export const a = 10;",
"/src/projects/common/moduleB.ts": Utils.dedent`
import { x } from "moduleX";
export const b = x;
`,
"/src/projects/app/tsconfig.json": JSON.stringify({
compilerOptions: compilerOptionsToConfigJson({
composite: true,
traceResolution: true,
}),
references: [{ path: "../common" }],
}),
"/src/projects/app/appA.ts": Utils.dedent`
import { x } from "moduleX";
export const y = x;
`,
"/src/projects/app/appB.ts": Utils.dedent`
import { x } from "../common/moduleB";
export const y = x;
`,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession(["/src/projects/app/appB.ts"], session);
baselineTsserverLogs("resolutionCache", "sharing across references", session);
});
it("not sharing across references", () => {
const host = createServerHost({
"/src/projects/node_modules/moduleX/index.d.ts": "export const x = 10;",
"/src/projects/common/tsconfig.json": JSON.stringify({
compilerOptions: { composite: true, traceResolution: true },
}),
"/src/projects/common/moduleA.ts": "export const a = 10;",
"/src/projects/common/moduleB.ts": Utils.dedent`
import { x } from "moduleX";
export const b = x;
`,
"/src/projects/app/tsconfig.json": JSON.stringify({
compilerOptions: {
composite: true,
traceResolution: true,
typeRoots: [], // Just some sample option that is different across the projects
},
references: [{ path: "../common" }],
}),
"/src/projects/app/appA.ts": Utils.dedent`
import { x } from "moduleX";
export const y = x;
`,
"/src/projects/app/appB.ts": Utils.dedent`
import { x } from "../common/moduleB";
export const y = x;
`,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openFilesForSession(["/src/projects/app/appB.ts"], session);
baselineTsserverLogs("resolutionCache", "not sharing across references", session);
});
});