Skip to content

Commit e6d920c

Browse files
committed
Refactor getUnresolvedImports and verify it incrementally
1 parent df1235e commit e6d920c

File tree

3 files changed

+89
-42
lines changed

3 files changed

+89
-42
lines changed

src/harness/incrementalUtils.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,17 +422,25 @@ function verifySet(
422422
expected?.forEach(expected =>
423423
ts.Debug.assert(
424424
actual?.has(expected),
425-
`${caption}:: Expected should be present in actual`,
425+
`${caption}:: ${expected} should be present in actual`,
426426
)
427427
);
428428
actual?.forEach(actual =>
429429
ts.Debug.assert(
430430
expected?.has(actual),
431-
`${caption}:: Actual should be present in expected`,
431+
`${caption}:: ${actual} should be present in expected`,
432432
)
433433
);
434434
}
435435

436+
function verifyArray(
437+
expected: readonly string[] | undefined,
438+
actual: readonly string[] | undefined,
439+
caption: string,
440+
) {
441+
return verifySet(expected && new Set(expected), actual && new Set(actual), caption);
442+
}
443+
436444
function verifyProgram(service: ts.server.ProjectService, project: ts.server.Project) {
437445
if (service.serverMode === ts.LanguageServiceMode.Syntactic) return;
438446
const options = project.getCompilerOptions();
@@ -523,6 +531,24 @@ function verifyProgram(service: ts.server.ProjectService, project: ts.server.Pro
523531
verifyResolutionCache(project.resolutionCache, project.getCurrentProgram()!, resolutionHostCacheHost, project.projectName);
524532
}
525533

534+
function verifyUnresolvedImports(_service: ts.server.ProjectService, project: ts.server.Project) {
535+
const cachedUnresolvedImportsPerFile = new Map<ts.Path, readonly string[]>();
536+
const lastCachedUnresolvedImportsList = project.useTypingsFromGlobalCache() ?
537+
ts.server.getUnresolvedImports(project.getCurrentProgram()!, cachedUnresolvedImportsPerFile, ts.noop) :
538+
undefined;
539+
verifyArray(
540+
lastCachedUnresolvedImportsList,
541+
project.lastCachedUnresolvedImportsList,
542+
`${project.getProjectName()}:: lastCachedUnresolvedImportsList`,
543+
);
544+
verifyMap(
545+
cachedUnresolvedImportsPerFile,
546+
project.cachedUnresolvedImportsPerFile,
547+
(expected, actual, caption) => verifyArray(expected, actual, caption),
548+
`${project.getProjectName()}:: cachedUnresolvedImportsPerFile`,
549+
);
550+
}
551+
526552
interface ResolveSingleModuleNameWithoutWatchingData {
527553
resolutionToData: Map<ts.ResolutionWithFailedLookupLocations, Pick<ts.ResolvedModuleWithFailedLookupLocations, "failedLookupLocations" | "affectingLocations" | "resolutionDiagnostics">>;
528554
packageJsonMap: Map<ts.Path, ts.PackageJsonInfo | boolean> | undefined;
@@ -599,6 +625,7 @@ export interface IncrementalVerifierCallbacks {
599625
export function incrementalVerifier(service: ts.server.ProjectService) {
600626
service.verifyDocumentRegistry = withIncrementalVerifierCallbacks(service, verifyDocumentRegistry);
601627
service.verifyProgram = withIncrementalVerifierCallbacks(service, verifyProgram);
628+
service.verifyUnresovedImports = withIncrementalVerifierCallbacks(service, verifyUnresolvedImports);
602629
service.onProjectCreation = onProjectCreation;
603630
}
604631

src/server/editorServices.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,7 @@ export class ProjectService {
11381138

11391139
/** @internal */ verifyDocumentRegistry = noop;
11401140
/** @internal */ verifyProgram: (project: Project) => void = noop;
1141+
/** @internal */ verifyUnresovedImports: (project: Project) => void = noop;
11411142
/** @internal */ onProjectCreation: (project: Project) => void = noop;
11421143

11431144
readonly jsDocParsingMode: JSDocParsingMode | undefined;

src/server/project.ts

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,17 +1408,22 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
14081408
this.hasAddedOrRemovedSymlinks = true;
14091409
}
14101410

1411+
/** @internal */
1412+
useTypingsFromGlobalCache() {
1413+
return this.languageServiceEnabled &&
1414+
this.projectService.serverMode === LanguageServiceMode.Semantic &&
1415+
this.projectService.typingsInstaller !== nullTypingsInstaller &&
1416+
this.getTypeAcquisition().enable;
1417+
}
1418+
14111419
/**
14121420
* Updates set of files that contribute to this project
14131421
* @returns: true if set of files in the project stays the same and false - otherwise.
14141422
*/
14151423
updateGraph(): boolean {
14161424
tracing?.push(tracing.Phase.Session, "updateGraph", { name: this.projectName, kind: ProjectKind[this.projectKind] });
14171425
perfLogger?.logStartUpdateGraph();
1418-
const useTypingsFromGlobalCache = this.languageServiceEnabled &&
1419-
this.projectService.serverMode === LanguageServiceMode.Semantic &&
1420-
this.projectService.typingsInstaller !== nullTypingsInstaller &&
1421-
this.getTypeAcquisition().enable;
1426+
const useTypingsFromGlobalCache = this.useTypingsFromGlobalCache();
14221427
if (!useTypingsFromGlobalCache) this.resolutionCache.invalidateResolutionsWithGlobalCachePass();
14231428
else this.resolutionCache.invalidateResolutionsWithoutGlobalCachePass();
14241429
if (useTypingsFromGlobalCache && this.cachedUnresolvedImportsPerFile.size) this.recordChangesToUnresolvedImports = true;
@@ -1430,14 +1435,19 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
14301435
this.recordChangesToUnresolvedImports = false;
14311436

14321437
if (useTypingsFromGlobalCache) {
1433-
this.lastCachedUnresolvedImportsList ??= this.getUnresolvedImports();
1438+
this.lastCachedUnresolvedImportsList ??= getUnresolvedImports(
1439+
this.program!,
1440+
this.cachedUnresolvedImportsPerFile,
1441+
s => this.writeLog(s),
1442+
);
14341443
this.enqueueInstallTypingsForProject(hasAddedorRemovedFiles);
14351444
}
14361445
else {
14371446
this.lastCachedUnresolvedImportsList = undefined;
14381447
this.cachedUnresolvedImportsPerFile.clear();
14391448
this.typingsCacheEntry = undefined;
14401449
}
1450+
this.projectService.verifyUnresovedImports(this);
14411451

14421452
const isFirstProgramLoad = this.projectProgramVersion === 0 && hasNewProgram;
14431453
if (hasNewProgram) {
@@ -2344,43 +2354,52 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
23442354
noLib: true,
23452355
};
23462356
}
2357+
}
23472358

2348-
/** @internal */
2349-
private getUnresolvedImports(): SortedReadonlyArray<string> {
2350-
const sourceFiles = this.program!.getSourceFiles();
2351-
this.writeLog(`Calculating unresolved imports list of program:: Files:: ${sourceFiles.length}`);
2352-
tracing?.push(tracing.Phase.Session, "getUnresolvedImports", { count: sourceFiles.length });
2353-
const ambientModules = this.program!.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName()));
2354-
const result = sortAndDeduplicate(flatMap(sourceFiles, sourceFile =>
2355-
this.extractUnresolvedImportsFromSourceFile(
2356-
sourceFile,
2357-
ambientModules,
2358-
)));
2359-
tracing?.pop();
2360-
this.writeLog(`Calculating unresolved imports list of program:: Files:: ${sourceFiles.length} Done: ${JSON.stringify(result)}`);
2361-
return result;
2362-
}
2359+
/** @internal */
2360+
export function getUnresolvedImports(
2361+
program: Program,
2362+
cachedUnresolvedImportsPerFile: Map<Path, readonly string[]>,
2363+
writeLog: (s: string) => void,
2364+
): SortedReadonlyArray<string> {
2365+
const sourceFiles = program.getSourceFiles();
2366+
writeLog(`Calculating unresolved imports list of program:: Files:: ${sourceFiles.length}`);
2367+
tracing?.push(tracing.Phase.Session, "getUnresolvedImports", { count: sourceFiles.length });
2368+
const ambientModules = program.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName()));
2369+
const result = sortAndDeduplicate(flatMap(sourceFiles, sourceFile =>
2370+
extractUnresolvedImportsFromSourceFile(
2371+
program,
2372+
sourceFile,
2373+
ambientModules,
2374+
cachedUnresolvedImportsPerFile,
2375+
writeLog,
2376+
)));
2377+
tracing?.pop();
2378+
writeLog(`Calculating unresolved imports list of program:: Files:: ${sourceFiles.length} Done: ${JSON.stringify(result)}`);
2379+
return result;
2380+
}
23632381

2364-
/** @internal */
2365-
private extractUnresolvedImportsFromSourceFile(
2366-
file: SourceFile,
2367-
ambientModules: readonly string[],
2368-
): readonly string[] {
2369-
return getOrUpdate(this.cachedUnresolvedImportsPerFile, file.path, () => {
2370-
let unresolvedImports: string[] | undefined;
2371-
this.program!.forEachResolvedModule((resolution, name) => {
2372-
// pick unresolved non-relative names
2373-
if (
2374-
needsResolutionFromGlobalCache(name, resolution) &&
2375-
!ambientModules.some(m => m === name)
2376-
) {
2377-
unresolvedImports = append(unresolvedImports, parsePackageName(name).packageName);
2378-
}
2379-
}, file);
2380-
this.writeLog(`New unresolvedImports for ${file.path}:: ${JSON.stringify(unresolvedImports || emptyArray)}`);
2381-
return unresolvedImports || emptyArray;
2382-
});
2383-
}
2382+
function extractUnresolvedImportsFromSourceFile(
2383+
program: Program,
2384+
file: SourceFile,
2385+
ambientModules: readonly string[],
2386+
cachedUnresolvedImportsPerFile: Map<Path, readonly string[]>,
2387+
writeLog: (s: string) => void,
2388+
): readonly string[] {
2389+
return getOrUpdate(cachedUnresolvedImportsPerFile, file.path, () => {
2390+
let unresolvedImports: string[] | undefined;
2391+
program.forEachResolvedModule((resolution, name) => {
2392+
// pick unresolved non-relative names
2393+
if (
2394+
needsResolutionFromGlobalCache(name, resolution) &&
2395+
!ambientModules.some(m => m === name)
2396+
) {
2397+
unresolvedImports = append(unresolvedImports, parsePackageName(name).packageName);
2398+
}
2399+
}, file);
2400+
writeLog(`New unresolvedImports for ${file.path}:: ${JSON.stringify(unresolvedImports || emptyArray)}`);
2401+
return unresolvedImports || emptyArray;
2402+
});
23842403
}
23852404

23862405
/**

0 commit comments

Comments
 (0)