@@ -35,9 +35,11 @@ namespace ts {
35
35
* Map from config file name to up-to-date status
36
36
*/
37
37
projectStatus : FileMap < UpToDateStatus > ;
38
+ diagnostics ?: FileMap < number > ; // TODO(shkamat): this should be really be diagnostics but thats for later time
38
39
39
- invalidatedProjects : FileMap < true > ;
40
- queuedProjects : FileMap < true > ;
40
+ invalidateProject ( project : ResolvedConfigFileName , dependencyGraph : DependencyGraph | undefined ) : void ;
41
+ getNextInvalidatedProject ( ) : ResolvedConfigFileName | undefined ;
42
+ pendingInvalidatedProjects ( ) : boolean ;
41
43
missingRoots : Map < true > ;
42
44
}
43
45
@@ -194,6 +196,7 @@ namespace ts {
194
196
hasKey ( fileName : string ) : boolean ;
195
197
removeKey ( fileName : string ) : void ;
196
198
getKeys ( ) : string [ ] ;
199
+ getSize ( ) : number ;
197
200
}
198
201
199
202
/**
@@ -209,7 +212,8 @@ namespace ts {
209
212
getValueOrUndefined,
210
213
removeKey,
211
214
getKeys,
212
- hasKey
215
+ hasKey,
216
+ getSize
213
217
} ;
214
218
215
219
function getKeys ( ) : string [ ] {
@@ -242,6 +246,10 @@ namespace ts {
242
246
const f = normalizePath ( fileName ) ;
243
247
return lookup . get ( f ) ;
244
248
}
249
+
250
+ function getSize ( ) {
251
+ return lookup . size ;
252
+ }
245
253
}
246
254
247
255
function createDependencyMapper ( ) {
@@ -375,18 +383,64 @@ namespace ts {
375
383
}
376
384
377
385
export function createBuildContext ( options : BuildOptions ) : BuildContext {
378
- const invalidatedProjects = createFileMap < true > ( ) ;
379
- const queuedProjects = createFileMap < true > ( ) ;
386
+ const invalidatedProjectQueue = [ ] as ResolvedConfigFileName [ ] ;
387
+ let nextIndex = 0 ;
388
+ const projectPendingBuild = createFileMap < true > ( ) ;
380
389
const missingRoots = createMap < true > ( ) ;
390
+ const diagnostics = options . watch ? createFileMap < number > ( ) : undefined ;
381
391
382
392
return {
383
393
options,
384
394
projectStatus : createFileMap ( ) ,
395
+ diagnostics,
385
396
unchangedOutputs : createFileMap ( ) ,
386
- invalidatedProjects,
387
- missingRoots,
388
- queuedProjects
397
+ invalidateProject,
398
+ getNextInvalidatedProject,
399
+ pendingInvalidatedProjects,
400
+ missingRoots
389
401
} ;
402
+
403
+ function invalidateProject ( proj : ResolvedConfigFileName , dependancyGraph : DependencyGraph | undefined ) {
404
+ if ( ! projectPendingBuild . hasKey ( proj ) ) {
405
+ addProjToQueue ( proj ) ;
406
+ if ( dependancyGraph ) {
407
+ queueBuildForDownstreamReferences ( proj , dependancyGraph ) ;
408
+ }
409
+ }
410
+ }
411
+
412
+ function addProjToQueue ( proj : ResolvedConfigFileName ) {
413
+ projectPendingBuild . setValue ( proj , true ) ;
414
+ invalidatedProjectQueue . push ( proj ) ;
415
+ }
416
+
417
+ function getNextInvalidatedProject ( ) {
418
+ if ( nextIndex < invalidatedProjectQueue . length ) {
419
+ const proj = invalidatedProjectQueue [ nextIndex ] ;
420
+ nextIndex ++ ;
421
+ projectPendingBuild . removeKey ( proj ) ;
422
+ if ( ! projectPendingBuild . getSize ( ) ) {
423
+ invalidatedProjectQueue . length = 0 ;
424
+ }
425
+ return proj ;
426
+ }
427
+ }
428
+
429
+ function pendingInvalidatedProjects ( ) {
430
+ return ! ! projectPendingBuild . getSize ( ) ;
431
+ }
432
+
433
+ // Mark all downstream projects of this one needing to be built "later"
434
+ function queueBuildForDownstreamReferences ( root : ResolvedConfigFileName , dependancyGraph : DependencyGraph ) {
435
+ const deps = dependancyGraph . dependencyMap . getReferencesTo ( root ) ;
436
+ for ( const ref of deps ) {
437
+ // Can skip circular references
438
+ if ( ! projectPendingBuild . hasKey ( ref ) ) {
439
+ addProjToQueue ( ref ) ;
440
+ queueBuildForDownstreamReferences ( ref , dependancyGraph ) ;
441
+ }
442
+ }
443
+ }
390
444
}
391
445
392
446
export interface SolutionBuilderHost extends CompilerHost {
@@ -443,6 +497,7 @@ namespace ts {
443
497
const hostWithWatch = host as SolutionBuilderWithWatchHost ;
444
498
const configFileCache = createConfigFileCache ( host ) ;
445
499
let context = createBuildContext ( defaultOptions ) ;
500
+ let timerToBuildInvalidatedProject : any ;
446
501
447
502
const existingWatchersForWildcards = createMap < WildcardDirectoryWatcher > ( ) ;
448
503
return {
@@ -454,8 +509,7 @@ namespace ts {
454
509
getBuildGraph,
455
510
456
511
invalidateProject,
457
- buildInvalidatedProjects,
458
- buildDependentInvalidatedProjects,
512
+ buildInvalidatedProject,
459
513
460
514
resolveProjectName,
461
515
@@ -466,7 +520,19 @@ namespace ts {
466
520
host . reportSolutionBuilderStatus ( createCompilerDiagnostic ( message , ...args ) ) ;
467
521
}
468
522
469
- function reportWatchStatus ( message : DiagnosticMessage , ...args : string [ ] ) {
523
+ function storeErrors ( proj : ResolvedConfigFileName , diagnostics : ReadonlyArray < Diagnostic > ) {
524
+ if ( context . options . watch ) {
525
+ storeErrorSummary ( proj , diagnostics . filter ( diagnostic => diagnostic . category === DiagnosticCategory . Error ) . length ) ;
526
+ }
527
+ }
528
+
529
+ function storeErrorSummary ( proj : ResolvedConfigFileName , errorCount : number ) {
530
+ if ( context . options . watch ) {
531
+ context . diagnostics ! . setValue ( proj , errorCount ) ;
532
+ }
533
+ }
534
+
535
+ function reportWatchStatus ( message : DiagnosticMessage , ...args : ( string | number | undefined ) [ ] ) {
470
536
if ( hostWithWatch . onWatchStatusChange ) {
471
537
hostWithWatch . onWatchStatusChange ( createCompilerDiagnostic ( message , ...args ) , host . getNewLine ( ) , { preserveWatchOutput : context . options . preserveWatchOutput } ) ;
472
538
}
@@ -506,15 +572,12 @@ namespace ts {
506
572
}
507
573
}
508
574
509
- function invalidateProjectAndScheduleBuilds ( resolved : ResolvedConfigFileName ) {
510
- reportWatchStatus ( Diagnostics . File_change_detected_Starting_incremental_compilation ) ;
511
- invalidateProject ( resolved ) ;
512
- if ( ! hostWithWatch . setTimeout ) {
513
- return ;
514
- }
515
- hostWithWatch . setTimeout ( buildInvalidatedProjects , 100 ) ;
516
- hostWithWatch . setTimeout ( buildDependentInvalidatedProjects , 3000 ) ;
517
- }
575
+ }
576
+
577
+ function invalidateProjectAndScheduleBuilds ( resolved : ResolvedConfigFileName ) {
578
+ reportWatchStatus ( Diagnostics . File_change_detected_Starting_incremental_compilation ) ;
579
+ invalidateProject ( resolved ) ;
580
+ scheduleBuildInvalidatedProject ( ) ;
518
581
}
519
582
520
583
function resetBuildContext ( opts = defaultOptions ) {
@@ -725,33 +788,44 @@ namespace ts {
725
788
}
726
789
727
790
configFileCache . removeKey ( resolved ) ;
728
- context . invalidatedProjects . setValue ( resolved , true ) ;
729
791
context . projectStatus . removeKey ( resolved ) ;
730
-
731
- const graph = getGlobalDependencyGraph ( ) ! ;
732
- if ( graph ) {
733
- queueBuildForDownstreamReferences ( resolved ) ;
792
+ if ( context . options . watch ) {
793
+ context . diagnostics ! . removeKey ( resolved ) ;
734
794
}
735
795
736
- // Mark all downstream projects of this one needing to be built "later"
737
- function queueBuildForDownstreamReferences ( root : ResolvedConfigFileName ) {
738
- const deps = graph . dependencyMap . getReferencesTo ( root ) ;
739
- for ( const ref of deps ) {
740
- // Can skip circular references
741
- if ( ! context . queuedProjects . hasKey ( ref ) ) {
742
- context . queuedProjects . setValue ( ref , true ) ;
743
- queueBuildForDownstreamReferences ( ref ) ;
744
- }
745
- }
796
+ context . invalidateProject ( resolved , getGlobalDependencyGraph ( ) ) ;
797
+ }
798
+
799
+ function scheduleBuildInvalidatedProject ( ) {
800
+ if ( ! hostWithWatch . setTimeout || ! hostWithWatch . clearTimeout ) {
801
+ return ;
746
802
}
803
+ if ( timerToBuildInvalidatedProject ) {
804
+ hostWithWatch . clearTimeout ( timerToBuildInvalidatedProject ) ;
805
+ }
806
+ timerToBuildInvalidatedProject = hostWithWatch . setTimeout ( buildInvalidatedProject , 250 ) ;
747
807
}
748
808
749
- function buildInvalidatedProjects ( ) {
750
- buildSomeProjects ( p => context . invalidatedProjects . hasKey ( p ) ) ;
809
+ function buildInvalidatedProject ( ) {
810
+ timerToBuildInvalidatedProject = undefined ;
811
+ const buildProject = context . getNextInvalidatedProject ( ) ;
812
+ buildSomeProjects ( p => p === buildProject ) ;
813
+ if ( context . pendingInvalidatedProjects ( ) ) {
814
+ if ( ! timerToBuildInvalidatedProject ) {
815
+ scheduleBuildInvalidatedProject ( ) ;
816
+ }
817
+ }
818
+ else {
819
+ reportErrorSummary ( ) ;
820
+ }
751
821
}
752
822
753
- function buildDependentInvalidatedProjects ( ) {
754
- buildSomeProjects ( p => context . queuedProjects . hasKey ( p ) ) ;
823
+ function reportErrorSummary ( ) {
824
+ if ( context . options . watch ) {
825
+ let errorCount = 0 ;
826
+ context . diagnostics ! . getKeys ( ) . forEach ( resolved => errorCount += context . diagnostics ! . getValue ( resolved ) ) ;
827
+ reportWatchStatus ( errorCount === 1 ? Diagnostics . Found_1_error_Watching_for_file_changes : Diagnostics . Found_0_errors_Watching_for_file_changes , errorCount ) ;
828
+ }
755
829
}
756
830
757
831
function buildSomeProjects ( predicate : ( projName : ResolvedConfigFileName ) => boolean ) {
@@ -808,6 +882,7 @@ namespace ts {
808
882
if ( temporaryMarks [ projPath ] ) {
809
883
if ( ! inCircularContext ) {
810
884
hadError = true ;
885
+ // TODO(shkamat): Account for this error
811
886
reportStatus ( Diagnostics . Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0 , circularityReportStack . join ( "\r\n" ) ) ;
812
887
return ;
813
888
}
@@ -853,6 +928,7 @@ namespace ts {
853
928
if ( ! configFile ) {
854
929
// Failed to read the config file
855
930
resultFlags |= BuildResultFlags . ConfigFileErrors ;
931
+ storeErrorSummary ( proj , 1 ) ;
856
932
context . projectStatus . setValue ( proj , { type : UpToDateStatusType . Unbuildable , reason : "Config file errors" } ) ;
857
933
return resultFlags ;
858
934
}
@@ -880,6 +956,7 @@ namespace ts {
880
956
for ( const diag of syntaxDiagnostics ) {
881
957
host . reportDiagnostic ( diag ) ;
882
958
}
959
+ storeErrors ( proj , syntaxDiagnostics ) ;
883
960
context . projectStatus . setValue ( proj , { type : UpToDateStatusType . Unbuildable , reason : "Syntactic errors" } ) ;
884
961
return resultFlags ;
885
962
}
@@ -892,6 +969,7 @@ namespace ts {
892
969
for ( const diag of declDiagnostics ) {
893
970
host . reportDiagnostic ( diag ) ;
894
971
}
972
+ storeErrors ( proj , declDiagnostics ) ;
895
973
context . projectStatus . setValue ( proj , { type : UpToDateStatusType . Unbuildable , reason : "Declaration file errors" } ) ;
896
974
return resultFlags ;
897
975
}
@@ -904,6 +982,7 @@ namespace ts {
904
982
for ( const diag of semanticDiagnostics ) {
905
983
host . reportDiagnostic ( diag ) ;
906
984
}
985
+ storeErrors ( proj , semanticDiagnostics ) ;
907
986
context . projectStatus . setValue ( proj , { type : UpToDateStatusType . Unbuildable , reason : "Semantic errors" } ) ;
908
987
return resultFlags ;
909
988
}
@@ -1029,6 +1108,7 @@ namespace ts {
1029
1108
if ( host . fileExists ( fullPathWithTsconfig ) ) {
1030
1109
return fullPathWithTsconfig as ResolvedConfigFileName ;
1031
1110
}
1111
+ // TODO(shkamat): right now this is accounted as 1 error in config file, but we need to do better
1032
1112
host . reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . File_0_not_found , relName ( fullPath ) ) ) ;
1033
1113
return undefined ;
1034
1114
}
@@ -1048,7 +1128,10 @@ namespace ts {
1048
1128
function buildAllProjects ( ) : ExitStatus {
1049
1129
if ( context . options . watch ) { reportWatchStatus ( Diagnostics . Starting_compilation_in_watch_mode ) ; }
1050
1130
const graph = getGlobalDependencyGraph ( ) ;
1051
- if ( graph === undefined ) return ExitStatus . DiagnosticsPresent_OutputsSkipped ;
1131
+ if ( graph === undefined ) {
1132
+ reportErrorSummary ( ) ;
1133
+ return ExitStatus . DiagnosticsPresent_OutputsSkipped ;
1134
+ }
1052
1135
1053
1136
const queue = graph . buildQueue ;
1054
1137
reportBuildQueue ( graph ) ;
@@ -1092,6 +1175,7 @@ namespace ts {
1092
1175
const buildResult = buildSingleProject ( next ) ;
1093
1176
anyFailed = anyFailed || ! ! ( buildResult & BuildResultFlags . AnyErrors ) ;
1094
1177
}
1178
+ reportErrorSummary ( ) ;
1095
1179
return anyFailed ? ExitStatus . DiagnosticsPresent_OutputsSkipped : ExitStatus . Success ;
1096
1180
}
1097
1181
0 commit comments