mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user