Skip to content

Commit d0b3ac3

Browse files
authored
Better reuse of package.json cache, module resolution cache, and package.json auto import filter (microsoft#47388)
* Use package.json cache in module specifier generation * Let AutoImportProviderProject reuse module resolution cache of host project * Add missing module resolution cache access, add logging to getRootFileNames * Reuse packageJsonImportFilter * Only log when the project will be created, update API baseline * Remove override that could mess up watches
1 parent 8153475 commit d0b3ac3

File tree

10 files changed

+38
-15
lines changed

10 files changed

+38
-15
lines changed

src/compiler/checker.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4722,6 +4722,7 @@ namespace ts {
47224722
getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "",
47234723
getCurrentDirectory: () => host.getCurrentDirectory(),
47244724
getSymlinkCache: maybeBind(host, host.getSymlinkCache),
4725+
getPackageJsonInfoCache: () => host.getPackageJsonInfoCache?.(),
47254726
useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames),
47264727
redirectTargetsMap: host.redirectTargetsMap,
47274728
getProjectReferenceRedirect: fileName => host.getProjectReferenceRedirect(fileName),

src/compiler/moduleSpecifiers.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace ts.moduleSpecifiers {
4545
&& getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
4646
return false;
4747
}
48-
return getImpliedNodeFormatForFile(importingSourceFileName, /*packageJsonInfoCache*/ undefined, getModuleResolutionHost(host), compilerOptions) !== ModuleKind.CommonJS;
48+
return getImpliedNodeFormatForFile(importingSourceFileName, host.getPackageJsonInfoCache?.(), getModuleResolutionHost(host), compilerOptions) !== ModuleKind.CommonJS;
4949
}
5050

5151
function getModuleResolutionHost(host: ModuleSpecifierResolutionHost): ModuleResolutionHost {
@@ -715,8 +715,9 @@ namespace ts.moduleSpecifiers {
715715
const packageRootPath = path.substring(0, packageRootIndex);
716716
const packageJsonPath = combinePaths(packageRootPath, "package.json");
717717
let moduleFileToTry = path;
718-
if (host.fileExists(packageJsonPath)) {
719-
const packageJsonContent = JSON.parse(host.readFile!(packageJsonPath)!);
718+
const cachedPackageJson = host.getPackageJsonInfoCache?.()?.getPackageJsonInfo(packageJsonPath);
719+
if (typeof cachedPackageJson === "object" || cachedPackageJson === undefined && host.fileExists(packageJsonPath)) {
720+
const packageJsonContent = cachedPackageJson?.packageJsonContent || JSON.parse(host.readFile!(packageJsonPath)!);
720721
if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext) {
721722
// `conditions` *could* be made to go against `importingSourceFile.impliedNodeFormat` if something wanted to generate
722723
// an ImportEqualsDeclaration in an ESM-implied file or an ImportCall in a CJS-implied file. But since this function is

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8345,6 +8345,7 @@ namespace ts {
83458345
realpath?(path: string): string;
83468346
getSymlinkCache?(): SymlinkCache;
83478347
getModuleSpecifierCache?(): ModuleSpecifierCache;
8348+
getPackageJsonInfoCache?(): PackageJsonInfoCache | undefined;
83488349
getGlobalTypingsCacheLocation?(): string | undefined;
83498350
getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined;
83508351

src/server/project.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,10 @@ namespace ts.server {
476476
return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, containingSourceFile);
477477
}
478478

479+
getModuleResolutionCache(): ModuleResolutionCache | undefined {
480+
return this.resolutionCache.getModuleResolutionCache();
481+
}
482+
479483
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined {
480484
return this.resolutionCache.getResolvedModuleWithFailedLookupLocationsFromCache(moduleName, containingFile, resolutionMode);
481485
}
@@ -1915,6 +1919,7 @@ namespace ts.server {
19151919
return ts.emptyArray;
19161920
}
19171921

1922+
const start = timestamp();
19181923
let dependencyNames: Set<string> | undefined;
19191924
let rootNames: string[] | undefined;
19201925
const rootFileName = combinePaths(hostProject.currentDirectory, inferredTypesContainingFile);
@@ -1924,13 +1929,13 @@ namespace ts.server {
19241929
packageJson.peerDependencies?.forEach((_, dependencyName) => addDependency(dependencyName));
19251930
}
19261931

1932+
let dependenciesAdded = 0;
19271933
if (dependencyNames) {
1928-
let dependenciesAdded = 0;
19291934
const symlinkCache = hostProject.getSymlinkCache();
19301935
for (const name of arrayFrom(dependencyNames.keys())) {
19311936
// Avoid creating a large project that would significantly slow down time to editor interactivity
19321937
if (dependencySelection === PackageJsonAutoImportPreference.Auto && dependenciesAdded > this.maxDependencies) {
1933-
hostProject.log(`Auto-import provider attempted to add more than ${this.maxDependencies} dependencies.`);
1938+
hostProject.log(`AutoImportProviderProject: attempted to add more than ${this.maxDependencies} dependencies. Aborting.`);
19341939
return ts.emptyArray;
19351940
}
19361941

@@ -1978,6 +1983,9 @@ namespace ts.server {
19781983
}
19791984
}
19801985

1986+
if (rootNames?.length) {
1987+
hostProject.log(`AutoImportProviderProject: found ${rootNames.length} root files in ${dependenciesAdded} dependencies in ${timestamp() - start} ms`);
1988+
}
19811989
return rootNames || ts.emptyArray;
19821990

19831991
function addDependency(dependency: string) {
@@ -2146,6 +2154,11 @@ namespace ts.server {
21462154
getSymlinkCache() {
21472155
return this.hostProject.getSymlinkCache();
21482156
}
2157+
2158+
/*@internal*/
2159+
getModuleResolutionCache() {
2160+
return this.hostProject.getCurrentProgram()?.getModuleResolutionCache();
2161+
}
21492162
}
21502163

21512164
/**

src/services/codefixes/importFixes.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ namespace ts.codefix {
303303

304304
function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], moduleSymbol: Symbol, symbolName: string, program: Program, position: number | undefined, isValidTypeOnlyUseSite: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) {
305305
Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol || info.symbol.parent === moduleSymbol), "Some exportInfo should match the specified moduleSymbol");
306-
return getBestFix(getImportFixes(exportInfos, symbolName, position, isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences), sourceFile, program, host, preferences);
306+
const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host);
307+
return getBestFix(getImportFixes(exportInfos, symbolName, position, isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences), sourceFile, program, packageJsonImportFilter);
307308
}
308309

309310
function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction {
@@ -369,6 +370,7 @@ namespace ts.codefix {
369370
program: Program,
370371
host: LanguageServiceHost,
371372
preferences: UserPreferences,
373+
packageJsonImportFilter?: PackageJsonImportFilter,
372374
fromCacheOnly?: boolean,
373375
): { exportInfo?: SymbolExportInfo, moduleSpecifier: string, computedWithoutCacheCount: number } | undefined {
374376
const { fixes, computedWithoutCacheCount } = getNewImportFixes(
@@ -381,7 +383,7 @@ namespace ts.codefix {
381383
host,
382384
preferences,
383385
fromCacheOnly);
384-
const result = getBestFix(fixes, importingFile, program, host, preferences);
386+
const result = getBestFix(fixes, importingFile, program, packageJsonImportFilter || createPackageJsonImportFilter(importingFile, preferences, host));
385387
return result && { ...result, computedWithoutCacheCount };
386388
}
387389

@@ -652,24 +654,23 @@ namespace ts.codefix {
652654
const info = errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
653655
? getFixesInfoForUMDImport(context, symbolToken)
654656
: isIdentifier(symbolToken) ? getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider) : undefined;
655-
return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.program, context.host, context.preferences) };
657+
const packageJsonImportFilter = createPackageJsonImportFilter(context.sourceFile, context.preferences, context.host);
658+
return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.program, packageJsonImportFilter) };
656659
}
657660

658-
function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): readonly ImportFix[] {
659-
const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host);
660-
return sort(fixes, (a, b) => compareValues(a.kind, b.kind) || compareModuleSpecifiers(a, b, sourceFile, program, allowsImportingSpecifier));
661+
function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter): readonly ImportFix[] {
662+
return sort(fixes, (a, b) => compareValues(a.kind, b.kind) || compareModuleSpecifiers(a, b, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier));
661663
}
662664

663-
function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): T | undefined {
665+
function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter): T | undefined {
664666
if (!some(fixes)) return;
665667
// These will always be placed first if available, and are better than other kinds
666668
if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) {
667669
return fixes[0];
668670
}
669-
const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host);
670671
return fixes.reduce((best, fix) =>
671672
// Takes true branch of conditional if `fix` is better than `best`
672-
compareModuleSpecifiers(fix, best, sourceFile, program, allowsImportingSpecifier) === Comparison.LessThan ? fix : best
673+
compareModuleSpecifiers(fix, best, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier) === Comparison.LessThan ? fix : best
673674
);
674675
}
675676

src/services/completions.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ namespace ts.Completions {
177177
cb: (context: ModuleSpecifierResolutioContext) => TReturn,
178178
): TReturn {
179179
const start = timestamp();
180+
const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host);
180181
let resolutionLimitExceeded = false;
181182
let ambientCount = 0;
182183
let resolvedCount = 0;
@@ -202,7 +203,7 @@ namespace ts.Completions {
202203
const shouldResolveModuleSpecifier = isForImportStatementCompletion || preferences.allowIncompleteCompletions && resolvedCount < moduleSpecifierResolutionLimit;
203204
const shouldGetModuleSpecifierFromCache = !shouldResolveModuleSpecifier && preferences.allowIncompleteCompletions && cacheAttemptCount < moduleSpecifierResolutionCacheAttemptLimit;
204205
const result = (shouldResolveModuleSpecifier || shouldGetModuleSpecifierFromCache)
205-
? codefix.getModuleSpecifierForBestExportInfo(exportInfo, sourceFile, program, host, preferences, shouldGetModuleSpecifierFromCache)
206+
? codefix.getModuleSpecifierForBestExportInfo(exportInfo, sourceFile, program, host, preferences, packageJsonImportFilter, shouldGetModuleSpecifierFromCache)
206207
: undefined;
207208

208209
if (!shouldResolveModuleSpecifier && !shouldGetModuleSpecifierFromCache || shouldGetModuleSpecifierFromCache && !result) {

src/services/services.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,7 @@ namespace ts {
13761376
hasChangedAutomaticTypeDirectiveNames,
13771377
trace: parseConfigHost.trace,
13781378
resolveModuleNames: maybeBind(host, host.resolveModuleNames),
1379+
getModuleResolutionCache: maybeBind(host, host.getModuleResolutionCache),
13791380
resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives),
13801381
useSourceOfProjectReferenceRedirect: maybeBind(host, host.useSourceOfProjectReferenceRedirect),
13811382
getParsedCommandLine,

src/services/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ namespace ts {
284284
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames;
285285
/* @internal */ getGlobalTypingsCacheLocation?(): string | undefined;
286286
/* @internal */ getSymlinkCache?(files?: readonly SourceFile[]): SymlinkCache;
287+
/* Lets the Program from a AutoImportProviderProject use its host project's ModuleResolutionCache */
288+
/* @internal */ getModuleResolutionCache?(): ModuleResolutionCache | undefined;
287289

288290
/*
289291
* Required for full import and type reference completions.

src/services/utilities.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,7 @@ namespace ts {
19071907
useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames),
19081908
getSymlinkCache: maybeBind(host, host.getSymlinkCache) || program.getSymlinkCache,
19091909
getModuleSpecifierCache: maybeBind(host, host.getModuleSpecifierCache),
1910+
getPackageJsonInfoCache: () => program.getModuleResolutionCache()?.getPackageJsonInfoCache(),
19101911
getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation),
19111912
redirectTargetsMap: program.redirectTargetsMap,
19121913
getProjectReferenceRedirect: fileName => program.getProjectReferenceRedirect(fileName),

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

+1
Original file line numberDiff line numberDiff line change
@@ -9874,6 +9874,7 @@ declare namespace ts.server {
98749874
writeFile(fileName: string, content: string): void;
98759875
fileExists(file: string): boolean;
98769876
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference, _options?: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[];
9877+
getModuleResolutionCache(): ModuleResolutionCache | undefined;
98779878
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
98789879
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
98799880
directoryExists(path: string): boolean;

0 commit comments

Comments
 (0)