@@ -434,25 +434,35 @@ namespace ts {
434434 return undefined ;
435435 }
436436
437+ function removeDiagnosticsOfLibraryFiles ( state : BuilderProgramState ) {
438+ if ( ! state . cleanedDiagnosticsOfLibFiles ) {
439+ state . cleanedDiagnosticsOfLibFiles = true ;
440+ const program = Debug . checkDefined ( state . program ) ;
441+ const options = program . getCompilerOptions ( ) ;
442+ forEach ( program . getSourceFiles ( ) , f =>
443+ program . isSourceFileDefaultLibrary ( f ) &&
444+ ! skipTypeChecking ( f , options , program ) &&
445+ removeSemanticDiagnosticsOf ( state , f . resolvedPath )
446+ ) ;
447+ }
448+ }
449+
437450 /**
438451 * Handles semantic diagnostics and dts emit for affectedFile and files, that are referencing modules that export entities from affected file
439452 * This is because even though js emit doesnt change, dts emit / type used can change resulting in need for dts emit and js change
440453 */
441- function handleDtsMayChangeOfAffectedFile ( state : BuilderProgramState , affectedFile : SourceFile , cancellationToken : CancellationToken | undefined , computeHash : BuilderState . ComputeHash , host : BuilderProgramHost ) {
454+ function handleDtsMayChangeOfAffectedFile (
455+ state : BuilderProgramState ,
456+ affectedFile : SourceFile ,
457+ cancellationToken : CancellationToken | undefined ,
458+ computeHash : BuilderState . ComputeHash ,
459+ host : BuilderProgramHost ,
460+ ) {
442461 removeSemanticDiagnosticsOf ( state , affectedFile . resolvedPath ) ;
443462
444463 // If affected files is everything except default library, then nothing more to do
445464 if ( state . allFilesExcludingDefaultLibraryFile === state . affectedFiles ) {
446- if ( ! state . cleanedDiagnosticsOfLibFiles ) {
447- state . cleanedDiagnosticsOfLibFiles = true ;
448- const program = Debug . checkDefined ( state . program ) ;
449- const options = program . getCompilerOptions ( ) ;
450- forEach ( program . getSourceFiles ( ) , f =>
451- program . isSourceFileDefaultLibrary ( f ) &&
452- ! skipTypeChecking ( f , options , program ) &&
453- removeSemanticDiagnosticsOf ( state , f . resolvedPath )
454- ) ;
455- }
465+ removeDiagnosticsOfLibraryFiles ( state ) ;
456466 // When a change affects the global scope, all files are considered to be affected without updating their signature
457467 // That means when affected file is handled, its signature can be out of date
458468 // To avoid this, ensure that we update the signature for any affected file in this scenario.
@@ -467,20 +477,22 @@ namespace ts {
467477 ) ;
468478 return ;
469479 }
470- else {
471- Debug . assert ( state . hasCalledUpdateShapeSignature . has ( affectedFile . resolvedPath ) || state . currentAffectedFilesSignatures ?. has ( affectedFile . resolvedPath ) , `Signature not updated for affected file: ${ affectedFile . fileName } ` ) ;
472- }
473-
474- if ( ! state . compilerOptions . assumeChangesOnlyAffectDirectDependencies ) {
475- forEachReferencingModulesOfExportOfAffectedFile ( state , affectedFile , ( state , path ) => handleDtsMayChangeOf ( state , path , cancellationToken , computeHash , host ) ) ;
476- }
480+ Debug . assert ( state . hasCalledUpdateShapeSignature . has ( affectedFile . resolvedPath ) || state . currentAffectedFilesSignatures ?. has ( affectedFile . resolvedPath ) , `Signature not updated for affected file: ${ affectedFile . fileName } ` ) ;
481+ if ( state . compilerOptions . assumeChangesOnlyAffectDirectDependencies ) return ;
482+ handleDtsMayChangeOfReferencingExportOfAffectedFile ( state , affectedFile , cancellationToken , computeHash , host ) ;
477483 }
478484
479485 /**
480486 * Handle the dts may change, so they need to be added to pending emit if dts emit is enabled,
481487 * Also we need to make sure signature is updated for these files
482488 */
483- function handleDtsMayChangeOf ( state : BuilderProgramState , path : Path , cancellationToken : CancellationToken | undefined , computeHash : BuilderState . ComputeHash , host : BuilderProgramHost ) : void {
489+ function handleDtsMayChangeOf (
490+ state : BuilderProgramState ,
491+ path : Path ,
492+ cancellationToken : CancellationToken | undefined ,
493+ computeHash : BuilderState . ComputeHash ,
494+ host : BuilderProgramHost
495+ ) : void {
484496 removeSemanticDiagnosticsOf ( state , path ) ;
485497
486498 if ( ! state . changedFilesSet . has ( path ) ) {
@@ -529,16 +541,61 @@ namespace ts {
529541 return newSignature !== oldSignature ;
530542 }
531543
544+ function forEachKeyOfExportedModulesMap < T > (
545+ state : BuilderProgramState ,
546+ filePath : Path ,
547+ fn : ( exportedFromPath : Path ) => T | undefined ,
548+ ) : T | undefined {
549+ // Go through exported modules from cache first
550+ let keys = state . currentAffectedFilesExportedModulesMap ! . getKeys ( filePath ) ;
551+ const result = keys && forEachKey ( keys , fn ) ;
552+ if ( result ) return result ;
553+
554+ // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
555+ keys = state . exportedModulesMap ! . getKeys ( filePath ) ;
556+ return keys && forEachKey ( keys , exportedFromPath =>
557+ // If the cache had an updated value, skip
558+ ! state . currentAffectedFilesExportedModulesMap ! . hasKey ( exportedFromPath ) &&
559+ ! state . currentAffectedFilesExportedModulesMap ! . deletedKeys ( ) ?. has ( exportedFromPath ) ?
560+ fn ( exportedFromPath ) :
561+ undefined
562+ ) ;
563+ }
564+
565+ function handleDtsMayChangeOfGlobalScope (
566+ state : BuilderProgramState ,
567+ filePath : Path ,
568+ cancellationToken : CancellationToken | undefined ,
569+ computeHash : BuilderState . ComputeHash ,
570+ host : BuilderProgramHost ,
571+ ) : boolean {
572+ if ( ! state . fileInfos . get ( filePath ) ?. affectsGlobalScope ) return false ;
573+ // Every file needs to be handled
574+ BuilderState . getAllFilesExcludingDefaultLibraryFile ( state , state . program ! , /*firstSourceFile*/ undefined )
575+ . forEach ( file => handleDtsMayChangeOf (
576+ state ,
577+ file . resolvedPath ,
578+ cancellationToken ,
579+ computeHash ,
580+ host ,
581+ ) ) ;
582+ removeDiagnosticsOfLibraryFiles ( state ) ;
583+ return true ;
584+ }
585+
532586 /**
533- * Iterate on referencing modules that export entities from affected file
587+ * Iterate on referencing modules that export entities from affected file and delete diagnostics and add pending emit
534588 */
535- function forEachReferencingModulesOfExportOfAffectedFile ( state : BuilderProgramState , affectedFile : SourceFile , fn : ( state : BuilderProgramState , filePath : Path ) => void ) {
589+ function handleDtsMayChangeOfReferencingExportOfAffectedFile (
590+ state : BuilderProgramState ,
591+ affectedFile : SourceFile ,
592+ cancellationToken : CancellationToken | undefined ,
593+ computeHash : BuilderState . ComputeHash ,
594+ host : BuilderProgramHost
595+ ) {
536596 // If there was change in signature (dts output) for the changed file,
537597 // then only we need to handle pending file emit
538- if ( ! state . exportedModulesMap || ! state . changedFilesSet . has ( affectedFile . resolvedPath ) ) {
539- return ;
540- }
541-
598+ if ( ! state . exportedModulesMap || ! state . changedFilesSet . has ( affectedFile . resolvedPath ) ) return ;
542599 if ( ! isChangedSignature ( state , affectedFile . resolvedPath ) ) return ;
543600
544601 // Since isolated modules dont change js files, files affected by change in signature is itself
@@ -551,7 +608,8 @@ namespace ts {
551608 const currentPath = queue . pop ( ) ! ;
552609 if ( ! seenFileNamesMap . has ( currentPath ) ) {
553610 seenFileNamesMap . set ( currentPath , true ) ;
554- fn ( state , currentPath ) ;
611+ if ( handleDtsMayChangeOfGlobalScope ( state , currentPath , cancellationToken , computeHash , host ) ) return ;
612+ handleDtsMayChangeOf ( state , currentPath , cancellationToken , computeHash , host ) ;
555613 if ( isChangedSignature ( state , currentPath ) ) {
556614 const currentSourceFile = Debug . checkDefined ( state . program ) . getSourceFileByPath ( currentPath ) ! ;
557615 queue . push ( ...BuilderState . getReferencedByPaths ( state , currentSourceFile . resolvedPath ) ) ;
@@ -561,65 +619,69 @@ namespace ts {
561619 }
562620
563621 Debug . assert ( ! ! state . currentAffectedFilesExportedModulesMap ) ;
564-
565622 const seenFileAndExportsOfFile = new Set < string > ( ) ;
566623 // Go through exported modules from cache first
567624 // If exported modules has path, all files referencing file exported from are affected
568- state . currentAffectedFilesExportedModulesMap . getKeys ( affectedFile . resolvedPath ) ?. forEach ( exportedFromPath =>
569- forEachFilesReferencingPath ( state , exportedFromPath , seenFileAndExportsOfFile , fn )
570- ) ;
571-
572- // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
573- state . exportedModulesMap . getKeys ( affectedFile . resolvedPath ) ?. forEach ( exportedFromPath =>
574- // If the cache had an updated value, skip
575- ! state . currentAffectedFilesExportedModulesMap ! . hasKey ( exportedFromPath ) &&
576- ! state . currentAffectedFilesExportedModulesMap ! . deletedKeys ( ) ?. has ( exportedFromPath ) &&
577- forEachFilesReferencingPath ( state , exportedFromPath , seenFileAndExportsOfFile , fn )
578- ) ;
579- }
580-
581- /**
582- * Iterate on files referencing referencedPath
583- */
584- function forEachFilesReferencingPath ( state : BuilderProgramState , referencedPath : Path , seenFileAndExportsOfFile : Set < string > , fn : ( state : BuilderProgramState , filePath : Path ) => void ) : void {
585- state . referencedMap ! . getKeys ( referencedPath ) ?. forEach ( filePath =>
586- forEachFileAndExportsOfFile ( state , filePath , seenFileAndExportsOfFile , fn )
587- ) ;
625+ forEachKeyOfExportedModulesMap ( state , affectedFile . resolvedPath , exportedFromPath => {
626+ if ( handleDtsMayChangeOfGlobalScope ( state , exportedFromPath , cancellationToken , computeHash , host ) ) return true ;
627+ const references = state . referencedMap ! . getKeys ( exportedFromPath ) ;
628+ return references && forEachKey ( references , filePath =>
629+ handleDtsMayChangeOfFileAndExportsOfFile (
630+ state ,
631+ filePath ,
632+ seenFileAndExportsOfFile ,
633+ cancellationToken ,
634+ computeHash ,
635+ host ,
636+ )
637+ ) ;
638+ } ) ;
588639 }
589640
590641 /**
591- * fn on file and iterate on anything that exports this file
642+ * handle dts and semantic diagnostics on file and iterate on anything that exports this file
643+ * return true when all work is done and we can exit handling dts emit and semantic diagnostics
592644 */
593- function forEachFileAndExportsOfFile ( state : BuilderProgramState , filePath : Path , seenFileAndExportsOfFile : Set < string > , fn : ( state : BuilderProgramState , filePath : Path ) => void ) : void {
594- if ( ! tryAddToSet ( seenFileAndExportsOfFile , filePath ) ) {
595- return ;
596- }
597-
598- fn ( state , filePath ) ;
599-
645+ function handleDtsMayChangeOfFileAndExportsOfFile (
646+ state : BuilderProgramState ,
647+ filePath : Path ,
648+ seenFileAndExportsOfFile : Set < string > ,
649+ cancellationToken : CancellationToken | undefined ,
650+ computeHash : BuilderState . ComputeHash ,
651+ host : BuilderProgramHost ,
652+ ) : boolean | undefined {
653+ if ( ! tryAddToSet ( seenFileAndExportsOfFile , filePath ) ) return undefined ;
654+
655+ if ( handleDtsMayChangeOfGlobalScope ( state , filePath , cancellationToken , computeHash , host ) ) return true ;
656+ handleDtsMayChangeOf ( state , filePath , cancellationToken , computeHash , host ) ;
600657 Debug . assert ( ! ! state . currentAffectedFilesExportedModulesMap ) ;
601- // Go through exported modules from cache first
602- // If exported modules has path, all files referencing file exported from are affected
603- state . currentAffectedFilesExportedModulesMap . getKeys ( filePath ) ?. forEach ( exportedFromPath =>
604- forEachFileAndExportsOfFile ( state , exportedFromPath , seenFileAndExportsOfFile , fn )
605- ) ;
606658
607- // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
608- state . exportedModulesMap ! . getKeys ( filePath ) ?. forEach ( exportedFromPath =>
609- // If the cache had an updated value, skip
610- ! state . currentAffectedFilesExportedModulesMap ! . hasKey ( exportedFromPath ) &&
611- ! state . currentAffectedFilesExportedModulesMap ! . deletedKeys ( ) ?. has ( exportedFromPath ) &&
612- forEachFileAndExportsOfFile ( state , exportedFromPath , seenFileAndExportsOfFile , fn )
659+ // If exported modules has path, all files referencing file exported from are affected
660+ forEachKeyOfExportedModulesMap ( state , filePath , exportedFromPath =>
661+ handleDtsMayChangeOfFileAndExportsOfFile (
662+ state ,
663+ exportedFromPath ,
664+ seenFileAndExportsOfFile ,
665+ cancellationToken ,
666+ computeHash ,
667+ host ,
668+ )
613669 ) ;
614670
615671 // Remove diagnostics of files that import this file (without going to exports of referencing files)
616672 state . referencedMap ! . getKeys ( filePath ) ?. forEach ( referencingFilePath =>
617673 ! seenFileAndExportsOfFile . has ( referencingFilePath ) && // Not already removed diagnostic file
618- fn ( state , referencingFilePath ) // Dont add to seen since this is not yet done with the export removal
674+ handleDtsMayChangeOf ( // Dont add to seen since this is not yet done with the export removal
675+ state ,
676+ referencingFilePath ,
677+ cancellationToken ,
678+ computeHash ,
679+ host ,
680+ )
619681 ) ;
682+ return undefined ;
620683 }
621684
622-
623685 /**
624686 * This is called after completing operation on the next affected file.
625687 * The operations here are postponed to ensure that cancellation during the iteration is handled correctly
0 commit comments