Skip to content

Commit b846033

Browse files
authored
Instead of clearing out all resolutions and closing all the directory watchers, mark everything as invalidated when changes affect module resolution (#53882)
1 parent 8575886 commit b846033

15 files changed

+113
-490
lines changed

src/compiler/resolutionCache.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export interface ResolutionCache {
125125
getModuleResolutionCache(): ModuleResolutionCache;
126126

127127
clear(): void;
128+
onChangesAffectModuleResolution(): void;
128129
}
129130

130131
/** @internal */
@@ -412,6 +413,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
412413
let failedLookupChecks: Set<Path> | undefined;
413414
let startsWithPathChecks: Set<Path> | undefined;
414415
let isInDirectoryChecks: Set<Path> | undefined;
416+
let allResolutionsAreInvalidated = false;
415417

416418
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!()); // TODO: GH#18217
417419
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
@@ -464,7 +466,8 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
464466
isFileWithInvalidatedNonRelativeUnresolvedImports,
465467
updateTypeRootsWatch,
466468
closeTypeRootsWatch,
467-
clear
469+
clear,
470+
onChangesAffectModuleResolution,
468471
};
469472

470473
function getResolvedModule(resolution: CachedResolvedModuleWithFailedLookupLocations) {
@@ -490,6 +493,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
490493
isInDirectoryChecks = undefined;
491494
affectingPathChecks = undefined;
492495
affectingPathChecksForFile = undefined;
496+
allResolutionsAreInvalidated = false;
493497
moduleResolutionCache.clear();
494498
typeReferenceDirectiveResolutionCache.clear();
495499
moduleResolutionCache.update(resolutionHost.getCompilationSettings());
@@ -498,6 +502,14 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
498502
hasChangedAutomaticTypeDirectiveNames = false;
499503
}
500504

505+
function onChangesAffectModuleResolution() {
506+
allResolutionsAreInvalidated = true;
507+
moduleResolutionCache.clearAllExceptPackageJsonInfoCache();
508+
typeReferenceDirectiveResolutionCache.clearAllExceptPackageJsonInfoCache();
509+
moduleResolutionCache.update(resolutionHost.getCompilationSettings());
510+
typeReferenceDirectiveResolutionCache.update(resolutionHost.getCompilationSettings());
511+
}
512+
501513
function startRecordingFilesWithChangedResolutions() {
502514
filesWithChangedSetOfUnresolvedImports = [];
503515
}
@@ -524,6 +536,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
524536
const collected = filesWithInvalidatedResolutions;
525537
filesWithInvalidatedResolutions = undefined;
526538
return path => customHasInvalidatedResolutions(path) ||
539+
allResolutionsAreInvalidated ||
527540
!!collected?.has(path) ||
528541
isFileWithInvalidatedNonRelativeUnresolvedImports(path);
529542
}
@@ -539,6 +552,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
539552

540553
function finishCachingPerDirectoryResolution(newProgram: Program | undefined, oldProgram: Program | undefined) {
541554
filesWithInvalidatedNonRelativeUnresolvedImports = undefined;
555+
allResolutionsAreInvalidated = false;
542556
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
543557
nonRelativeExternalModuleResolutions.clear();
544558
// Update file watches
@@ -671,9 +685,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
671685
let resolution = resolutionsInFile.get(name, mode);
672686
// Resolution is valid if it is present and not invalidated
673687
if (!seenNamesInFile.has(name, mode) &&
674-
unmatchedRedirects || !resolution || resolution.isInvalidated ||
675-
// If the name is unresolved import that was invalidated, recalculate
676-
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
688+
(allResolutionsAreInvalidated || unmatchedRedirects || !resolution || resolution.isInvalidated ||
689+
// If the name is unresolved import that was invalidated, recalculate
690+
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution)))) {
677691
const existingResolution = resolution;
678692
resolution = loader.resolve(name, mode);
679693
if (resolutionHost.onDiscoveredSymlink && resolutionIsSymlink(resolution)) {
@@ -694,7 +708,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
694708
else {
695709
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
696710
if (isTraceEnabled(options, host) && !seenNamesInFile.has(name, mode)) {
697-
const resolved = getResolutionWithResolvedFileName(resolution);
711+
const resolved = getResolutionWithResolvedFileName(resolution!);
698712
trace(
699713
host,
700714
perFileCache === resolvedModuleNames as unknown ?
@@ -1163,7 +1177,23 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
11631177
resolutionHost.scheduleInvalidateResolutionsOfFailedLookupLocations();
11641178
}
11651179

1180+
function invalidatePackageJsonMap() {
1181+
const packageJsonMap = moduleResolutionCache.getPackageJsonInfoCache().getInternalMap();
1182+
if (packageJsonMap && (failedLookupChecks || startsWithPathChecks || isInDirectoryChecks)) {
1183+
packageJsonMap.forEach((_value, path) => isInvalidatedFailedLookup(path) ? packageJsonMap.delete(path) : undefined);
1184+
}
1185+
}
1186+
11661187
function invalidateResolutionsOfFailedLookupLocations() {
1188+
if (allResolutionsAreInvalidated) {
1189+
affectingPathChecksForFile = undefined;
1190+
invalidatePackageJsonMap();
1191+
failedLookupChecks = undefined;
1192+
startsWithPathChecks = undefined;
1193+
isInDirectoryChecks = undefined;
1194+
affectingPathChecks = undefined;
1195+
return true;
1196+
}
11671197
let invalidated = false;
11681198
if (affectingPathChecksForFile) {
11691199
resolutionHost.getCurrentProgram()?.getSourceFiles().forEach(f => {
@@ -1180,10 +1210,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
11801210
}
11811211

11821212
invalidated = invalidateResolutions(resolutionsWithFailedLookups, canInvalidateFailedLookupResolution) || invalidated;
1183-
const packageJsonMap = moduleResolutionCache.getPackageJsonInfoCache().getInternalMap();
1184-
if (packageJsonMap && (failedLookupChecks || startsWithPathChecks || isInDirectoryChecks)) {
1185-
packageJsonMap.forEach((_value, path) => isInvalidatedFailedLookup(path) ? packageJsonMap.delete(path) : undefined);
1186-
}
1213+
invalidatePackageJsonMap();
11871214
failedLookupChecks = undefined;
11881215
startsWithPathChecks = undefined;
11891216
isInDirectoryChecks = undefined;

src/compiler/watchPublic.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
584584
if (hasChangedCompilerOptions) {
585585
newLine = updateNewLine();
586586
if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) {
587-
resolutionCache.clear();
587+
debugger;
588+
resolutionCache.onChangesAffectModuleResolution();
588589
}
589590
}
590591

src/server/project.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
16431643
// reset cached unresolved imports if changes in compiler options affected module resolution
16441644
this.cachedUnresolvedImportsPerFile.clear();
16451645
this.lastCachedUnresolvedImportsList = undefined;
1646-
this.resolutionCache.clear();
1646+
this.resolutionCache.onChangesAffectModuleResolution();
16471647
this.moduleSpecifierCache.clear();
16481648
}
16491649
this.markAsDirty();
@@ -2205,13 +2205,17 @@ export class InferredProject extends Project {
22052205
if (!this._isJsInferredProject && info.isJavaScript()) {
22062206
this.toggleJsInferredProject(/*isJsInferredProject*/ true);
22072207
}
2208+
else if (this.isOrphan() && this._isJsInferredProject && !info.isJavaScript()) {
2209+
this.toggleJsInferredProject(/*isJsInferredProject*/ false);
2210+
}
22082211
super.addRoot(info);
22092212
}
22102213

22112214
override removeRoot(info: ScriptInfo) {
22122215
this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info);
22132216
super.removeRoot(info);
2214-
if (this._isJsInferredProject && info.isJavaScript()) {
2217+
// Delay toggling to isJsInferredProject = false till we actually need it again
2218+
if (!this.isOrphan() && this._isJsInferredProject && info.isJavaScript()) {
22152219
if (every(this.getRootScriptInfos(), rootInfo => !rootInfo.isJavaScript())) {
22162220
this.toggleJsInferredProject(/*isJsInferredProject*/ false);
22172221
}

tests/baselines/reference/tscWatch/programUpdates/updates-emit-on-jsx-option-change.js

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -99,30 +99,6 @@ Semantic diagnostics in builder refreshed for::
9999

100100
No shapes updated in the builder::
101101

102-
PolledWatches::
103-
/user/username/projects/myproject/node_modules/@types:
104-
{"pollingInterval":500} *new*
105-
/user/username/projects/node_modules/@types:
106-
{"pollingInterval":500} *new*
107-
108-
PolledWatches *deleted*::
109-
/user/username/projects/myproject/node_modules/@types:
110-
{"pollingInterval":500}
111-
/user/username/projects/node_modules/@types:
112-
{"pollingInterval":500}
113-
114-
FsWatches::
115-
/user/username/projects/myproject/tsconfig.json:
116-
{}
117-
/user/username/projects/myproject/index.tsx:
118-
{}
119-
/a/lib/lib.d.ts:
120-
{}
121-
122-
FsWatchesRecursive::
123-
/user/username/projects/myproject:
124-
{}
125-
126102
exitCode:: ExitStatus.undefined
127103

128104
//// [/user/username/projects/myproject/index.js]

tests/baselines/reference/tscWatch/programUpdates/updates-moduleResolution-when-resolveJsonModule-changes.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,6 @@ Shape signatures in builder refreshed for::
117117
/user/username/projects/myproject/a.ts (computed .d.ts)
118118

119119
PolledWatches::
120-
/user/username/projects/myproject/data.json:
121-
{"pollingInterval":500} *new*
122-
/user/username/projects/myproject/node_modules/@types:
123-
{"pollingInterval":500} *new*
124-
/user/username/projects/node_modules/@types:
125-
{"pollingInterval":500} *new*
126-
127-
PolledWatches *deleted*::
128120
/user/username/projects/myproject/data.json:
129121
{"pollingInterval":500}
130122
/user/username/projects/myproject/node_modules/@types:
@@ -137,17 +129,13 @@ FsWatches::
137129
{}
138130
/user/username/projects/myproject/a.ts:
139131
{}
132+
/user/username/projects/myproject:
133+
{}
140134
/a/lib/lib.d.ts:
141135
{}
142-
/user/username/projects/myproject:
143-
{} *new*
144136
/user/username/projects/myproject/data.json: *new*
145137
{}
146138

147-
FsWatches *deleted*::
148-
/user/username/projects/myproject:
149-
{}
150-
151139
FsWatchesRecursive::
152140
/user/username/projects/myproject:
153141
{}

0 commit comments

Comments
 (0)