Skip to content

Commit 005a50f

Browse files
authored
Merge pull request #22136 from Microsoft/moduleResolution
Use cache for the non-relative module resolution and enhance the watches for failed lookup locations
2 parents ca12cdc + 9569b13 commit 005a50f

17 files changed

+481
-86
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3296,7 +3296,7 @@
32963296
"category": "Message",
32973297
"code": 6146
32983298
},
3299-
"Resolution for module '{0}' was found in cache.": {
3299+
"Resolution for module '{0}' was found in cache from location '{1}'.": {
33003300
"category": "Message",
33013301
"code": 6147
33023302
},

src/compiler/moduleNameResolver.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,20 @@ namespace ts {
335335
}
336336

337337
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
338-
const directoryToModuleNameMap = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
339-
const moduleNameToDirectoryMap = createMap<PerModuleNameCache>();
338+
return createModuleResolutionCacheWithMaps(
339+
createMap<Map<ResolvedModuleWithFailedLookupLocations>>(),
340+
createMap<PerModuleNameCache>(),
341+
currentDirectory,
342+
getCanonicalFileName
343+
);
344+
}
345+
346+
/*@internal*/
347+
export function createModuleResolutionCacheWithMaps(
348+
directoryToModuleNameMap: Map<Map<ResolvedModuleWithFailedLookupLocations>>,
349+
moduleNameToDirectoryMap: Map<PerModuleNameCache>,
350+
currentDirectory: string,
351+
getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache {
340352

341353
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
342354

@@ -445,7 +457,7 @@ namespace ts {
445457

446458
if (result) {
447459
if (traceEnabled) {
448-
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
460+
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
449461
}
450462
}
451463
else {
@@ -1188,7 +1200,7 @@ namespace ts {
11881200
const result = cache && cache.get(containingDirectory);
11891201
if (result) {
11901202
if (traceEnabled) {
1191-
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
1203+
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
11921204
}
11931205
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
11941206
}

src/compiler/resolutionCache.ts

Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace ts {
2828
interface ResolutionWithFailedLookupLocations {
2929
readonly failedLookupLocations: ReadonlyArray<string>;
3030
isInvalidated?: boolean;
31+
refCount?: number;
3132
}
3233

3334
interface ResolutionWithResolvedFileName {
@@ -42,6 +43,7 @@ namespace ts {
4243

4344
export interface ResolutionCacheHost extends ModuleResolutionHost {
4445
toPath(fileName: string): Path;
46+
getCanonicalFileName: GetCanonicalFileName;
4547
getCompilationSettings(): CompilerOptions;
4648
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher;
4749
onInvalidatedResolution(): void;
@@ -78,18 +80,25 @@ namespace ts {
7880
let filesWithInvalidatedResolutions: Map<true> | undefined;
7981
let allFilesHaveInvalidatedResolution = false;
8082

83+
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory());
84+
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
85+
8186
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
8287
// The key in the map is source file's path.
8388
// The values are Map of resolutions with key being name lookedup.
8489
const resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
8590
const perDirectoryResolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
91+
const nonRelaticeModuleNameCache = createMap<PerModuleNameCache>();
92+
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
93+
perDirectoryResolvedModuleNames,
94+
nonRelaticeModuleNameCache,
95+
getCurrentDirectory(),
96+
resolutionHost.getCanonicalFileName
97+
);
8698

8799
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
88100
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
89101

90-
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory());
91-
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
92-
93102
/**
94103
* These are the extensions that failed lookup files will have by default,
95104
* any other extension of failed lookup will be store that path in custom failed lookup path
@@ -173,6 +182,7 @@ namespace ts {
173182

174183
function clearPerDirectoryResolutions() {
175184
perDirectoryResolvedModuleNames.clear();
185+
nonRelaticeModuleNameCache.clear();
176186
perDirectoryResolvedTypeReferenceDirectives.clear();
177187
}
178188

@@ -189,7 +199,7 @@ namespace ts {
189199
}
190200

191201
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
192-
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host);
202+
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache);
193203
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
194204
if (!resolutionHost.getGlobalCache) {
195205
return primaryResult;
@@ -248,17 +258,11 @@ namespace ts {
248258
perDirectoryResolution.set(name, resolution);
249259
}
250260
resolutionsInFile.set(name, resolution);
251-
if (resolution.failedLookupLocations) {
252-
if (existingResolution && existingResolution.failedLookupLocations) {
253-
watchAndStopWatchDiffFailedLookupLocations(resolution, existingResolution);
254-
}
255-
else {
256-
watchFailedLookupLocationOfResolution(resolution, 0);
257-
}
258-
}
259-
else if (existingResolution) {
261+
watchFailedLookupLocationOfResolution(resolution);
262+
if (existingResolution) {
260263
stopWatchFailedLookupLocationOfResolution(existingResolution);
261264
}
265+
262266
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
263267
filesWithChangedSetOfUnresolvedImports.push(path);
264268
// reset log changes to avoid recording the same file multiple times
@@ -390,80 +394,98 @@ namespace ts {
390394
return fileExtensionIsOneOf(path, failedLookupDefaultExtensions);
391395
}
392396

393-
function watchAndStopWatchDiffFailedLookupLocations(resolution: ResolutionWithFailedLookupLocations, existingResolution: ResolutionWithFailedLookupLocations) {
394-
const failedLookupLocations = resolution.failedLookupLocations;
395-
const existingFailedLookupLocations = existingResolution.failedLookupLocations;
396-
for (let index = 0; index < failedLookupLocations.length; index++) {
397-
if (index === existingFailedLookupLocations.length) {
398-
// Additional failed lookup locations, watch from this index
399-
watchFailedLookupLocationOfResolution(resolution, index);
400-
return;
401-
}
402-
else if (failedLookupLocations[index] !== existingFailedLookupLocations[index]) {
403-
// Different failed lookup locations,
404-
// Watch new resolution failed lookup locations from this index and
405-
// stop watching existing resolutions from this index
406-
watchFailedLookupLocationOfResolution(resolution, index);
407-
stopWatchFailedLookupLocationOfResolutionFrom(existingResolution, index);
408-
return;
409-
}
397+
function watchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
398+
// No need to set the resolution refCount
399+
if (!resolution.failedLookupLocations || !resolution.failedLookupLocations.length) {
400+
return;
410401
}
411402

412-
// All new failed lookup locations are already watched (and are same),
413-
// Stop watching failed lookup locations of existing resolution after failed lookup locations length
414-
stopWatchFailedLookupLocationOfResolutionFrom(existingResolution, failedLookupLocations.length);
415-
}
403+
if (resolution.refCount !== undefined) {
404+
resolution.refCount++;
405+
return;
406+
}
416407

417-
function watchFailedLookupLocationOfResolution({ failedLookupLocations }: ResolutionWithFailedLookupLocations, startIndex: number) {
418-
for (let i = startIndex; i < failedLookupLocations.length; i++) {
419-
const failedLookupLocation = failedLookupLocations[i];
408+
resolution.refCount = 1;
409+
const { failedLookupLocations } = resolution;
410+
let setAtRoot = false;
411+
for (const failedLookupLocation of failedLookupLocations) {
420412
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
421-
// If the failed lookup location path is not one of the supported extensions,
422-
// store it in the custom path
423-
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
424-
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
425-
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
426-
}
427413
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
428414
if (!ignore) {
429-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
430-
if (dirWatcher) {
431-
dirWatcher.refCount++;
415+
// If the failed lookup location path is not one of the supported extensions,
416+
// store it in the custom path
417+
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
418+
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
419+
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
420+
}
421+
if (dirPath === rootPath) {
422+
setAtRoot = true;
432423
}
433424
else {
434-
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
425+
setDirectoryWatcher(dir, dirPath);
435426
}
436427
}
437428
}
429+
430+
if (setAtRoot) {
431+
setDirectoryWatcher(rootDir, rootPath);
432+
}
438433
}
439434

440-
function stopWatchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
441-
if (resolution.failedLookupLocations) {
442-
stopWatchFailedLookupLocationOfResolutionFrom(resolution, 0);
435+
function setDirectoryWatcher(dir: string, dirPath: Path) {
436+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
437+
if (dirWatcher) {
438+
dirWatcher.refCount++;
439+
}
440+
else {
441+
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
443442
}
444443
}
445444

446-
function stopWatchFailedLookupLocationOfResolutionFrom({ failedLookupLocations }: ResolutionWithFailedLookupLocations, startIndex: number) {
447-
for (let i = startIndex; i < failedLookupLocations.length; i++) {
448-
const failedLookupLocation = failedLookupLocations[i];
445+
function stopWatchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
446+
if (!resolution.failedLookupLocations || !resolution.failedLookupLocations.length) {
447+
return;
448+
}
449+
450+
resolution.refCount!--;
451+
if (resolution.refCount) {
452+
return;
453+
}
454+
455+
const { failedLookupLocations } = resolution;
456+
let removeAtRoot = false;
457+
for (const failedLookupLocation of failedLookupLocations) {
449458
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
450-
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
451-
if (refCount) {
452-
if (refCount === 1) {
453-
customFailedLookupPaths.delete(failedLookupLocationPath);
459+
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
460+
if (!ignore) {
461+
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
462+
if (refCount) {
463+
if (refCount === 1) {
464+
customFailedLookupPaths.delete(failedLookupLocationPath);
465+
}
466+
else {
467+
Debug.assert(refCount > 1);
468+
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
469+
}
470+
}
471+
472+
if (dirPath === rootPath) {
473+
removeAtRoot = true;
454474
}
455475
else {
456-
Debug.assert(refCount > 1);
457-
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
476+
removeDirectoryWatcher(dirPath);
458477
}
459478
}
460-
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
461-
if (!ignore) {
462-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
463-
// Do not close the watcher yet since it might be needed by other failed lookup locations.
464-
dirWatcher.refCount--;
465-
}
466479
}
480+
if (removeAtRoot) {
481+
removeDirectoryWatcher(rootPath);
482+
}
483+
}
484+
485+
function removeDirectoryWatcher(dirPath: string) {
486+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
487+
// Do not close the watcher yet since it might be needed by other failed lookup locations.
488+
dirWatcher.refCount--;
467489
}
468490

469491
function createDirectoryWatcher(directory: string, dirPath: Path) {

0 commit comments

Comments
 (0)