Skip to content

Commit 0e79849

Browse files
committed
Update cached import list update as part of resolving modules instead of another pass
1 parent 1831d2f commit 0e79849

3 files changed

Lines changed: 32 additions & 50 deletions

File tree

src/compiler/resolutionCache.ts

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ export interface HasInvalidatedFromResolutionCache {
8484
hasInvalidatedResolutions: HasInvalidatedResolutions;
8585
hasInvalidatedLibResolutions: HasInvalidatedLibResolutions;
8686
}
87+
/** @internal */
88+
export type CallbackOnNewResolution<T extends ResolutionWithFailedLookupLocations> = (
89+
existing: T | undefined,
90+
current: T,
91+
path: Path,
92+
name: string,
93+
mode: ResolutionMode,
94+
) => void;
8795
/**
8896
* This is the cache of module/typedirectives resolution that can be retained across program
8997
*
@@ -103,8 +111,6 @@ export interface ResolutionCache {
103111
dirPathToSymlinkPackageRefCount: Map<Path, number>;
104112
countResolutionsResolvedWithGlobalCache(): number;
105113
countResolutionsResolvedWithoutGlobalCache(): number;
106-
startRecordingFilesWithChangedResolutions(): void;
107-
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
108114

109115
watchFailedLookupLocationsOfExternalModuleResolutions<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
110116
name: string,
@@ -121,6 +127,7 @@ export interface ResolutionCache {
121127
options: CompilerOptions,
122128
containingSourceFile: SourceFile,
123129
reusedNames: readonly StringLiteralLike[] | undefined,
130+
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
124131
): readonly ResolvedModuleWithFailedLookupLocations[];
125132
resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
126133
typeDirectiveReferences: readonly T[],
@@ -526,7 +533,8 @@ export function needsResolutionFromGlobalCache(moduleName: string, resolution: R
526533
return !isExternalModuleNameRelative(moduleName) && isUnresolvedOrResolvedToJs(resolution);
527534
}
528535

529-
function isUnresolvedOrResolvedToJs(resolution: ResolvedModuleWithFailedLookupLocations) {
536+
/** @internal */
537+
export function isUnresolvedOrResolvedToJs(resolution: ResolvedModuleWithFailedLookupLocations) {
530538
return !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension);
531539
}
532540

@@ -586,7 +594,6 @@ export function createResolutionCache(
586594
resolutionHost: ResolutionCacheHost,
587595
rootDirForResolution: string,
588596
): ResolutionCache {
589-
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
590597
let filesWithInvalidatedResolutions: Set<Path> | undefined;
591598
const nonRelativeExternalModuleResolutions = createMultiMap<string, ResolutionWithFailedLookupLocations>();
592599

@@ -668,8 +675,6 @@ export function createResolutionCache(
668675
countResolutionsResolvedWithoutGlobalCache: () => resolutionsResolvedWithoutGlobalCache,
669676
watchFailedLookupLocationsOfExternalModuleResolutions,
670677
getModuleResolutionCache: () => moduleResolutionCache,
671-
startRecordingFilesWithChangedResolutions,
672-
finishRecordingFilesWithChangedResolutions,
673678
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
674679
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
675680
startCachingPerDirectoryResolution,
@@ -735,16 +740,6 @@ export function createResolutionCache(
735740
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
736741
}
737742

738-
function startRecordingFilesWithChangedResolutions() {
739-
filesWithChangedSetOfUnresolvedImports = [];
740-
}
741-
742-
function finishRecordingFilesWithChangedResolutions() {
743-
const collected = filesWithChangedSetOfUnresolvedImports;
744-
filesWithChangedSetOfUnresolvedImports = undefined;
745-
return collected;
746-
}
747-
748743
function createHasInvalidatedResolutions(
749744
customHasInvalidatedResolutions: HasInvalidatedResolutions,
750745
customHasInvalidatedLibResolutions: HasInvalidatedLibResolutions,
@@ -875,8 +870,8 @@ export function createResolutionCache(
875870
perFileCache: Map<Path, ModeAwareCache<T>>;
876871
loader: ResolutionLoader<Entry, T, SourceFile>;
877872
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
878-
logChanges?: boolean;
879873
deferWatchingNonRelativeResolution: boolean;
874+
onNewResolution?: CallbackOnNewResolution<T>;
880875
}
881876
function resolveNamesWithLocalCache<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
882877
entries,
@@ -889,7 +884,7 @@ export function createResolutionCache(
889884
loader,
890885
getResolutionWithResolvedFileName,
891886
deferWatchingNonRelativeResolution,
892-
logChanges,
887+
onNewResolution,
893888
}: ResolveNamesWithLocalCacheInput<Entry, SourceFile, T, R>): readonly T[] {
894889
const path = resolutionHost.toPath(containingFile);
895890
const resolutionsInFile = perFileCache.get(path) || perFileCache.set(path, createModeAwareCache()).get(path)!;
@@ -930,12 +925,7 @@ export function createResolutionCache(
930925
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolutionWithResolvedFileName);
931926
}
932927
}
933-
934-
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
935-
filesWithChangedSetOfUnresolvedImports.push(path);
936-
// reset log changes to avoid recording the same file multiple times
937-
logChanges = false;
938-
}
928+
onNewResolution?.(existingResolution, resolution, path, name, mode);
939929
}
940930
else {
941931
const host = getModuleResolutionHost(resolutionHost);
@@ -982,24 +972,6 @@ export function createResolutionCache(
982972
});
983973
}
984974
return resolvedModules;
985-
986-
function resolutionIsEqualTo(oldResolution: T | undefined, newResolution: T | undefined): boolean {
987-
if (oldResolution === newResolution) {
988-
return true;
989-
}
990-
if (!oldResolution || !newResolution) {
991-
return false;
992-
}
993-
const oldResult = getResolutionWithResolvedFileName(oldResolution);
994-
const newResult = getResolutionWithResolvedFileName(newResolution);
995-
if (oldResult === newResult) {
996-
return true;
997-
}
998-
if (!oldResult || !newResult) {
999-
return false;
1000-
}
1001-
return oldResult.resolvedFileName === newResult.resolvedFileName;
1002-
}
1003975
}
1004976

1005977
function resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
@@ -1037,6 +1009,7 @@ export function createResolutionCache(
10371009
options: CompilerOptions,
10381010
containingSourceFile: SourceFile,
10391011
reusedNames: readonly StringLiteralLike[] | undefined,
1012+
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
10401013
): readonly ResolvedModuleWithFailedLookupLocations[] {
10411014
return resolveNamesWithLocalCache({
10421015
entries: moduleLiterals,
@@ -1054,8 +1027,8 @@ export function createResolutionCache(
10541027
moduleResolutionCache,
10551028
),
10561029
getResolutionWithResolvedFileName: getResolvedModuleFromResolution,
1057-
logChanges: !!resolutionHost.getGlobalCache,
10581030
deferWatchingNonRelativeResolution: true, // Defer non relative resolution watch because we could be using ambient modules
1031+
onNewResolution,
10591032
});
10601033
}
10611034

src/server/project.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ import {
7474
InstallPackageOptions,
7575
IScriptSnapshot,
7676
isDeclarationFileName,
77+
isExternalModuleNameRelative,
7778
isInsideNodeModules,
79+
isUnresolvedOrResolvedToJs,
7880
JSDocParsingMode,
7981
JsTyping,
8082
LanguageService,
@@ -386,6 +388,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
386388
* @internal
387389
*/
388390
cachedUnresolvedImportsPerFile = new Map<Path, readonly string[]>();
391+
private recordChangesToUnresolvedImports = false;
389392

390393
/** @internal */
391394
lastCachedUnresolvedImportsList: SortedReadonlyArray<string> | undefined;
@@ -784,13 +787,22 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
784787

785788
/** @internal */
786789
resolveModuleNameLiterals(moduleLiterals: readonly StringLiteralLike[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile: SourceFile, reusedNames: readonly StringLiteralLike[] | undefined): readonly ResolvedModuleWithFailedLookupLocations[] {
790+
let needsUpdate = this.recordChangesToUnresolvedImports && this.cachedUnresolvedImportsPerFile.has(this.toPath(containingFile));
787791
return this.resolutionCache.resolveModuleNameLiterals(
788792
moduleLiterals,
789793
containingFile,
790794
redirectedReference,
791795
options,
792796
containingSourceFile,
793797
reusedNames,
798+
needsUpdate ? (existing, current, path, name) => {
799+
if (!needsUpdate || isExternalModuleNameRelative(name)) return;
800+
// If only unresolved flag is changed, update
801+
if ((existing && isUnresolvedOrResolvedToJs(existing)) === isUnresolvedOrResolvedToJs(current)) return;
802+
needsUpdate = false;
803+
this.cachedUnresolvedImportsPerFile.delete(path);
804+
this.lastCachedUnresolvedImportsList = undefined;
805+
} : undefined,
794806
);
795807
}
796808

@@ -1430,26 +1442,22 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
14301442
const useTypingsFromGlobalCache = this.useTypingsFromGlobalCache();
14311443
if (!useTypingsFromGlobalCache) this.resolutionCache.invalidateResolutionsWithGlobalCachePass();
14321444
else this.resolutionCache.invalidateResolutionsWithoutGlobalCachePass();
1433-
if (useTypingsFromGlobalCache && this.cachedUnresolvedImportsPerFile.size) this.resolutionCache.startRecordingFilesWithChangedResolutions();
1445+
if (useTypingsFromGlobalCache && this.cachedUnresolvedImportsPerFile.size) this.recordChangesToUnresolvedImports = true;
14341446

14351447
const hasNewProgram = this.updateGraphWorker();
14361448
const hasAddedorRemovedFiles = this.hasAddedorRemovedFiles;
14371449
this.hasAddedorRemovedFiles = false;
14381450
this.hasAddedOrRemovedSymlinks = false;
1451+
this.recordChangesToUnresolvedImports = false;
14391452

14401453
if (useTypingsFromGlobalCache) {
1441-
const changedFiles: readonly Path[] = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray;
1442-
for (const file of changedFiles) {
1443-
// delete cached information for changed files
1444-
this.cachedUnresolvedImportsPerFile.delete(file);
1445-
}
14461454
// 1. no changes in structure, no changes in unresolved imports - do nothing
14471455
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
14481456
// (can reuse cached imports for files that were not changed)
14491457
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
14501458
// (can reuse cached imports for files that were not changed)
14511459
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
1452-
if (!this.lastCachedUnresolvedImportsList || hasNewProgram || changedFiles.length) {
1460+
if (!this.lastCachedUnresolvedImportsList || hasNewProgram) {
14531461
this.lastCachedUnresolvedImportsList = getUnresolvedImports(
14541462
this.program!,
14551463
this.cachedUnresolvedImportsPerFile,

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2804,6 +2804,7 @@ declare namespace ts {
28042804
private externalFiles;
28052805
private missingFilesMap;
28062806
private generatedFilesMap;
2807+
private recordChangesToUnresolvedImports;
28072808
private hasAddedorRemovedFiles;
28082809
private hasAddedOrRemovedSymlinks;
28092810
protected languageService: LanguageService;

0 commit comments

Comments
 (0)