Skip to content

Commit 6923f2c

Browse files
committed
Handle caching of module resolution depending on project references
1 parent 0e4b10d commit 6923f2c

File tree

9 files changed

+278
-151
lines changed

9 files changed

+278
-151
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ namespace ts {
345345
}
346346
let result: SearchResult<Resolved> | undefined;
347347
if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) {
348-
result = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined);
348+
result = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined);
349349
}
350350
else {
351351
const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName));
@@ -415,15 +415,15 @@ namespace ts {
415415
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
416416
*/
417417
export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache {
418-
getOrCreateCacheForDirectory(directoryName: string): Map<ResolvedModuleWithFailedLookupLocations>;
418+
getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map<ResolvedModuleWithFailedLookupLocations>;
419419
}
420420

421421
/**
422422
* Stored map from non-relative module name to a table: directory -> result of module lookup in this directory
423423
* We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
424424
*/
425425
export interface NonRelativeModuleNameResolutionCache {
426-
getOrCreateCacheForModuleName(nonRelativeModuleName: string): PerModuleNameCache;
426+
getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
427427
}
428428

429429
export interface PerModuleNameCache {
@@ -433,40 +433,78 @@ namespace ts {
433433

434434
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
435435
return createModuleResolutionCacheWithMaps(
436-
createMap<Map<ResolvedModuleWithFailedLookupLocations>>(),
437-
createMap<PerModuleNameCache>(),
436+
createCacheWithRedirects(),
437+
createCacheWithRedirects(),
438438
currentDirectory,
439439
getCanonicalFileName
440440
);
441441
}
442442

443+
/*@internal*/
444+
export interface CacheWithRedirects<T> {
445+
ownMap: Map<T>;
446+
redirectsMap: Map<Map<T>>;
447+
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<T>;
448+
clear(): void;
449+
}
450+
451+
/*@internal*/
452+
export function createCacheWithRedirects<T>(): CacheWithRedirects<T> {
453+
const ownMap: Map<T> = createMap();
454+
const redirectsMap: Map<Map<T>> = createMap();
455+
return {
456+
ownMap,
457+
redirectsMap,
458+
getOrCreateMapOfCacheRedirects,
459+
clear
460+
};
461+
462+
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
463+
if (!redirectedReference) {
464+
return ownMap;
465+
}
466+
const path = redirectedReference.sourceFile.path;
467+
let redirects = redirectsMap.get(path);
468+
if (!redirects) {
469+
redirects = createMap();
470+
redirectsMap.set(path, redirects);
471+
}
472+
return redirects;
473+
}
474+
475+
function clear() {
476+
ownMap.clear();
477+
redirectsMap.clear();
478+
}
479+
}
480+
443481
/*@internal*/
444482
export function createModuleResolutionCacheWithMaps(
445-
directoryToModuleNameMap: Map<Map<ResolvedModuleWithFailedLookupLocations>>,
446-
moduleNameToDirectoryMap: Map<PerModuleNameCache>,
483+
directoryToModuleNameMap: CacheWithRedirects<Map<ResolvedModuleWithFailedLookupLocations>>,
484+
moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>,
447485
currentDirectory: string,
448486
getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache {
449487

450488
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
451489

452-
function getOrCreateCacheForDirectory(directoryName: string) {
490+
function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
453491
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
454-
let perFolderCache = directoryToModuleNameMap.get(path);
455-
if (!perFolderCache) {
456-
perFolderCache = createMap<ResolvedModuleWithFailedLookupLocations>();
457-
directoryToModuleNameMap.set(path, perFolderCache);
458-
}
459-
return perFolderCache;
492+
return getOrCreateCache<Map<ResolvedModuleWithFailedLookupLocations>>(directoryToModuleNameMap, redirectedReference, path, createMap);
460493
}
461494

462-
function getOrCreateCacheForModuleName(nonRelativeModuleName: string): PerModuleNameCache {
495+
function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
463496
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
464-
let perModuleNameCache = moduleNameToDirectoryMap.get(nonRelativeModuleName);
465-
if (!perModuleNameCache) {
466-
perModuleNameCache = createPerModuleNameCache();
467-
moduleNameToDirectoryMap.set(nonRelativeModuleName, perModuleNameCache);
497+
return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, nonRelativeModuleName, createPerModuleNameCache);
498+
}
499+
500+
function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T {
501+
const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
502+
let result = cache.get(key);
503+
if (!result) {
504+
result = create();
505+
cache.set(key, result);
468506
}
469-
return perModuleNameCache;
507+
return result;
470508
}
471509

472510
function createPerModuleNameCache(): PerModuleNameCache {
@@ -560,7 +598,7 @@ namespace ts {
560598
}
561599
}
562600
const containingDirectory = getDirectoryPath(containingFile);
563-
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory);
601+
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference);
564602
let result = perFolderCache && perFolderCache.get(moduleName);
565603

566604
if (result) {
@@ -584,10 +622,10 @@ namespace ts {
584622

585623
switch (moduleResolution) {
586624
case ModuleResolutionKind.NodeJs:
587-
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache);
625+
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
588626
break;
589627
case ModuleResolutionKind.Classic:
590-
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache);
628+
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
591629
break;
592630
default:
593631
return Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`);
@@ -597,7 +635,7 @@ namespace ts {
597635
perFolderCache.set(moduleName, result);
598636
if (!isExternalModuleNameRelative(moduleName)) {
599637
// put result in per-module name cache
600-
cache!.getOrCreateCacheForModuleName(moduleName).set(containingDirectory, result);
638+
cache!.getOrCreateCacheForModuleName(moduleName, redirectedReference).set(containingDirectory, result);
601639
}
602640
}
603641
}
@@ -817,14 +855,14 @@ namespace ts {
817855
}
818856

819857
function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
820-
return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
858+
return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*redirectedReference*/ undefined, /*jsOnly*/ true);
821859
}
822860

823-
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
824-
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, /*jsOnly*/ false);
861+
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
862+
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, redirectedReference, /*jsOnly*/ false);
825863
}
826864

827-
function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, jsOnly: boolean): ResolvedModuleWithFailedLookupLocations {
865+
function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, jsOnly: boolean): ResolvedModuleWithFailedLookupLocations {
828866
const traceEnabled = isTraceEnabled(compilerOptions, host);
829867

830868
const failedLookupLocations: string[] = [];
@@ -852,7 +890,7 @@ namespace ts {
852890
if (traceEnabled) {
853891
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
854892
}
855-
const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache);
893+
const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
856894
if (!resolved) return undefined;
857895

858896
let resolvedValue = resolved.value;
@@ -1190,17 +1228,17 @@ namespace ts {
11901228
return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
11911229
}
11921230

1193-
function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined): SearchResult<Resolved> {
1194-
return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache);
1231+
function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
1232+
return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference);
11951233
}
11961234

11971235
function loadModuleFromNearestNodeModulesDirectoryTypesScope(moduleName: string, directory: string, state: ModuleResolutionState): SearchResult<Resolved> {
11981236
// Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
1199-
return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined);
1237+
return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined);
12001238
}
12011239

1202-
function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined): SearchResult<Resolved> {
1203-
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
1240+
function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
1241+
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
12041242
return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
12051243
if (getBaseFileName(ancestorDirectory) !== "node_modules") {
12061244
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state);
@@ -1368,7 +1406,7 @@ namespace ts {
13681406
}
13691407
}
13701408

1371-
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache): ResolvedModuleWithFailedLookupLocations {
1409+
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
13721410
const traceEnabled = isTraceEnabled(compilerOptions, host);
13731411
const failedLookupLocations: string[] = [];
13741412
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations };
@@ -1385,7 +1423,7 @@ namespace ts {
13851423
}
13861424

13871425
if (!isExternalModuleNameRelative(moduleName)) {
1388-
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
1426+
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
13891427
// Climb up parent directories looking for a module.
13901428
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
13911429
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state);

src/compiler/resolutionCache.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,17 @@ namespace ts {
9090
// The key in the map is source file's path.
9191
// The values are Map of resolutions with key being name lookedup.
9292
const resolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
93-
const perDirectoryResolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
94-
const nonRelaticeModuleNameCache = createMap<PerModuleNameCache>();
93+
const perDirectoryResolvedModuleNames: CacheWithRedirects<Map<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
94+
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
9595
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
9696
perDirectoryResolvedModuleNames,
97-
nonRelaticeModuleNameCache,
97+
nonRelativeModuleNameCache,
9898
getCurrentDirectory(),
9999
resolutionHost.getCanonicalFileName
100100
);
101101

102102
const resolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
103-
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
103+
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
104104

105105
/**
106106
* These are the extensions that failed lookup files will have by default,
@@ -199,7 +199,7 @@ namespace ts {
199199

200200
function clearPerDirectoryResolutions() {
201201
perDirectoryResolvedModuleNames.clear();
202-
nonRelaticeModuleNameCache.clear();
202+
nonRelativeModuleNameCache.clear();
203203
perDirectoryResolvedTypeReferenceDirectives.clear();
204204
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
205205
nonRelativeExternalModuleResolutions.clear();
@@ -244,7 +244,7 @@ namespace ts {
244244
containingFile: string,
245245
redirectedReference: ResolvedProjectReference | undefined,
246246
cache: Map<Map<T>>,
247-
perDirectoryCache: Map<Map<T>>,
247+
perDirectoryCacheWithRedirects: CacheWithRedirects<Map<T>>,
248248
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T,
249249
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
250250
reusedNames: string[] | undefined,
@@ -253,6 +253,7 @@ namespace ts {
253253
const path = resolutionHost.toPath(containingFile);
254254
const resolutionsInFile = cache.get(path) || cache.set(path, createMap()).get(path)!;
255255
const dirPath = getDirectoryPath(path);
256+
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
256257
let perDirectoryResolution = perDirectoryCache.get(dirPath);
257258
if (!perDirectoryResolution) {
258259
perDirectoryResolution = createMap();

src/testRunner/unittests/tsbuild.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ export class cNew {}`);
392392
...getLibs(),
393393
"/src/a.d.ts",
394394
"/src/b.d.ts",
395+
"/src/refs/a.d.ts",
395396
"/src/c.ts"
396397
]);
397398
});

0 commit comments

Comments
 (0)