Update cached import list update as part of resolving modules instead of another pass

This commit is contained in:
Sheetal Nandi
2023-10-20 13:10:34 -07:00
parent 163778335c
commit dad8851564
3 changed files with 32 additions and 50 deletions
+16 -43
View File
@@ -81,6 +81,14 @@ export interface HasInvalidatedFromResolutionCache {
hasInvalidatedResolutions: HasInvalidatedResolutions;
hasInvalidatedLibResolutions: HasInvalidatedLibResolutions;
}
/** @internal */
export type CallbackOnNewResolution<T extends ResolutionWithFailedLookupLocations> = (
existing: T | undefined,
current: T,
path: Path,
name: string,
mode: ResolutionMode,
) => void;
/**
* This is the cache of module/typedirectives resolution that can be retained across program
*
@@ -100,8 +108,6 @@ export interface ResolutionCache {
dirPathToSymlinkPackageRefCount: Map<Path, number>;
countResolutionsResolvedWithGlobalCache(): number;
countResolutionsResolvedWithoutGlobalCache(): number;
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
watchFailedLookupLocationsOfExternalModuleResolutions<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
name: string,
@@ -118,6 +124,7 @@ export interface ResolutionCache {
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
): readonly ResolvedModuleWithFailedLookupLocations[];
resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
typeDirectiveReferences: readonly T[],
@@ -533,7 +540,8 @@ export function needsResolutionFromGlobalCache(moduleName: string, resolution: R
return !isExternalModuleNameRelative(moduleName) && isUnresolvedOrResolvedToJs(resolution);
}
function isUnresolvedOrResolvedToJs(resolution: ResolvedModuleWithFailedLookupLocations) {
/** @internal */
export function isUnresolvedOrResolvedToJs(resolution: ResolvedModuleWithFailedLookupLocations): boolean {
return !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension);
}
@@ -592,7 +600,6 @@ export function createResolutionCache(
resolutionHost: ResolutionCacheHost,
rootDirForResolution: string,
): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Set<Path> | undefined;
const nonRelativeExternalModuleResolutions = new Set<ResolutionWithFailedLookupLocations>();
@@ -675,8 +682,6 @@ export function createResolutionCache(
countResolutionsResolvedWithoutGlobalCache: () => resolutionsResolvedWithoutGlobalCache,
watchFailedLookupLocationsOfExternalModuleResolutions,
getModuleResolutionCache: () => moduleResolutionCache,
startRecordingFilesWithChangedResolutions,
finishRecordingFilesWithChangedResolutions,
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
startCachingPerDirectoryResolution,
@@ -742,16 +747,6 @@ export function createResolutionCache(
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
}
function startRecordingFilesWithChangedResolutions() {
filesWithChangedSetOfUnresolvedImports = [];
}
function finishRecordingFilesWithChangedResolutions() {
const collected = filesWithChangedSetOfUnresolvedImports;
filesWithChangedSetOfUnresolvedImports = undefined;
return collected;
}
function createHasInvalidatedResolutions(
customHasInvalidatedResolutions: HasInvalidatedResolutions,
customHasInvalidatedLibResolutions: HasInvalidatedLibResolutions,
@@ -880,8 +875,8 @@ export function createResolutionCache(
perFileCache: Map<Path, ModeAwareCache<T>>;
loader: ResolutionLoader<Entry, T, SourceFile>;
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
logChanges?: boolean;
deferWatchingNonRelativeResolution: boolean;
onNewResolution?: CallbackOnNewResolution<T>;
}
function resolveNamesWithLocalCache<Entry, SourceFile, T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
entries,
@@ -894,7 +889,7 @@ export function createResolutionCache(
loader,
getResolutionWithResolvedFileName,
deferWatchingNonRelativeResolution,
logChanges,
onNewResolution,
}: ResolveNamesWithLocalCacheInput<Entry, SourceFile, T, R>): readonly T[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = perFileCache.get(path) || perFileCache.set(path, createModeAwareCache()).get(path)!;
@@ -935,12 +930,7 @@ export function createResolutionCache(
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolutionWithResolvedFileName);
}
}
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
filesWithChangedSetOfUnresolvedImports.push(path);
// reset log changes to avoid recording the same file multiple times
logChanges = false;
}
onNewResolution?.(existingResolution, resolution, path, name, mode);
}
else {
const host = getModuleResolutionHost(resolutionHost);
@@ -987,24 +977,6 @@ export function createResolutionCache(
});
}
return resolvedModules;
function resolutionIsEqualTo(oldResolution: T | undefined, newResolution: T | undefined): boolean {
if (oldResolution === newResolution) {
return true;
}
if (!oldResolution || !newResolution) {
return false;
}
const oldResult = getResolutionWithResolvedFileName(oldResolution);
const newResult = getResolutionWithResolvedFileName(newResolution);
if (oldResult === newResult) {
return true;
}
if (!oldResult || !newResult) {
return false;
}
return oldResult.resolvedFileName === newResult.resolvedFileName;
}
}
function resolveTypeReferenceDirectiveReferences<T extends FileReference | string>(
@@ -1042,6 +1014,7 @@ export function createResolutionCache(
options: CompilerOptions,
containingSourceFile: SourceFile,
reusedNames: readonly StringLiteralLike[] | undefined,
onNewResolution?: CallbackOnNewResolution<ResolvedModuleWithFailedLookupLocations>,
): readonly ResolvedModuleWithFailedLookupLocations[] {
return resolveNamesWithLocalCache({
entries: moduleLiterals,
@@ -1059,8 +1032,8 @@ export function createResolutionCache(
moduleResolutionCache,
),
getResolutionWithResolvedFileName: getResolvedModuleFromResolution,
logChanges: !!resolutionHost.getGlobalTypingsCacheLocation,
deferWatchingNonRelativeResolution: true, // Defer non relative resolution watch because we could be using ambient modules
onNewResolution,
});
}
+15 -7
View File
@@ -76,7 +76,9 @@ import {
InstallPackageOptions,
IScriptSnapshot,
isDeclarationFileName,
isExternalModuleNameRelative,
isInsideNodeModules,
isUnresolvedOrResolvedToJs,
JSDocParsingMode,
JsTyping,
LanguageService,
@@ -390,6 +392,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
* @internal
*/
cachedUnresolvedImportsPerFile: Map<Path, readonly string[]> = new Map();
private recordChangesToUnresolvedImports = false;
/** @internal */
lastCachedUnresolvedImportsList: SortedReadonlyArray<string> | undefined;
@@ -796,6 +799,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
/** @internal */
resolveModuleNameLiterals(moduleLiterals: readonly StringLiteralLike[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile: SourceFile, reusedNames: readonly StringLiteralLike[] | undefined): readonly ResolvedModuleWithFailedLookupLocations[] {
let needsUpdate = this.recordChangesToUnresolvedImports && this.cachedUnresolvedImportsPerFile.has(this.toPath(containingFile));
return this.resolutionCache.resolveModuleNameLiterals(
moduleLiterals,
containingFile,
@@ -803,6 +807,14 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
options,
containingSourceFile,
reusedNames,
needsUpdate ? (existing, current, path, name) => {
if (!needsUpdate || isExternalModuleNameRelative(name)) return;
// If only unresolved flag is changed, update
if ((existing && isUnresolvedOrResolvedToJs(existing)) === isUnresolvedOrResolvedToJs(current)) return;
needsUpdate = false;
this.cachedUnresolvedImportsPerFile.delete(path);
this.lastCachedUnresolvedImportsList = undefined;
} : undefined,
);
}
@@ -1448,26 +1460,22 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
const useTypingsFromGlobalCache = this.useTypingsFromGlobalCache();
if (!useTypingsFromGlobalCache) this.resolutionCache.invalidateResolutionsWithGlobalCachePass();
else this.resolutionCache.invalidateResolutionsWithoutGlobalCachePass();
if (useTypingsFromGlobalCache && this.cachedUnresolvedImportsPerFile.size) this.resolutionCache.startRecordingFilesWithChangedResolutions();
if (useTypingsFromGlobalCache && this.cachedUnresolvedImportsPerFile.size) this.recordChangesToUnresolvedImports = true;
const hasNewProgram = this.updateGraphWorker();
const hasAddedorRemovedFiles = this.hasAddedorRemovedFiles;
this.hasAddedorRemovedFiles = false;
this.hasAddedOrRemovedSymlinks = false;
this.recordChangesToUnresolvedImports = false;
if (useTypingsFromGlobalCache) {
const changedFiles: readonly Path[] = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray;
for (const file of changedFiles) {
// delete cached information for changed files
this.cachedUnresolvedImportsPerFile.delete(file);
}
// 1. no changes in structure, no changes in unresolved imports - do nothing
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
// (can reuse cached imports for files that were not changed)
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
// (can reuse cached imports for files that were not changed)
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
if (!this.lastCachedUnresolvedImportsList || hasNewProgram || changedFiles.length) {
if (!this.lastCachedUnresolvedImportsList || hasNewProgram) {
this.lastCachedUnresolvedImportsList = getUnresolvedImports(
this.program!,
this.cachedUnresolvedImportsPerFile,
+1
View File
@@ -2862,6 +2862,7 @@ declare namespace ts {
private externalFiles;
private missingFilesMap;
private generatedFilesMap;
private recordChangesToUnresolvedImports;
private hasAddedorRemovedFiles;
private hasAddedOrRemovedSymlinks;
protected languageService: LanguageService;