@@ -22,6 +22,7 @@ import {
2222 convertToOptionsWithAbsolutePaths ,
2323 createBuildInfo ,
2424 createGetCanonicalFileName ,
25+ createModuleNotFoundChain ,
2526 createProgram ,
2627 CustomTransformers ,
2728 Debug ,
@@ -68,8 +69,11 @@ import {
6869 ProjectReference ,
6970 ReadBuildProgramHost ,
7071 ReadonlyCollection ,
72+ RepopulateDiagnosticChainInfo ,
73+ RepopulateModuleNotFoundDiagnosticChain ,
7174 returnFalse ,
7275 returnUndefined ,
76+ sameMap ,
7377 SemanticDiagnosticsBuilderProgram ,
7478 skipTypeChecking ,
7579 some ,
@@ -103,7 +107,18 @@ export interface ReusableDiagnosticRelatedInformation {
103107}
104108
105109/** @internal */
106- export type ReusableDiagnosticMessageChain = DiagnosticMessageChain ;
110+ export interface ReusableRepopulateModuleNotFoundChain {
111+ info : RepopulateModuleNotFoundDiagnosticChain ;
112+ next ?: ReusableDiagnosticMessageChain [ ] ;
113+ }
114+
115+ /** @internal */
116+ export type SerializedDiagnosticMessageChain = Omit < DiagnosticMessageChain , "next" | "repopulateInfo" > & {
117+ next ?: ReusableDiagnosticMessageChain [ ] ;
118+ } ;
119+
120+ /** @internal */
121+ export type ReusableDiagnosticMessageChain = SerializedDiagnosticMessageChain | ReusableRepopulateModuleNotFoundChain ;
107122
108123/**
109124 * Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string
@@ -364,7 +379,12 @@ function createBuilderProgramState(newProgram: Program, oldState: Readonly<Reusa
364379 // Unchanged file copy diagnostics
365380 const diagnostics = oldState ! . semanticDiagnosticsPerFile ! . get ( sourceFilePath ) ;
366381 if ( diagnostics ) {
367- state . semanticDiagnosticsPerFile ! . set ( sourceFilePath , oldState ! . hasReusableDiagnostic ? convertToDiagnostics ( diagnostics as readonly ReusableDiagnostic [ ] , newProgram ) : diagnostics as readonly Diagnostic [ ] ) ;
382+ state . semanticDiagnosticsPerFile ! . set (
383+ sourceFilePath ,
384+ oldState ! . hasReusableDiagnostic ?
385+ convertToDiagnostics ( diagnostics as readonly ReusableDiagnostic [ ] , newProgram ) :
386+ repopulateDiagnostics ( diagnostics as readonly Diagnostic [ ] , newProgram )
387+ ) ;
368388 if ( ! state . semanticDiagnosticsFromOldState ) {
369389 state . semanticDiagnosticsFromOldState = new Set ( ) ;
370390 }
@@ -448,6 +468,43 @@ function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions:
448468 isString ( oldEmitSignature ) ? [ oldEmitSignature ] : oldEmitSignature [ 0 ] ;
449469}
450470
471+ function repopulateDiagnostics ( diagnostics : readonly Diagnostic [ ] , newProgram : Program ) : readonly Diagnostic [ ] {
472+ if ( ! diagnostics . length ) return diagnostics ;
473+ return sameMap ( diagnostics , diag => {
474+ if ( isString ( diag . messageText ) ) return diag ;
475+ const repopulatedChain = convertOrRepopulateDiagnosticMessageChain ( diag . messageText , diag . file , newProgram , chain => chain . repopulateInfo ?.( ) ) ;
476+ return repopulatedChain === diag . messageText ?
477+ diag :
478+ { ...diag , messageText : repopulatedChain } ;
479+ } ) ;
480+ }
481+
482+ function convertOrRepopulateDiagnosticMessageChain < T extends DiagnosticMessageChain | ReusableDiagnosticMessageChain > (
483+ chain : T ,
484+ sourceFile : SourceFile | undefined ,
485+ newProgram : Program ,
486+ repopulateInfo : ( chain : T ) => RepopulateDiagnosticChainInfo | undefined ,
487+ ) : DiagnosticMessageChain {
488+ const info = repopulateInfo ( chain ) ;
489+ if ( info ) {
490+ return {
491+ ...createModuleNotFoundChain ( sourceFile ! , newProgram , info . moduleReference , info . mode , info . packageName || info . moduleReference ) ,
492+ next : convertOrRepopulateDiagnosticMessageChainArray ( chain . next as T [ ] , sourceFile , newProgram , repopulateInfo ) ,
493+ } ;
494+ }
495+ const next = convertOrRepopulateDiagnosticMessageChainArray ( chain . next as T [ ] , sourceFile , newProgram , repopulateInfo ) ;
496+ return next === chain . next ? chain as DiagnosticMessageChain : { ...chain as DiagnosticMessageChain , next } ;
497+ }
498+
499+ function convertOrRepopulateDiagnosticMessageChainArray < T extends DiagnosticMessageChain | ReusableDiagnosticMessageChain > (
500+ array : T [ ] | undefined ,
501+ sourceFile : SourceFile | undefined ,
502+ newProgram : Program ,
503+ repopulateInfo : ( chain : T ) => RepopulateDiagnosticChainInfo | undefined ,
504+ ) : DiagnosticMessageChain [ ] | undefined {
505+ return sameMap ( array , chain => convertOrRepopulateDiagnosticMessageChain ( chain , sourceFile , newProgram , repopulateInfo ) ) ;
506+ }
507+
451508function convertToDiagnostics ( diagnostics : readonly ReusableDiagnostic [ ] , newProgram : Program ) : readonly Diagnostic [ ] {
452509 if ( ! diagnostics . length ) return emptyArray ;
453510 let buildInfoDirectory : string | undefined ;
@@ -474,9 +531,13 @@ function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newPro
474531
475532function convertToDiagnosticRelatedInformation ( diagnostic : ReusableDiagnosticRelatedInformation , newProgram : Program , toPath : ( path : string ) => Path ) : DiagnosticRelatedInformation {
476533 const { file } = diagnostic ;
534+ const sourceFile = file ? newProgram . getSourceFileByPath ( toPath ( file ) ) : undefined ;
477535 return {
478536 ...diagnostic ,
479- file : file ? newProgram . getSourceFileByPath ( toPath ( file ) ) : undefined
537+ file : sourceFile ,
538+ messageText : isString ( diagnostic . messageText ) ?
539+ diagnostic . messageText :
540+ convertOrRepopulateDiagnosticMessageChain ( diagnostic . messageText , sourceFile , newProgram , chain => ( chain as ReusableRepopulateModuleNotFoundChain ) . info ) ,
480541 } ;
481542}
482543
@@ -1232,10 +1293,36 @@ function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRel
12321293 const { file } = diagnostic ;
12331294 return {
12341295 ...diagnostic ,
1235- file : file ? relativeToBuildInfo ( file . resolvedPath ) : undefined
1296+ file : file ? relativeToBuildInfo ( file . resolvedPath ) : undefined ,
1297+ messageText : isString ( diagnostic . messageText ) ? diagnostic . messageText : convertToReusableDiagnosticMessageChain ( diagnostic . messageText ) ,
12361298 } ;
12371299}
12381300
1301+ function convertToReusableDiagnosticMessageChain ( chain : DiagnosticMessageChain ) : ReusableDiagnosticMessageChain {
1302+ if ( chain . repopulateInfo ) {
1303+ return {
1304+ info : chain . repopulateInfo ( ) ,
1305+ next : convertToReusableDiagnosticMessageChainArray ( chain . next ) ,
1306+ } ;
1307+ }
1308+ const next = convertToReusableDiagnosticMessageChainArray ( chain . next ) ;
1309+ return next === chain . next ? chain : { ...chain , next } ;
1310+ }
1311+
1312+ function convertToReusableDiagnosticMessageChainArray ( array : DiagnosticMessageChain [ ] | undefined ) : ReusableDiagnosticMessageChain [ ] | undefined {
1313+ if ( ! array ) return array ;
1314+ return forEach ( array , ( chain , index ) => {
1315+ const reusable = convertToReusableDiagnosticMessageChain ( chain ) ;
1316+ if ( chain === reusable ) return undefined ;
1317+ const result : ReusableDiagnosticMessageChain [ ] = index > 0 ? array . slice ( 0 , index - 1 ) : [ ] ;
1318+ result . push ( reusable ) ;
1319+ for ( let i = index + 1 ; i < array . length ; i ++ ) {
1320+ result . push ( convertToReusableDiagnosticMessageChain ( array [ i ] ) ) ;
1321+ }
1322+ return result ;
1323+ } ) || array ;
1324+ }
1325+
12391326/** @internal */
12401327export enum BuilderProgramKind {
12411328 SemanticDiagnosticsBuilderProgram ,
0 commit comments