@@ -22,6 +22,7 @@ import {
22
22
convertToOptionsWithAbsolutePaths ,
23
23
createBuildInfo ,
24
24
createGetCanonicalFileName ,
25
+ createModuleNotFoundChain ,
25
26
createProgram ,
26
27
CustomTransformers ,
27
28
Debug ,
@@ -68,8 +69,11 @@ import {
68
69
ProjectReference ,
69
70
ReadBuildProgramHost ,
70
71
ReadonlyCollection ,
72
+ RepopulateDiagnosticChainInfo ,
73
+ RepopulateModuleNotFoundDiagnosticChain ,
71
74
returnFalse ,
72
75
returnUndefined ,
76
+ sameMap ,
73
77
SemanticDiagnosticsBuilderProgram ,
74
78
skipTypeChecking ,
75
79
some ,
@@ -103,7 +107,18 @@ export interface ReusableDiagnosticRelatedInformation {
103
107
}
104
108
105
109
/** @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 ;
107
122
108
123
/**
109
124
* 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
364
379
// Unchanged file copy diagnostics
365
380
const diagnostics = oldState ! . semanticDiagnosticsPerFile ! . get ( sourceFilePath ) ;
366
381
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
+ ) ;
368
388
if ( ! state . semanticDiagnosticsFromOldState ) {
369
389
state . semanticDiagnosticsFromOldState = new Set ( ) ;
370
390
}
@@ -448,6 +468,43 @@ function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions:
448
468
isString ( oldEmitSignature ) ? [ oldEmitSignature ] : oldEmitSignature [ 0 ] ;
449
469
}
450
470
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
+
451
508
function convertToDiagnostics ( diagnostics : readonly ReusableDiagnostic [ ] , newProgram : Program ) : readonly Diagnostic [ ] {
452
509
if ( ! diagnostics . length ) return emptyArray ;
453
510
let buildInfoDirectory : string | undefined ;
@@ -474,9 +531,13 @@ function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newPro
474
531
475
532
function convertToDiagnosticRelatedInformation ( diagnostic : ReusableDiagnosticRelatedInformation , newProgram : Program , toPath : ( path : string ) => Path ) : DiagnosticRelatedInformation {
476
533
const { file } = diagnostic ;
534
+ const sourceFile = file ? newProgram . getSourceFileByPath ( toPath ( file ) ) : undefined ;
477
535
return {
478
536
...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 ) ,
480
541
} ;
481
542
}
482
543
@@ -1232,10 +1293,36 @@ function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRel
1232
1293
const { file } = diagnostic ;
1233
1294
return {
1234
1295
...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 ) ,
1236
1298
} ;
1237
1299
}
1238
1300
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
+
1239
1326
/** @internal */
1240
1327
export enum BuilderProgramKind {
1241
1328
SemanticDiagnosticsBuilderProgram ,
0 commit comments