@@ -276,6 +276,15 @@ namespace ts {
276
276
return Debug . fail ( `project ${ configFile . options . configFilePath } expected to have at least one output` ) ;
277
277
}
278
278
279
+ /*@internal */
280
+ export function getCacheDtsEmitResultKey ( sourceFileOrBundle : SourceFile | Bundle , options : CompilerOptions ) {
281
+ return isSourceFile ( sourceFileOrBundle ) ? sourceFileOrBundle . resolvedPath : outFile ( options ) as Path ;
282
+ }
283
+
284
+ function isForceDtsEmitResult ( result : CacheDtsEmitResult | undefined ) : result is ForceDtsEmitResult {
285
+ return ! ! result && ! ( result as TransformationResult < SourceFile | Bundle > ) . transformed ;
286
+ }
287
+
279
288
/*@internal */
280
289
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
281
290
export function emitFiles ( resolver : EmitResolver , host : EmitHost , targetSourceFile : SourceFile | undefined , { scriptTransformers, declarationTransformers } : EmitTransformers , emitOnlyDtsFiles ?: boolean , onlyBuildInfo ?: boolean , forceDtsEmit ?: boolean ) : EmitResult {
@@ -288,7 +297,6 @@ namespace ts {
288
297
const { enter, exit } = performance . createTimer ( "printTime" , "beforePrint" , "afterPrint" ) ;
289
298
let bundleBuildInfo : BundleBuildInfo | undefined ;
290
299
let emitSkipped = false ;
291
- let exportedModulesFromDeclarationEmit : ExportedModulesFromDeclarationEmit | undefined ;
292
300
293
301
// Emit each output file
294
302
enter ( ) ;
@@ -308,7 +316,6 @@ namespace ts {
308
316
diagnostics : emitterDiagnostics . getDiagnostics ( ) ,
309
317
emittedFiles : emittedFilesList ,
310
318
sourceMaps : sourceMapDataList ,
311
- exportedModulesFromDeclarationEmit
312
319
} ;
313
320
314
321
function emitSourceFileOrBundle ( { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } : EmitFileNames , sourceFileOrBundle : SourceFile | Bundle | undefined ) {
@@ -411,8 +418,7 @@ namespace ts {
411
418
substituteNode : transform . substituteNode ,
412
419
} ) ;
413
420
414
- Debug . assert ( transform . transformed . length === 1 , "Should only see one output from the transform" ) ;
415
- printSourceFileOrBundle ( jsFilePath , sourceMapFilePath , transform . transformed [ 0 ] , printer , compilerOptions ) ;
421
+ printSourceFileOrBundle ( jsFilePath , sourceMapFilePath , transform , printer , compilerOptions ) ;
416
422
417
423
// Clean up emit nodes on parse tree
418
424
transform . dispose ( ) ;
@@ -433,12 +439,48 @@ namespace ts {
433
439
const filesForEmit = forceDtsEmit ? sourceFiles : filter ( sourceFiles , isSourceFileNotJson ) ;
434
440
// Setup and perform the transformation to retrieve declarations from the input files
435
441
const inputListOrBundle = outFile ( compilerOptions ) ? [ factory . createBundle ( filesForEmit , ! isSourceFile ( sourceFileOrBundle ) ? sourceFileOrBundle . prepends : undefined ) ] : filesForEmit ;
436
- if ( emitOnlyDtsFiles && ! getEmitDeclarations ( compilerOptions ) ) {
442
+ // Use existing result:
443
+ const key = getCacheDtsEmitResultKey ( sourceFileOrBundle , compilerOptions ) ;
444
+ const cache = host . getDtsEmitResultCache ( ) ;
445
+ const existing = declarationTransformers . length === 1 ? cache . get ( key ) : undefined ;
446
+ if ( isForceDtsEmitResult ( existing ) ) {
447
+ if ( existing . diagnostics ?. length ) {
448
+ for ( const diagnostic of existing . diagnostics ) {
449
+ emitterDiagnostics . add ( diagnostic ) ;
450
+ }
451
+ }
452
+ const declBlocked = ! ! existing . diagnostics ?. length || ! ! host . isEmitBlocked ( declarationFilePath ) || ! ! compilerOptions . noEmit ;
453
+ emitSkipped = emitSkipped || declBlocked ;
454
+ if ( ! declBlocked || forceDtsEmit ) {
455
+ // Write the output file
456
+ if ( declarationMapPath && ! forceDtsEmit ) {
457
+ if ( sourceMapDataList && existing . sourceMapData ) {
458
+ sourceMapDataList . push ( existing . sourceMapData ) ;
459
+ }
460
+ writeFile ( host , emitterDiagnostics , declarationMapPath , existing . sourceMap ! , /*writeByteOrderMark*/ false , sourceFiles ) ;
461
+ }
462
+ writeFile (
463
+ host ,
464
+ emitterDiagnostics ,
465
+ declarationFilePath ,
466
+ existing . text ,
467
+ ! ! compilerOptions . emitBOM ,
468
+ filesForEmit ,
469
+ {
470
+ sourceMapUrlPos : existing . sourceMapUrlPos ,
471
+ exportedModulesFromDeclarationEmit : existing . exportedModulesFromDeclarationEmit ,
472
+ }
473
+ ) ;
474
+ }
475
+ return ;
476
+ }
477
+
478
+ if ( ! existing && emitOnlyDtsFiles && ! getEmitDeclarations ( compilerOptions ) ) {
437
479
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
438
480
// Do that here when emitting only dts files
439
481
filesForEmit . forEach ( collectLinkedAliases ) ;
440
482
}
441
- const declarationTransform = transformNodes ( resolver , host , factory , compilerOptions , inputListOrBundle , declarationTransformers , /*allowDtsFiles*/ false ) ;
483
+ const declarationTransform = existing || transformNodes ( resolver , host , factory , compilerOptions , inputListOrBundle , declarationTransformers , /*allowDtsFiles*/ false ) ;
442
484
if ( length ( declarationTransform . diagnostics ) ) {
443
485
for ( const diagnostic of declarationTransform . diagnostics ! ) {
444
486
emitterDiagnostics . add ( diagnostic ) ;
@@ -451,8 +493,8 @@ namespace ts {
451
493
noEmitHelpers : true ,
452
494
module : compilerOptions . module ,
453
495
target : compilerOptions . target ,
454
- sourceMap : compilerOptions . sourceMap ,
455
- inlineSourceMap : compilerOptions . inlineSourceMap ,
496
+ // Emit maps when there is noEmit not set with forceDtsEmit
497
+ sourceMap : ( ! forceDtsEmit || ! compilerOptions . noEmit ) && compilerOptions . declarationMap ,
456
498
extendedDiagnostics : compilerOptions . extendedDiagnostics ,
457
499
onlyPrintJsDocStyle : true ,
458
500
writeBundleFileInfo : ! ! bundleBuildInfo ,
@@ -472,24 +514,19 @@ namespace ts {
472
514
const declBlocked = ( ! ! declarationTransform . diagnostics && ! ! declarationTransform . diagnostics . length ) || ! ! host . isEmitBlocked ( declarationFilePath ) || ! ! compilerOptions . noEmit ;
473
515
emitSkipped = emitSkipped || declBlocked ;
474
516
if ( ! declBlocked || forceDtsEmit ) {
475
- Debug . assert ( declarationTransform . transformed . length === 1 , "Should only see one output from the decl transform" ) ;
476
517
printSourceFileOrBundle (
477
518
declarationFilePath ,
478
519
declarationMapPath ,
479
- declarationTransform . transformed [ 0 ] ,
520
+ declarationTransform ,
480
521
declarationPrinter ,
481
522
{
482
- sourceMap : ! forceDtsEmit && compilerOptions . declarationMap ,
523
+ sourceMap : printerOptions . sourceMap ,
483
524
sourceRoot : compilerOptions . sourceRoot ,
484
525
mapRoot : compilerOptions . mapRoot ,
485
526
extendedDiagnostics : compilerOptions . extendedDiagnostics ,
486
527
// Explicitly do not passthru either `inline` option
487
528
}
488
529
) ;
489
- if ( forceDtsEmit && declarationTransform . transformed [ 0 ] . kind === SyntaxKind . SourceFile ) {
490
- const sourceFile = declarationTransform . transformed [ 0 ] ;
491
- exportedModulesFromDeclarationEmit = sourceFile . exportedModulesFromDeclarationEmit ;
492
- }
493
530
}
494
531
declarationTransform . dispose ( ) ;
495
532
if ( bundleBuildInfo ) bundleBuildInfo . dts = declarationPrinter . bundleFileInfo ;
@@ -509,7 +546,10 @@ namespace ts {
509
546
forEachChild ( node , collectLinkedAliases ) ;
510
547
}
511
548
512
- function printSourceFileOrBundle ( jsFilePath : string , sourceMapFilePath : string | undefined , sourceFileOrBundle : SourceFile | Bundle , printer : Printer , mapOptions : SourceMapOptions ) {
549
+ function printSourceFileOrBundle (
550
+ jsFilePath : string , sourceMapFilePath : string | undefined , transform : TransformationResult < SourceFile | Bundle > , printer : Printer , mapOptions : SourceMapOptions ) {
551
+ Debug . assert ( transform . transformed . length === 1 , "Should only see one output from the transform" ) ;
552
+ const sourceFileOrBundle = transform . transformed [ 0 ] ;
513
553
const bundle = sourceFileOrBundle . kind === SyntaxKind . Bundle ? sourceFileOrBundle : undefined ;
514
554
const sourceFile = sourceFileOrBundle . kind === SyntaxKind . SourceFile ? sourceFileOrBundle : undefined ;
515
555
const sourceFiles = bundle ? bundle . sourceFiles : [ sourceFile ! ] ;
@@ -532,9 +572,11 @@ namespace ts {
532
572
}
533
573
534
574
let sourceMapUrlPos ;
575
+ let sourceMap ;
576
+ let sourceMapData ;
535
577
if ( sourceMapGenerator ) {
536
578
if ( sourceMapDataList ) {
537
- sourceMapDataList . push ( {
579
+ sourceMapDataList . push ( sourceMapData = {
538
580
inputSourceFileNames : sourceMapGenerator . getSources ( ) ,
539
581
sourceMap : sourceMapGenerator . toJSON ( )
540
582
} ) ;
@@ -555,16 +597,39 @@ namespace ts {
555
597
556
598
// Write the source map
557
599
if ( sourceMapFilePath ) {
558
- const sourceMap = sourceMapGenerator . toString ( ) ;
559
- writeFile ( host , emitterDiagnostics , sourceMapFilePath , sourceMap , /*writeByteOrderMark*/ false , sourceFiles ) ;
600
+ sourceMap = sourceMapGenerator . toString ( ) ;
601
+ if ( ! forceDtsEmit ) writeFile ( host , emitterDiagnostics , sourceMapFilePath , sourceMap , /*writeByteOrderMark*/ false , sourceFiles ) ;
560
602
}
561
603
}
562
604
else {
563
605
writer . writeLine ( ) ;
564
606
}
565
607
566
608
// Write the output file
567
- writeFile ( host , emitterDiagnostics , jsFilePath , writer . getText ( ) , ! ! compilerOptions . emitBOM , sourceFiles , { sourceMapUrlPos } ) ;
609
+ writeFile (
610
+ host ,
611
+ emitterDiagnostics ,
612
+ jsFilePath ,
613
+ writer . getText ( ) ,
614
+ ! ! compilerOptions . emitBOM ,
615
+ sourceFiles ,
616
+ {
617
+ sourceMapUrlPos,
618
+ exportedModulesFromDeclarationEmit : sourceFile ?. exportedModulesFromDeclarationEmit ,
619
+ }
620
+ ) ;
621
+ if ( forceDtsEmit ) {
622
+ // Store the result
623
+ const key = getCacheDtsEmitResultKey ( sourceFileOrBundle , compilerOptions ) ;
624
+ host . getDtsEmitResultCache ( ) . set ( key , {
625
+ diagnostics : transform . diagnostics ,
626
+ text : writer . getText ( ) ,
627
+ sourceMap,
628
+ sourceMapData,
629
+ sourceMapUrlPos,
630
+ exportedModulesFromDeclarationEmit : sourceFile ?. exportedModulesFromDeclarationEmit ,
631
+ } ) ;
632
+ }
568
633
569
634
// Reset state
570
635
writer . clear ( ) ;
@@ -833,6 +898,7 @@ namespace ts {
833
898
getSourceFileFromReference : returnUndefined ,
834
899
redirectTargetsMap : createMultiMap ( ) ,
835
900
getFileIncludeReasons : notImplemented ,
901
+ getDtsEmitResultCache : ( ) => new Map ( ) ,
836
902
} ;
837
903
emitFiles (
838
904
notImplementedResolver ,
0 commit comments