Skip to content

Commit 7bb44e7

Browse files
committed
Cache packagejson scopes per directory
1 parent b2159e4 commit 7bb44e7

File tree

46 files changed

+5120
-11443
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+5120
-11443
lines changed

src/compiler/builder.ts

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
getOriginalOrResolvedModuleFileName,
5656
getOriginalOrResolvedTypeReferenceFileName,
5757
getOwnKeys,
58+
getPackageJsonLocationFromScope,
5859
getRelativePathFromDirectory,
5960
getTsBuildInfoEmitOutputFilePath,
6061
handleNoEmitOptions,
@@ -66,7 +67,6 @@ import {
6667
isJsonSourceFile,
6768
isNumber,
6869
isString,
69-
last,
7070
map,
7171
mapDefined,
7272
maybeBind,
@@ -80,6 +80,7 @@ import {
8080
outFile,
8181
PackageJsonInfo,
8282
PackageJsonInfoCache,
83+
PackageJsonScope,
8384
Path,
8485
PerDirectoryAndNonRelativeNameCache,
8586
PerNonRelativeNameCache,
@@ -187,7 +188,7 @@ export interface ReusableBuilderProgramState extends BuilderState {
187188
modules: PerDirectoryAndNonRelativeNameCache<ResolvedModuleWithFailedLookupLocations> | undefined;
188189
typeRefs: PerDirectoryAndNonRelativeNameCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations> | undefined;
189190
packageJsons: Map<Path, string> | undefined;
190-
nonRelativePackageJsonsCache: PerNonRelativeNameCache<string> | undefined;
191+
packageJsonScopes: PerNonRelativeNameCache<PackageJsonScope> | undefined;
191192
packageJsonCache: PackageJsonInfoCache | undefined;
192193
};
193194
resuableCacheResolutions?: {
@@ -1472,20 +1473,19 @@ function getCacheResolutions(state: BuilderProgramState) {
14721473
let modules: PerDirectoryAndNonRelativeNameCache<ResolvedModuleWithFailedLookupLocations> | undefined;
14731474
let typeRefs: PerDirectoryAndNonRelativeNameCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations> | undefined;
14741475
let packageJsons: Map<Path, string> | undefined;
1475-
let nonRelativePackageJsonsCache: PerNonRelativeNameCache<string> | undefined;
1476+
let packageJsonScopes: PerNonRelativeNameCache<PackageJsonScope> | undefined;
14761477
for (const f of state.program!.getSourceFiles()) {
14771478
modules = toPerDirectoryAndNonRelativeNameCache(state, modules, getOriginalOrResolvedModuleFileName, f.resolvedModules, f);
14781479
typeRefs = toPerDirectoryAndNonRelativeNameCache(state, typeRefs, getOriginalOrResolvedTypeReferenceFileName, f.resolvedTypeReferenceDirectiveNames, f);
1479-
if (f.packageJsonScope) {
1480+
if (f.packageJsonScope?.info) {
14801481
const dirPath = getDirectoryPath(f.resolvedPath);
1481-
if (!nonRelativePackageJsonsCache?.getWithPath(dirPath)) {
1482-
const result = last(f.packageJsonLocations!);
1483-
(packageJsons ??= new Map()).set(dirPath, result);
1484-
(nonRelativePackageJsonsCache ??= createPerNonRelativeNameCache(
1482+
if (!packageJsonScopes?.getWithPath(dirPath)) {
1483+
(packageJsons ??= new Map()).set(dirPath, getPackageJsonLocationFromScope(f.packageJsonScope));
1484+
(packageJsonScopes ??= createPerNonRelativeNameCache(
14851485
state.program!.getCurrentDirectory(),
14861486
state.program!.getCanonicalFileName,
1487-
identity,
1488-
)).setWithPath(dirPath, result, ancestorPath => packageJsons!.delete(ancestorPath));
1487+
getPackageJsonLocationFromScope,
1488+
)).setWithPath(dirPath, f.packageJsonScope, ancestorPath => packageJsons!.delete(ancestorPath));
14891489
}
14901490
}
14911491
}
@@ -1498,7 +1498,7 @@ function getCacheResolutions(state: BuilderProgramState) {
14981498
modules,
14991499
typeRefs,
15001500
packageJsons,
1501-
nonRelativePackageJsonsCache,
1501+
packageJsonScopes,
15021502
packageJsonCache: state.program!.getModuleResolutionCache()?.getPackageJsonInfoCache().clone(),
15031503
};
15041504
}
@@ -2158,6 +2158,7 @@ export function createOldBuildInfoProgram(
21582158
if (!cacheResolutions && !resuableCacheResolutions) return undefined;
21592159
const fileExistsMap = new Map<string, boolean>();
21602160
const affectingLoationsSameMap = new Map<string, boolean>();
2161+
const packageJsonInfoMap = new Map<string, PackageJsonInfo | false>();
21612162

21622163
type Resolution = ResolvedModuleWithFailedLookupLocations & ResolvedTypeReferenceDirectiveWithFailedLookupLocations;
21632164
type ResolutionEntry = [name: string, resolutionId: ProgramBuildInfoResolutionId, mode: ResolutionMode];
@@ -2168,6 +2169,7 @@ export function createOldBuildInfoProgram(
21682169
const reusableResolvedModules = intializeReusableResolutionsCache(resuableCacheResolutions?.cache.modules);
21692170
const reusableResolvedTypeRefs = intializeReusableResolutionsCache(resuableCacheResolutions?.cache.typeRefs);
21702171
let decodedPackageJsons: PerNonRelativeNameCache<string> | undefined;
2172+
let packageJsonScopes: Map<string, PackageJsonScope | false> | undefined;
21712173
let decodedHashes: Map<ProgramBuildInfoAbsoluteFileId, string | undefined> | undefined;
21722174
let resolutions: (Resolution | false)[] | undefined;
21732175
let originalPathOrResolvedFileNames: string[] | undefined;
@@ -2194,7 +2196,7 @@ export function createOldBuildInfoProgram(
21942196
dirPath,
21952197
redirectedReference,
21962198
),
2197-
getPackageJsonPath,
2199+
getPackageJsonScope,
21982200
};
21992201

22002202
function intializeReusableResolutionsCache(reusable: ProgramBuildInfoResolutionCacheWithRedirects | undefined): ReusableResolutionsCache | undefined {
@@ -2226,10 +2228,28 @@ export function createOldBuildInfoProgram(
22262228
return result;
22272229
}
22282230

2229-
function getPackageJsonPath(dir: string) {
2230-
const fromCache = cacheResolutions?.nonRelativePackageJsonsCache?.get(dir);
2231+
function getPackageJsonInfo(fileName: string) {
2232+
let result = packageJsonInfoMap.get(fileName);
2233+
if (result === undefined) packageJsonInfoMap.set(fileName, result = host.getPackageJsonInfo(fileName) || false);
2234+
return result || undefined;
2235+
}
2236+
2237+
function getPackageJsonScope(dir: string): PackageJsonScope | undefined{
2238+
const fromCache = cacheResolutions?.packageJsonScopes?.get(dir);
22312239
if (fromCache) {
2232-
return fileExists(fromCache) ? fromCache : undefined;
2240+
const packageJson = getPackageJsonLocationFromScope(fromCache)!;
2241+
let result = packageJsonScopes?.get(packageJson);
2242+
if (result === undefined) {
2243+
(packageJsonScopes ??= new Map()).set(
2244+
packageJson,
2245+
result = affectingLocationsSame(packageJson, fromCache.info) ?
2246+
fromCache :
2247+
fileExists(packageJson) ?
2248+
{ info: getPackageJsonInfo(packageJson), affectingLocations: [packageJson] } :
2249+
false
2250+
);
2251+
}
2252+
return result || undefined;
22332253
}
22342254
if (!resuableCacheResolutions?.cache.packageJsons) return;
22352255
if (!decodedPackageJsons) {
@@ -2252,8 +2272,22 @@ export function createOldBuildInfoProgram(
22522272
decodedPackageJsons.setWithPath(dirPath, packageJson, noop);
22532273
}
22542274
}
2255-
const fromDecoded = decodedPackageJsons.get(dir);
2256-
return fromDecoded && fileExists(fromDecoded) ? fromDecoded : undefined;
2275+
return toPackageJsonScope(decodedPackageJsons.get(dir));
2276+
}
2277+
2278+
function toPackageJsonScope(file: string | undefined): PackageJsonScope | undefined {
2279+
if (!file) return undefined;
2280+
let result = packageJsonScopes?.get(file);
2281+
if (result !== undefined) return result || undefined;
2282+
(packageJsonScopes ??= new Map());
2283+
if (fileExists(file)) {
2284+
result = {
2285+
info: getPackageJsonInfo(file),
2286+
affectingLocations: [file]
2287+
};
2288+
}
2289+
packageJsonScopes.set(file, result || false);
2290+
return result;
22572291
}
22582292

22592293
function getResolvedFromCache<T extends ResolvedModuleWithFailedLookupLocations | ResolvedTypeReferenceDirectiveWithFailedLookupLocations>(

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4728,7 +4728,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
47284728
let diagnosticDetails;
47294729
const ext = tryGetExtensionFromPath(currentSourceFile.fileName);
47304730
if (ext === Extension.Ts || ext === Extension.Js || ext === Extension.Tsx || ext === Extension.Jsx) {
4731-
const scope = currentSourceFile.packageJsonScope;
4731+
const scope = currentSourceFile.packageJsonScope?.info;
47324732
const targetExt = ext === Extension.Ts ? Extension.Mts : ext === Extension.Js ? Extension.Mjs : undefined;
47334733
if (scope && !scope.contents.packageJsonContent.type) {
47344734
if (targetExt) {

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5154,6 +5154,14 @@
51545154
"category": "Message",
51555155
"code": 6261
51565156
},
5157+
"Directory '{0}' resolves to '{1}' scope according to cache.": {
5158+
"category": "Message",
5159+
"code": 6263
5160+
},
5161+
"Directory '{0}' has no containing package.json scope according to cache.": {
5162+
"category": "Message",
5163+
"code": 6264
5164+
},
51575165

51585166
"Directory '{0}' has no containing package.json scope. Imports will not resolve.": {
51595167
"category": "Message",

src/compiler/moduleNameResolver.ts

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,6 @@ export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: M
690690
}
691691

692692
export interface TypeReferenceDirectiveResolutionCache extends PerDirectoryResolutionCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>, NonRelativeNameResolutionCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>, PackageJsonInfoCache {
693-
/** @internal */ clearAllExceptPackageJsonInfoCache(): void;
694693
}
695694

696695
export interface ModeAwareCache<T> {
@@ -744,11 +743,11 @@ export interface PerNonRelativeNameCache<T> {
744743
set(directory: string, result: T): void;
745744
/** @internal */ getWithPath(directory: Path): T | undefined;
746745
/** @internal */ setWithPath(directory: Path, result: T, ancestoryWorker: (directory: Path) => void): void;
746+
/** @internal */ clear(): void;
747747
}
748748

749749
export interface ModuleResolutionCache extends PerDirectoryResolutionCache<ResolvedModuleWithFailedLookupLocations>, NonRelativeModuleNameResolutionCache, PackageJsonInfoCache {
750750
getPackageJsonInfoCache(): PackageJsonInfoCache;
751-
/** @internal */ clearAllExceptPackageJsonInfoCache(): void;
752751
}
753752

754753
/**
@@ -760,12 +759,32 @@ export interface NonRelativeModuleNameResolutionCache extends NonRelativeNameRes
760759
getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ResolutionMode, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
761760
}
762761

762+
/** @internal */
763+
export interface PackageJsonScope {
764+
info: PackageJsonInfo | undefined;
765+
failedLookupLocations?: string[];
766+
affectingLocations?: string[];
767+
}
768+
/** @internal */
769+
export function getPackageJsonLocationFromScope(scope: PackageJsonScope) {
770+
return lastOrUndefined(scope.affectingLocations);
771+
}
772+
773+
/** @internal */
774+
export interface OldPackageJsonInfoCache {
775+
getPackageJsonScope(dir: string): PackageJsonScope | undefined;
776+
}
777+
763778
export interface PackageJsonInfoCache {
764779
/** @internal */ getPackageJsonInfo(packageJsonPath: string): PackageJsonInfo | boolean | undefined;
765780
/** @internal */ setPackageJsonInfo(packageJsonPath: string, info: PackageJsonInfo | boolean): void;
766781
/** @internal */ entries(): [Path, PackageJsonInfo | boolean][];
767782
/** @internal */ getInternalMap(): Map<Path, PackageJsonInfo | boolean> | undefined;
783+
/** @internal */ setInternalMap(value: Map<Path, PackageJsonInfo | boolean> | undefined): void;
768784
/** @internal */ clone(): PackageJsonInfoCache;
785+
/** @internal */ getPackageJsonScope(dir: string): PackageJsonScope | undefined;
786+
/** @internal */ setPackageJsonScope(dir: string, scope: PackageJsonScope): void;
787+
/** @internal */ setOldPackageJsonScopeCache(cache: OldPackageJsonInfoCache | undefined): void;
769788
clear(): void;
770789
}
771790

@@ -886,26 +905,44 @@ function createCacheWithRedirects<K, V>(ownOptions: CompilerOptions | undefined)
886905
}
887906

888907
function createPackageJsonInfoCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, cache?: Map<Path, PackageJsonInfo | boolean>): PackageJsonInfoCache {
889-
return { getPackageJsonInfo, setPackageJsonInfo, clear, entries, getInternalMap, clone };
908+
const packageJsonScopes = createPerNonRelativeNameCache(currentDirectory, getCanonicalFileName, getPackageJsonLocationFromScope);
909+
let oldPackageJsonInfoCache: OldPackageJsonInfoCache | undefined;
910+
return {
911+
getPackageJsonInfo,
912+
setPackageJsonInfo,
913+
clear,
914+
entries,
915+
getInternalMap: () => cache,
916+
setInternalMap: value => cache = value,
917+
clone,
918+
getPackageJsonScope,
919+
setPackageJsonScope,
920+
setOldPackageJsonScopeCache: cache => oldPackageJsonInfoCache = cache,
921+
};
890922
function getPackageJsonInfo(packageJsonPath: string) {
891923
return cache?.get(toPath(packageJsonPath, currentDirectory, getCanonicalFileName));
892924
}
893925
function setPackageJsonInfo(packageJsonPath: string, info: PackageJsonInfo | boolean) {
894926
(cache ||= new Map()).set(toPath(packageJsonPath, currentDirectory, getCanonicalFileName), info);
895927
}
896928
function clear() {
929+
oldPackageJsonInfoCache = undefined;
897930
cache = undefined;
931+
packageJsonScopes.clear();
898932
}
899933
function entries() {
900934
const iter = cache?.entries();
901935
return iter ? arrayFrom(iter) : [];
902936
}
903-
function getInternalMap() {
904-
return cache;
905-
}
906937
function clone() {
907938
return createPackageJsonInfoCache(currentDirectory, getCanonicalFileName, cache && new Map(cache));
908939
}
940+
function getPackageJsonScope(dir: string) {
941+
return packageJsonScopes.get(dir) || oldPackageJsonInfoCache?.getPackageJsonScope(dir);
942+
}
943+
function setPackageJsonScope(dir: string, scope: PackageJsonScope) {
944+
packageJsonScopes.set(dir, scope);
945+
}
909946
}
910947

911948
function getOrCreateCache<K, V>(cacheWithRedirects: CacheWithRedirects<K, V>, redirectedReference: ResolvedProjectReference | undefined, key: K, create: () => V): V {
@@ -1041,7 +1078,11 @@ export function createPerNonRelativeNameCache<T>(
10411078
): PerNonRelativeNameCache<T> {
10421079
const directoryPathMap = new Map<Path, T>();
10431080

1044-
return { get, set, getWithPath, setWithPath };
1081+
return { get, set, getWithPath, setWithPath, clear };
1082+
1083+
function clear() {
1084+
directoryPathMap.clear();
1085+
}
10451086

10461087
function get(directory: string): T | undefined {
10471088
return getWithPath(toPath(directory, currentDirectory, getCanonicalFileName));
@@ -1242,7 +1283,6 @@ export function toPerDirectoryAndNonRelativeNameCache<T, U, V>(
12421283

12431284
interface ModuleOrTypeReferenceResolutionCache<T> extends PerDirectoryResolutionCache<T>, NonRelativeNameResolutionCache<T>, PackageJsonInfoCache {
12441285
getPackageJsonInfoCache(): PackageJsonInfoCache;
1245-
clearAllExceptPackageJsonInfoCache(): void;
12461286
}
12471287
function createModuleOrTypeReferenceResolutionCache<T>(
12481288
currentDirectory: string,
@@ -1261,18 +1301,13 @@ function createModuleOrTypeReferenceResolutionCache<T>(
12611301
clear,
12621302
update,
12631303
getPackageJsonInfoCache: () => packageJsonInfoCache!,
1264-
clearAllExceptPackageJsonInfoCache,
12651304
setOldResolutionCache,
12661305
};
12671306

12681307
function clear() {
1269-
clearAllExceptPackageJsonInfoCache();
1270-
packageJsonInfoCache!.clear();
1271-
}
1272-
1273-
function clearAllExceptPackageJsonInfoCache() {
12741308
perDirectory.clear();
12751309
perNonRelativeName.clear();
1310+
packageJsonInfoCache!.clear();
12761311
}
12771312

12781313
function update(options: CompilerOptions) {
@@ -2189,15 +2224,51 @@ export interface PackageJsonInfoContents {
21892224
/** false: resolved to nothing. undefined: not yet resolved */
21902225
resolvedEntrypoints: string[] | false | undefined;
21912226
}
2227+
/** @internal */
2228+
export function getPackageScope(
2229+
dir: string,
2230+
packageJsonInfoCache: PackageJsonInfoCache | undefined,
2231+
host: ModuleResolutionHost,
2232+
options: CompilerOptions,
2233+
): PackageJsonScope {
2234+
const state = getTemporaryModuleResolutionState(packageJsonInfoCache, host, options);
2235+
const failedLookupLocations: string[] = [];
2236+
const affectingLocations: string[] = [];
2237+
state.failedLookupLocations = failedLookupLocations;
2238+
state.affectingLocations = affectingLocations;
2239+
const result: PackageJsonScope = forEachAncestorDirectory(dir, directory => {
2240+
const fromCache = state.packageJsonInfoCache?.getPackageJsonScope(directory);
2241+
if (fromCache) {
2242+
const { host, traceEnabled } = state;
2243+
if (traceEnabled) {
2244+
trace(
2245+
host,
2246+
fromCache.info ?
2247+
Diagnostics.Directory_0_resolves_to_1_scope_according_to_cache :
2248+
Diagnostics.Directory_0_has_no_containing_package_json_scope_according_to_cache,
2249+
directory,
2250+
getPackageJsonLocationFromScope(fromCache)
2251+
);
2252+
}
2253+
return fromCache;
2254+
}
2255+
2256+
const info = getPackageJsonInfo(directory, /*onlyRecordFailures*/ false, state);
2257+
if (info) return { info };
2258+
}) || { info: undefined };
2259+
result.failedLookupLocations = updateResolutionField(result.failedLookupLocations, !options.cacheResolutions || !result.info ? failedLookupLocations : undefined);
2260+
result.affectingLocations = updateResolutionField(result.affectingLocations, affectingLocations);
2261+
packageJsonInfoCache?.setPackageJsonScope(dir, result);
2262+
return result;
2263+
}
21922264

21932265
/**
21942266
* A function for locating the package.json scope for a given path
21952267
*
21962268
* @internal
21972269
*/
2198-
export function getPackageScopeForPath(fileName: string, state: ModuleResolutionState): PackageJsonInfo | undefined {
2199-
const parts = getPathComponents(fileName);
2200-
parts.pop();
2270+
export function getPackageScopeForPath(dir: string, state: ModuleResolutionState): PackageJsonInfo | undefined {
2271+
const parts = getPathComponents(dir);
22012272
while (parts.length > 0) {
22022273
const pkg = getPackageJsonInfo(getPathFromPathComponents(parts), /*onlyRecordFailures*/ false, state);
22032274
if (pkg) {
@@ -2362,7 +2433,7 @@ function noKeyStartsWithDot(obj: MapLike<unknown>) {
23622433
}
23632434

23642435
function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
2365-
const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.());
2436+
const directoryPath = getNormalizedAbsolutePath(directory, state.host.getCurrentDirectory?.());
23662437
const scope = getPackageScopeForPath(directoryPath, state);
23672438
if (!scope || !scope.contents.packageJsonContent.exports) {
23682439
return undefined;
@@ -2423,7 +2494,7 @@ function loadModuleFromImports(extensions: Extensions, moduleName: string, direc
24232494
}
24242495
return toSearchResult(/*value*/ undefined);
24252496
}
2426-
const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.());
2497+
const directoryPath = getNormalizedAbsolutePath(directory, state.host.getCurrentDirectory?.());
24272498
const scope = getPackageScopeForPath(directoryPath, state);
24282499
if (!scope) {
24292500
if (state.traceEnabled) {

0 commit comments

Comments
 (0)