diff --git a/src/harness/incrementalUtils.ts b/src/harness/incrementalUtils.ts index 47114c88d51..55301c7ec61 100644 --- a/src/harness/incrementalUtils.ts +++ b/src/harness/incrementalUtils.ts @@ -448,17 +448,25 @@ function verifySet( expected?.forEach(expected => ts.Debug.assert( actual?.has(expected), - `${caption}:: Expected should be present in actual`, + `${caption}:: ${expected} should be present in actual`, ) ); actual?.forEach(actual => ts.Debug.assert( expected?.has(actual), - `${caption}:: Actual should be present in expected`, + `${caption}:: ${actual} should be present in expected`, ) ); } +function verifyArray( + expected: readonly string[] | undefined, + actual: readonly string[] | undefined, + caption: string, +) { + return verifySet(expected && new Set(expected), actual && new Set(actual), caption); +} + function verifyProgram(service: ts.server.ProjectService, project: ts.server.Project) { if (service.serverMode === ts.LanguageServiceMode.Syntactic) return; const options = project.getCompilerOptions(); @@ -572,6 +580,24 @@ function verifyProgram(service: ts.server.ProjectService, project: ts.server.Pro verifyResolutionCache(project.resolutionCache, project.getCurrentProgram()!, resolutionHostCacheHost, project.projectName); } +function verifyUnresolvedImports(_service: ts.server.ProjectService, project: ts.server.Project) { + const cachedUnresolvedImportsPerFile = new Map(); + const lastCachedUnresolvedImportsList = project.useTypingsFromGlobalCache() ? + ts.server.getUnresolvedImports(project.getCurrentProgram()!, cachedUnresolvedImportsPerFile, ts.noop) : + undefined; + verifyArray( + lastCachedUnresolvedImportsList, + project.lastCachedUnresolvedImportsList, + `${project.getProjectName()}:: lastCachedUnresolvedImportsList`, + ); + verifyMap( + cachedUnresolvedImportsPerFile, + project.cachedUnresolvedImportsPerFile, + (expected, actual, caption) => verifyArray(expected, actual, caption), + `${project.getProjectName()}:: cachedUnresolvedImportsPerFile`, + ); +} + interface ResolveSingleModuleNameWithoutWatchingData { resolutionToData: Map>; packageJsonMap: Map | undefined; @@ -648,6 +674,7 @@ export interface IncrementalVerifierCallbacks { export function incrementalVerifier(service: ts.server.ProjectService): void { service.verifyDocumentRegistry = withIncrementalVerifierCallbacks(service, verifyDocumentRegistry); service.verifyProgram = withIncrementalVerifierCallbacks(service, verifyProgram); + service.verifyUnresovedImports = withIncrementalVerifierCallbacks(service, verifyUnresolvedImports); service.onProjectCreation = onProjectCreation; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 23f8d844c98..3b260b15d0e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1424,6 +1424,7 @@ export class ProjectService { /** @internal */ baseline: (title?: string) => void = noop; /** @internal */ verifyDocumentRegistry: typeof noop = noop; /** @internal */ verifyProgram: (project: Project) => void = noop; + /** @internal */ verifyUnresovedImports: (project: Project) => void = noop; /** @internal */ onProjectCreation: (project: Project) => void = noop; /** @internal */ canUseWatchEvents: boolean; diff --git a/src/server/project.ts b/src/server/project.ts index 85b1d08f96d..f3f8026a9a2 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1418,6 +1418,13 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo updateProjectIfDirty(this); } + /** @internal */ + useTypingsFromGlobalCache(): boolean { + return this.languageServiceEnabled && + this.projectService.serverMode === LanguageServiceMode.Semantic && + !this.isOrphan(); + } + /** * Updates set of files that contribute to this project * @returns: true if set of files in the project stays the same and false - otherwise. @@ -1440,7 +1447,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo // update builder only if language service is enabled // otherwise tell it to drop its internal state - if (this.languageServiceEnabled && this.projectService.serverMode === LanguageServiceMode.Semantic && !this.isOrphan()) { + if (this.useTypingsFromGlobalCache()) { // 1. no changes in structure, no changes in unresolved imports - do nothing // 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files // (can reuse cached imports for files that were not changed) @@ -1460,6 +1467,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo else { this.lastCachedUnresolvedImportsList = undefined; } + this.projectService.verifyUnresovedImports(this); const isFirstProgramLoad = this.projectProgramVersion === 0 && hasNewProgram; if (hasNewProgram) { @@ -2361,7 +2369,8 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo } } -function getUnresolvedImports( +/** @internal */ +export function getUnresolvedImports( program: Program, cachedUnresolvedImportsPerFile: Map, writeLog: (s: string) => void,