Skip to content

Commit 0633e0e

Browse files
committed
Dont pollute resolution so we can reuse it across projects
1 parent fb623b9 commit 0633e0e

File tree

2 files changed

+90
-97
lines changed

2 files changed

+90
-97
lines changed

src/compiler/resolutionCache.ts

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export interface ResolutionCache extends Required<CompilerHostSupportingResoluti
112112
resolutionsWithFailedLookups: Set<ResolutionWithFailedLookupLocations>;
113113
resolutionsWithOnlyAffectingLocations: Set<ResolutionWithFailedLookupLocations>;
114114
packageJsonRefCount: Map<Path, number>;
115+
watchedResolutionInfoMap: Map<ResolutionWithFailedLookupLocations, WatchedResolutionInfo>;
115116
directoryWatchesOfFailedLookups: Map<Path, DirectoryWatchesOfFailedLookup>;
116117
fileWatchesOfAffectingLocations: Map<string, FileWatcherOfAffectingLocation>;
117118
packageDirWatchers: Map<Path, PackageDirWatcher>;
@@ -190,10 +191,10 @@ export interface RootDirInfo {
190191
}
191192

192193
/** @internal */
193-
export interface WatchedResolutionWithFailedLookupLocations {
194+
export interface WatchedResolutionInfo {
194195
isInvalidated?: boolean;
195196
// Files that have this resolution using
196-
files?: Set<Path>;
197+
files: Set<Path>;
197198
watchedFailed?: number;
198199
watchedAffected?: number;
199200
setAtRoot?: boolean;
@@ -209,15 +210,6 @@ export type ResolutionWithFailedLookupLocations =
209210
& Pick<ResolvedModuleWithFailedLookupLocations, "alternateResult" | "globalCacheResolution">
210211
);
211212

212-
declare module "./types.js" {
213-
/** @internal */
214-
export interface ResolvedModuleWithFailedLookupLocations extends WatchedResolutionWithFailedLookupLocations {
215-
}
216-
/** @internal */
217-
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations extends WatchedResolutionWithFailedLookupLocations {
218-
}
219-
}
220-
221213
/** @internal */
222214
export interface ResolutionCacheHost extends MinimalResolutionCacheHost {
223215
toPath(fileName: string): Path;
@@ -249,6 +241,7 @@ export interface ResolutionCacheHost extends MinimalResolutionCacheHost {
249241
result: ResolvedModuleWithFailedLookupLocations,
250242
data: any,
251243
): any;
244+
getRootDirInfoForResolution?(resolution: ResolutionWithFailedLookupLocations): RootDirInfo;
252245
}
253246

254247
/** @internal */
@@ -649,6 +642,7 @@ export function createResolutionCache(
649642
const resolutionsWithOnlyAffectingLocations = new Set<ResolutionWithFailedLookupLocations>();
650643
const resolvedFileToResolution = new Map<Path, Set<ResolutionWithFailedLookupLocations>>();
651644
const impliedFormatPackageJsons = new Map<Path, readonly string[]>();
645+
const watchedResolutionInfoMap = new Map<ResolutionWithFailedLookupLocations, WatchedResolutionInfo>();
652646

653647
let hasChangedAutomaticTypeDirectiveNames = false;
654648
let affectingPathChecksForFile: Set<string> | undefined;
@@ -664,6 +658,7 @@ export function createResolutionCache(
664658
let potentiallyUnreferencedResolutions: Map<ModuleOrTypeReferenceResolutionCache<ResolutionWithFailedLookupLocations>, Set<ResolutionWithFailedLookupLocations>> | undefined;
665659
let potentiallyUnreferencedDirWatchers: Set<Path> | undefined;
666660
let newUnresolvedResolutionCachePassResolutions: Set<ResolutionWithFailedLookupLocations> | undefined;
661+
let considerNonWatchedResolutionAsInvalidated = false;
667662

668663
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!());
669664
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
@@ -730,6 +725,7 @@ export function createResolutionCache(
730725
resolutionsWithFailedLookups,
731726
resolutionsWithOnlyAffectingLocations,
732727
packageJsonRefCount,
728+
watchedResolutionInfoMap,
733729
directoryWatchesOfFailedLookups,
734730
fileWatchesOfAffectingLocations,
735731
packageDirWatchers,
@@ -816,6 +812,7 @@ export function createResolutionCache(
816812
resolvedFileToResolution.clear();
817813
resolutionsWithFailedLookups.clear();
818814
resolutionsWithOnlyAffectingLocations.clear();
815+
watchedResolutionInfoMap.clear();
819816
resolutionsResolvedWithGlobalCache = 0;
820817
resolutionsResolvedWithoutGlobalCache = 0;
821818
failedLookupChecks = undefined;
@@ -857,7 +854,7 @@ export function createResolutionCache(
857854
!!collected?.has(path),
858855
hasInvalidatedLibResolutions: libFileName =>
859856
customHasInvalidatedLibResolutions(libFileName) ||
860-
!!resolvedLibraries?.get(libFileName)?.isInvalidated,
857+
!!watchedResolutionInfoMap.get(resolvedLibraries?.get(libFileName)!)?.isInvalidated,
861858
};
862859
}
863860

@@ -969,16 +966,13 @@ export function createResolutionCache(
969966
cache: ModuleOrTypeReferenceResolutionCache<ResolutionWithFailedLookupLocations>,
970967
) {
971968
let needsGc = false;
972-
setOfResolutions.forEach(resolution => {
973-
if (resolution.files!.size) return;
974-
needsGc = true;
975-
releaseResolution(resolution);
976-
});
969+
setOfResolutions.forEach(resolution => needsGc = releaseResolution(resolution) || needsGc);
977970
if (needsGc) {
971+
considerNonWatchedResolutionAsInvalidated = true;
978972
// Iterate through maps to remove things that have 0 refCount
979973
cache.directoryToModuleNameMap.forEach((resolutions, dir, redirectsCacheKey, directoryToModuleNameMap) => {
980974
resolutions.forEach((resolution, name, mode, key) => {
981-
if (resolution.files?.size) return;
975+
if (watchedResolutionInfoMap.has(resolution)) return;
982976
resolutions.delete(name, mode);
983977
if (!isExternalModuleNameRelative(name)) {
984978
const moduleNameToDirectoryMap = !redirectsCacheKey ?
@@ -991,6 +985,7 @@ export function createResolutionCache(
991985
});
992986
if (!resolutions.size()) directoryToModuleNameMap.delete(dir);
993987
});
988+
considerNonWatchedResolutionAsInvalidated = false;
994989
}
995990
}
996991

@@ -1044,7 +1039,8 @@ export function createResolutionCache(
10441039

10451040
function isInvalidatedResolution(resolution: ResolutionWithFailedLookupLocations | undefined) {
10461041
return !resolution ||
1047-
resolution.isInvalidated ||
1042+
(considerNonWatchedResolutionAsInvalidated && !watchedResolutionInfoMap.has(resolution)) ||
1043+
watchedResolutionInfoMap.get(resolution)?.isInvalidated ||
10481044
(resolutionsWithGlobalCachePassAreInvalidated && isResolvedWithGlobalCachePass(resolution)) ||
10491045
(resolutionsWithoutGlobalCachePassAreInvalidated && isResolvedWithoutGlobalCachePass(resolution)) ||
10501046
// If this is not a new resolution and its unresolved, its invalid
@@ -1150,7 +1146,7 @@ export function createResolutionCache(
11501146
);
11511147
}
11521148
}
1153-
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
1149+
Debug.assert(resolution !== undefined && !watchedResolutionInfoMap.get(resolution)!.isInvalidated);
11541150
seenNamesInFile.set(name, mode, true);
11551151
resolvedModules.push(resolution);
11561152
}
@@ -1349,7 +1345,7 @@ export function createResolutionCache(
13491345
const path = resolutionHost.toPath(containingFile);
13501346
const resolutionsInFile = resolvedModuleNames.get(path);
13511347
const resolution = resolutionsInFile?.get(moduleName, /*mode*/ undefined);
1352-
if (resolution && !resolution.isInvalidated) return resolution;
1348+
if (resolution && !watchedResolutionInfoMap.get(resolution)!.isInvalidated) return resolution;
13531349
const data = resolutionHost.beforeResolveSingleModuleNameWithoutWatching?.(moduleResolutionCache);
13541350
const host = getModuleResolutionHost(resolutionHost);
13551351
// We are not resolving d.ts so just normal resolution instead of doing resolution pass to global cache
@@ -1374,10 +1370,16 @@ export function createResolutionCache(
13741370
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
13751371
redirectedReference: ResolvedProjectReference | undefined,
13761372
) {
1377-
const firstTime = !resolution.files;
1378-
(resolution.files ??= new Set()).add(filePath);
1379-
watchFailedLookupLocationOfResolution(resolution, redirectedReference);
1380-
watchAffectingLocationsOfResolution(resolution);
1373+
let watchedResolutionInfo = watchedResolutionInfoMap.get(resolution);
1374+
const firstTime = !watchedResolutionInfo;
1375+
if (!watchedResolutionInfo) {
1376+
watchedResolutionInfoMap.set(resolution, watchedResolutionInfo = { files: new Set([filePath]) });
1377+
}
1378+
else {
1379+
watchedResolutionInfo.files.add(filePath);
1380+
}
1381+
watchFailedLookupLocationOfResolution(resolution, redirectedReference, watchedResolutionInfo);
1382+
watchAffectingLocationsOfResolution(resolution, watchedResolutionInfo);
13811383
if (!firstTime) return;
13821384
if (resolution.globalCacheResolution && !resolution.globalCacheResolution.resolution.resolvedModule) {
13831385
// Add to potentially unreferenced resolutions
@@ -1437,61 +1439,66 @@ export function createResolutionCache(
14371439
function watchFailedLookupLocationOfResolution(
14381440
resolution: ResolutionWithFailedLookupLocations,
14391441
redirectedReference: ResolvedProjectReference | undefined,
1442+
watchedResolutionInfo: WatchedResolutionInfo,
14401443
) {
1441-
Debug.assert(!!resolution.files?.size);
1442-
const { failedLookupLocations, alternateResult, watchedFailed } = resolution;
1444+
const { failedLookupLocations, alternateResult } = resolution;
14431445
// There have to be failed lookup locations if there is alternateResult so storing failedLookupLocation length is good enough,
14441446
// alternateResult doesnt change later only failed lookup locations get added on
1445-
if (watchedFailed === failedLookupLocations?.length) return;
1446-
if (!watchedFailed) {
1447+
if (watchedResolutionInfo.watchedFailed === failedLookupLocations?.length) return;
1448+
if (!watchedResolutionInfo.watchedFailed) {
14471449
resolutionsWithFailedLookups.add(resolution);
1448-
if (resolution.watchedAffected) resolutionsWithOnlyAffectingLocations.delete(resolution);
1450+
if (watchedResolutionInfo.watchedAffected) resolutionsWithOnlyAffectingLocations.delete(resolution);
14491451
}
14501452

1451-
let setAtRoot = !!resolution.setAtRoot;
1452-
for (let i = watchedFailed || 0; i < failedLookupLocations!.length; i++) {
1453+
let setAtRoot = !!watchedResolutionInfo.setAtRoot;
1454+
for (let i = watchedResolutionInfo.watchedFailed || 0; i < failedLookupLocations!.length; i++) {
14531455
setAtRoot = watchFailedLookupLocation(
14541456
failedLookupLocations![i],
1455-
getRootDirInfoForResolution(resolution, redirectedReference),
1457+
getRootDirInfoForResolution(resolution, redirectedReference, watchedResolutionInfo),
14561458
) || setAtRoot;
14571459
}
1458-
if (!watchedFailed && alternateResult) {
1460+
if (!watchedResolutionInfo.watchedFailed && alternateResult) {
14591461
setAtRoot = watchFailedLookupLocation(
14601462
alternateResult,
1461-
getRootDirInfoForResolution(resolution, redirectedReference),
1463+
getRootDirInfoForResolution(resolution, redirectedReference, watchedResolutionInfo),
14621464
) || setAtRoot;
14631465
}
1464-
if (!resolution.setAtRoot && setAtRoot) {
1466+
if (!watchedResolutionInfo.setAtRoot && setAtRoot) {
14651467
// This is always non recursive
14661468
setDirectoryWatcher(
1467-
getRootDirInfoForResolution(resolution, redirectedReference).rootDir,
1468-
getRootDirInfoForResolution(resolution, redirectedReference).rootPath,
1469+
getRootDirInfoForResolution(resolution, redirectedReference, watchedResolutionInfo).rootDir,
1470+
getRootDirInfoForResolution(resolution, redirectedReference, watchedResolutionInfo).rootPath,
14691471
/*packageDir*/ undefined,
14701472
/*packageDirPath*/ undefined,
14711473
/*nonRecursive*/ true,
14721474
);
14731475
}
1474-
resolution.watchedFailed = failedLookupLocations?.length;
1475-
resolution.setAtRoot = setAtRoot;
1476+
watchedResolutionInfo.watchedFailed = failedLookupLocations?.length;
1477+
watchedResolutionInfo.setAtRoot = setAtRoot;
14761478
}
14771479

14781480
function getRootDirInfoForResolution(
14791481
resolution: ResolutionWithFailedLookupLocations,
14801482
redirectedReference: ResolvedProjectReference | undefined,
1483+
watchedResolutionInfo: WatchedResolutionInfo,
14811484
) {
1482-
return resolution.rootDirInfo ??= getRootDirInfoOfRedirectedReference(redirectedReference);
1485+
return watchedResolutionInfo.rootDirInfo ??= !resolutionHost.getRootDirInfoForResolution ?
1486+
getRootDirInfoOfRedirectedReference(redirectedReference) :
1487+
resolutionHost.getRootDirInfoForResolution(resolution);
14831488
}
14841489

1485-
function watchAffectingLocationsOfResolution(resolution: ResolutionWithFailedLookupLocations) {
1486-
Debug.assert(!!resolution.files?.size);
1487-
const { affectingLocations, watchedAffected } = resolution;
1488-
if (affectingLocations?.length === watchedAffected) return;
1489-
if (!watchedAffected && !resolution.watchedFailed) resolutionsWithOnlyAffectingLocations.add(resolution);
1490+
function watchAffectingLocationsOfResolution(
1491+
resolution: ResolutionWithFailedLookupLocations,
1492+
watchedResolutionInfo: WatchedResolutionInfo,
1493+
) {
1494+
const { affectingLocations } = resolution;
1495+
if (affectingLocations?.length === watchedResolutionInfo.watchedAffected) return;
1496+
if (!watchedResolutionInfo.watchedAffected && !watchedResolutionInfo.watchedFailed) resolutionsWithOnlyAffectingLocations.add(resolution);
14901497
// Watch package json
1491-
for (let i = watchedAffected || 0; i < affectingLocations!.length; i++) {
1498+
for (let i = watchedResolutionInfo.watchedAffected || 0; i < affectingLocations!.length; i++) {
14921499
createFileWatcherOfAffectingLocation(affectingLocations![i], /*forResolution*/ true);
14931500
}
1494-
resolution.watchedAffected = affectingLocations?.length;
1501+
watchedResolutionInfo.watchedAffected = affectingLocations?.length;
14951502
}
14961503

14971504
function createFileWatcherOfAffectingLocation(affectingLocation: string, forResolution: boolean) {
@@ -1693,18 +1700,18 @@ export function createResolutionCache(
16931700
filePath: Path,
16941701
cache: ModuleOrTypeReferenceResolutionCache<ResolutionWithFailedLookupLocations>,
16951702
) {
1696-
Debug.assertIsDefined(resolution.files);
1697-
resolution.files.delete(filePath);
1698-
if (resolution.files.size) return;
1703+
const watchedResolutionInfo = watchedResolutionInfoMap.get(resolution)!;
1704+
watchedResolutionInfo.files.delete(filePath);
1705+
if (watchedResolutionInfo.files.size) return;
16991706
let setOfResolutions = potentiallyUnreferencedResolutions?.get(cache);
17001707
if (!setOfResolutions) (potentiallyUnreferencedResolutions ??= new Map()).set(cache, setOfResolutions = new Set());
17011708
setOfResolutions.add(resolution);
17021709
}
17031710

17041711
function releaseResolution(resolution: ResolutionWithFailedLookupLocations) {
1705-
resolution.files = undefined;
1706-
// Even if this is in cache, we cant reuse this resolution after this since we are not watching it any more
1707-
resolution.isInvalidated = true;
1712+
const watchedResolutionInfo = watchedResolutionInfoMap.get(resolution)!;
1713+
if (watchedResolutionInfo.files.size) return false;
1714+
watchedResolutionInfoMap.delete(resolution);
17081715
if (isResolvedWithGlobalCachePass(resolution)) resolutionsResolvedWithGlobalCache--;
17091716
else if (isResolvedWithoutGlobalCachePass(resolution)) resolutionsResolvedWithoutGlobalCache--;
17101717
const resolved = getModuleOrTypeRefResolved(resolution);
@@ -1714,7 +1721,8 @@ export function createResolutionCache(
17141721
if (resolutions?.delete(resolution) && !resolutions.size) resolvedFileToResolution.delete(key);
17151722
}
17161723

1717-
const { failedLookupLocations, affectingLocations, alternateResult, setAtRoot, rootDirInfo } = resolution;
1724+
const { failedLookupLocations, affectingLocations, alternateResult } = resolution;
1725+
const { setAtRoot, rootDirInfo } = watchedResolutionInfo;
17181726
if (resolutionsWithFailedLookups.delete(resolution)) {
17191727
if (failedLookupLocations) {
17201728
for (const failedLookupLocation of failedLookupLocations) {
@@ -1735,6 +1743,7 @@ export function createResolutionCache(
17351743
closeFileWatcherOfAffectingLocation(watcher, affectingLocation);
17361744
}
17371745
}
1746+
return true;
17381747
}
17391748

17401749
function removeDirectoryWatcher(dirPath: Path, delayed: boolean) {
@@ -1799,9 +1808,10 @@ export function createResolutionCache(
17991808
if (!resolutions) return false;
18001809
let invalidated = false;
18011810
resolutions.forEach(resolution => {
1802-
if (resolution.isInvalidated || !canInvalidate(resolution)) return;
1803-
resolution.isInvalidated = invalidated = true;
1804-
for (const containingFilePath of Debug.checkDefined(resolution.files)) {
1811+
const watchedResolutionInfo = watchedResolutionInfoMap.get(resolution)!;
1812+
if (watchedResolutionInfo.isInvalidated || !canInvalidate(resolution)) return;
1813+
watchedResolutionInfo.isInvalidated = invalidated = true;
1814+
for (const containingFilePath of watchedResolutionInfo.files) {
18051815
(filesWithInvalidatedResolutions ??= new Set()).add(containingFilePath);
18061816
// When its a file with inferred types resolution, invalidate type reference directive resolution
18071817
hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames || endsWith(containingFilePath, inferredTypesContainingFile);

0 commit comments

Comments
 (0)