@@ -520,16 +520,6 @@ namespace ts {
520
520
let context = createBuildContext ( defaultOptions ) ;
521
521
522
522
const existingWatchersForWildcards = createMap < WildcardDirectoryWatcher > ( ) ;
523
-
524
- const upToDateHost : UpToDateHost = {
525
- fileExists : fileName => compilerHost . fileExists ( fileName ) ,
526
- getModifiedTime : fileName => compilerHost . getModifiedTime ! ( fileName ) ,
527
- getUnchangedTime : fileName => context . unchangedOutputs . getValueOrUndefined ( fileName ) ,
528
- getLastStatus : fileName => context . projectStatus . getValueOrUndefined ( fileName ) ,
529
- setLastStatus : ( fileName , status ) => context . projectStatus . setValue ( fileName , status ) ,
530
- parseConfigFile : configFilePath => configFileCache . parseConfigFile ( configFilePath )
531
- } ;
532
-
533
523
return {
534
524
buildAllProjects,
535
525
getUpToDateStatus,
@@ -611,7 +601,180 @@ namespace ts {
611
601
}
612
602
613
603
function getUpToDateStatus ( project : ParsedCommandLine | undefined ) : UpToDateStatus {
614
- return ts . getUpToDateStatus ( upToDateHost , project ) ;
604
+ if ( project === undefined ) {
605
+ return { type : UpToDateStatusType . Unbuildable , reason : "File deleted mid-build" } ;
606
+ }
607
+
608
+ const prior = context . projectStatus . getValueOrUndefined ( project . options . configFilePath ! ) ;
609
+ if ( prior !== undefined ) {
610
+ return prior ;
611
+ }
612
+
613
+ const actual = getUpToDateStatusWorker ( project ) ;
614
+ context . projectStatus . setValue ( project . options . configFilePath ! , actual ) ;
615
+ return actual ;
616
+ }
617
+
618
+ function getUpToDateStatusWorker ( project : ParsedCommandLine ) : UpToDateStatus {
619
+ let newestInputFileName : string = undefined ! ;
620
+ let newestInputFileTime = minimumDate ;
621
+ // Get timestamps of input files
622
+ for ( const inputFile of project . fileNames ) {
623
+ if ( ! compilerHost . fileExists ( inputFile ) ) {
624
+ return {
625
+ type : UpToDateStatusType . Unbuildable ,
626
+ reason : `${ inputFile } does not exist`
627
+ } ;
628
+ }
629
+
630
+ const inputTime = compilerHost . getModifiedTime ! ( inputFile ) || missingFileModifiedTime ;
631
+ if ( inputTime > newestInputFileTime ) {
632
+ newestInputFileName = inputFile ;
633
+ newestInputFileTime = inputTime ;
634
+ }
635
+ }
636
+
637
+ // Collect the expected outputs of this project
638
+ const outputs = getAllProjectOutputs ( project ) ;
639
+
640
+ if ( outputs . length === 0 ) {
641
+ return {
642
+ type : UpToDateStatusType . ContainerOnly
643
+ } ;
644
+ }
645
+
646
+ // Now see if all outputs are newer than the newest input
647
+ let oldestOutputFileName = "(none)" ;
648
+ let oldestOutputFileTime = maximumDate ;
649
+ let newestOutputFileName = "(none)" ;
650
+ let newestOutputFileTime = minimumDate ;
651
+ let missingOutputFileName : string | undefined ;
652
+ let newestDeclarationFileContentChangedTime = minimumDate ;
653
+ let isOutOfDateWithInputs = false ;
654
+ for ( const output of outputs ) {
655
+ // Output is missing; can stop checking
656
+ // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
657
+ if ( ! compilerHost . fileExists ( output ) ) {
658
+ missingOutputFileName = output ;
659
+ break ;
660
+ }
661
+
662
+ const outputTime = compilerHost . getModifiedTime ! ( output ) || missingFileModifiedTime ;
663
+ if ( outputTime < oldestOutputFileTime ) {
664
+ oldestOutputFileTime = outputTime ;
665
+ oldestOutputFileName = output ;
666
+ }
667
+
668
+ // If an output is older than the newest input, we can stop checking
669
+ // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
670
+ if ( outputTime < newestInputFileTime ) {
671
+ isOutOfDateWithInputs = true ;
672
+ break ;
673
+ }
674
+
675
+ if ( outputTime > newestOutputFileTime ) {
676
+ newestOutputFileTime = outputTime ;
677
+ newestOutputFileName = output ;
678
+ }
679
+
680
+ // Keep track of when the most recent time a .d.ts file was changed.
681
+ // In addition to file timestamps, we also keep track of when a .d.ts file
682
+ // had its file touched but not had its contents changed - this allows us
683
+ // to skip a downstream typecheck
684
+ if ( isDeclarationFile ( output ) ) {
685
+ const unchangedTime = context . unchangedOutputs . getValueOrUndefined ( output ) ;
686
+ if ( unchangedTime !== undefined ) {
687
+ newestDeclarationFileContentChangedTime = newer ( unchangedTime , newestDeclarationFileContentChangedTime ) ;
688
+ }
689
+ else {
690
+ const outputModifiedTime = compilerHost . getModifiedTime ! ( output ) || missingFileModifiedTime ;
691
+ newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , outputModifiedTime ) ;
692
+ }
693
+ }
694
+ }
695
+
696
+ let pseudoUpToDate = false ;
697
+ let usesPrepend = false ;
698
+ let upstreamChangedProject : string | undefined ;
699
+ if ( project . projectReferences ) {
700
+ for ( const ref of project . projectReferences ) {
701
+ usesPrepend = usesPrepend || ! ! ( ref . prepend ) ;
702
+ const resolvedRef = resolveProjectReferencePath ( compilerHost , ref ) ;
703
+ const refStatus = getUpToDateStatus ( configFileCache . parseConfigFile ( resolvedRef ) ) ;
704
+
705
+ // An upstream project is blocked
706
+ if ( refStatus . type === UpToDateStatusType . Unbuildable ) {
707
+ return {
708
+ type : UpToDateStatusType . UpstreamBlocked ,
709
+ upstreamProjectName : ref . path
710
+ } ;
711
+ }
712
+
713
+ // If the upstream project is out of date, then so are we (someone shouldn't have asked, though?)
714
+ if ( refStatus . type !== UpToDateStatusType . UpToDate ) {
715
+ return {
716
+ type : UpToDateStatusType . UpstreamOutOfDate ,
717
+ upstreamProjectName : ref . path
718
+ } ;
719
+ }
720
+
721
+ // If the upstream project's newest file is older than our oldest output, we
722
+ // can't be out of date because of it
723
+ if ( refStatus . newestInputFileTime && refStatus . newestInputFileTime <= oldestOutputFileTime ) {
724
+ continue ;
725
+ }
726
+
727
+ // If the upstream project has only change .d.ts files, and we've built
728
+ // *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
729
+ if ( refStatus . newestDeclarationFileContentChangedTime && refStatus . newestDeclarationFileContentChangedTime <= oldestOutputFileTime ) {
730
+ pseudoUpToDate = true ;
731
+ upstreamChangedProject = ref . path ;
732
+ continue ;
733
+ }
734
+
735
+ // We have an output older than an upstream output - we are out of date
736
+ Debug . assert ( oldestOutputFileName !== undefined , "Should have an oldest output filename here" ) ;
737
+ return {
738
+ type : UpToDateStatusType . OutOfDateWithUpstream ,
739
+ outOfDateOutputFileName : oldestOutputFileName ,
740
+ newerProjectName : ref . path
741
+ } ;
742
+ }
743
+ }
744
+
745
+ if ( missingOutputFileName !== undefined ) {
746
+ return {
747
+ type : UpToDateStatusType . OutputMissing ,
748
+ missingOutputFileName
749
+ } ;
750
+ }
751
+
752
+ if ( isOutOfDateWithInputs ) {
753
+ return {
754
+ type : UpToDateStatusType . OutOfDateWithSelf ,
755
+ outOfDateOutputFileName : oldestOutputFileName ,
756
+ newerInputFileName : newestInputFileName
757
+ } ;
758
+ }
759
+
760
+ if ( usesPrepend && pseudoUpToDate ) {
761
+ return {
762
+ type : UpToDateStatusType . OutOfDateWithUpstream ,
763
+ outOfDateOutputFileName : oldestOutputFileName ,
764
+ newerProjectName : upstreamChangedProject !
765
+ } ;
766
+ }
767
+
768
+ // Up to date
769
+ return {
770
+ type : pseudoUpToDate ? UpToDateStatusType . UpToDateWithUpstreamTypes : UpToDateStatusType . UpToDate ,
771
+ newestDeclarationFileContentChangedTime,
772
+ newestInputFileTime,
773
+ newestOutputFileTime,
774
+ newestInputFileName,
775
+ newestOutputFileName,
776
+ oldestOutputFileName
777
+ } ;
615
778
}
616
779
617
780
function invalidateProject ( configFileName : string ) {
@@ -1030,187 +1193,13 @@ namespace ts {
1030
1193
}
1031
1194
}
1032
1195
1033
- /**
1034
- * Gets the UpToDateStatus for a project
1035
- */
1036
- export function getUpToDateStatus ( host : UpToDateHost , project : ParsedCommandLine | undefined ) : UpToDateStatus {
1037
- if ( project === undefined ) {
1038
- return { type : UpToDateStatusType . Unbuildable , reason : "File deleted mid-build" } ;
1039
- }
1040
-
1041
- const prior = host . getLastStatus ? host . getLastStatus ( project . options . configFilePath ! ) : undefined ;
1042
- if ( prior !== undefined ) {
1043
- return prior ;
1044
- }
1045
-
1046
- const actual = getUpToDateStatusWorker ( host , project ) ;
1047
- if ( host . setLastStatus ) {
1048
- host . setLastStatus ( project . options . configFilePath ! , actual ) ;
1049
- }
1050
-
1051
- return actual ;
1052
- }
1053
-
1054
- function getUpToDateStatusWorker ( host : UpToDateHost , project : ParsedCommandLine ) : UpToDateStatus {
1055
- let newestInputFileName : string = undefined ! ;
1056
- let newestInputFileTime = minimumDate ;
1057
- // Get timestamps of input files
1058
- for ( const inputFile of project . fileNames ) {
1059
- if ( ! host . fileExists ( inputFile ) ) {
1060
- return {
1061
- type : UpToDateStatusType . Unbuildable ,
1062
- reason : `${ inputFile } does not exist`
1063
- } ;
1064
- }
1065
-
1066
- const inputTime = host . getModifiedTime ( inputFile ) || missingFileModifiedTime ;
1067
- if ( inputTime > newestInputFileTime ) {
1068
- newestInputFileName = inputFile ;
1069
- newestInputFileTime = inputTime ;
1070
- }
1071
- }
1072
-
1073
- // Collect the expected outputs of this project
1074
- const outputs = getAllProjectOutputs ( project ) ;
1075
-
1076
- if ( outputs . length === 0 ) {
1077
- return {
1078
- type : UpToDateStatusType . ContainerOnly
1079
- } ;
1080
- }
1081
-
1082
- // Now see if all outputs are newer than the newest input
1083
- let oldestOutputFileName = "(none)" ;
1084
- let oldestOutputFileTime = maximumDate ;
1085
- let newestOutputFileName = "(none)" ;
1086
- let newestOutputFileTime = minimumDate ;
1087
- let missingOutputFileName : string | undefined ;
1088
- let newestDeclarationFileContentChangedTime = minimumDate ;
1089
- let isOutOfDateWithInputs = false ;
1090
- for ( const output of outputs ) {
1091
- // Output is missing; can stop checking
1092
- // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
1093
- if ( ! host . fileExists ( output ) ) {
1094
- missingOutputFileName = output ;
1095
- break ;
1096
- }
1097
-
1098
- const outputTime = host . getModifiedTime ( output ) || missingFileModifiedTime ;
1099
- if ( outputTime < oldestOutputFileTime ) {
1100
- oldestOutputFileTime = outputTime ;
1101
- oldestOutputFileName = output ;
1102
- }
1103
-
1104
- // If an output is older than the newest input, we can stop checking
1105
- // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status
1106
- if ( outputTime < newestInputFileTime ) {
1107
- isOutOfDateWithInputs = true ;
1108
- break ;
1109
- }
1110
-
1111
- if ( outputTime > newestOutputFileTime ) {
1112
- newestOutputFileTime = outputTime ;
1113
- newestOutputFileName = output ;
1114
- }
1115
-
1116
- // Keep track of when the most recent time a .d.ts file was changed.
1117
- // In addition to file timestamps, we also keep track of when a .d.ts file
1118
- // had its file touched but not had its contents changed - this allows us
1119
- // to skip a downstream typecheck
1120
- if ( isDeclarationFile ( output ) ) {
1121
- const unchangedTime = host . getUnchangedTime ? host . getUnchangedTime ( output ) : undefined ;
1122
- if ( unchangedTime !== undefined ) {
1123
- newestDeclarationFileContentChangedTime = newer ( unchangedTime , newestDeclarationFileContentChangedTime ) ;
1124
- }
1125
- else {
1126
- const outputModifiedTime = host . getModifiedTime ( output ) || missingFileModifiedTime ;
1127
- newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , outputModifiedTime ) ;
1128
- }
1129
- }
1130
- }
1131
-
1132
- let pseudoUpToDate = false ;
1133
- let usesPrepend = false ;
1134
- let upstreamChangedProject : string | undefined ;
1135
- if ( project . projectReferences && host . parseConfigFile ) {
1136
- for ( const ref of project . projectReferences ) {
1137
- usesPrepend = usesPrepend || ! ! ( ref . prepend ) ;
1138
- const resolvedRef = resolveProjectReferencePath ( host , ref ) ;
1139
- const refStatus = getUpToDateStatus ( host , host . parseConfigFile ( resolvedRef ) ) ;
1140
-
1141
- // An upstream project is blocked
1142
- if ( refStatus . type === UpToDateStatusType . Unbuildable ) {
1143
- return {
1144
- type : UpToDateStatusType . UpstreamBlocked ,
1145
- upstreamProjectName : ref . path
1146
- } ;
1147
- }
1148
-
1149
- // If the upstream project is out of date, then so are we (someone shouldn't have asked, though?)
1150
- if ( refStatus . type !== UpToDateStatusType . UpToDate ) {
1151
- return {
1152
- type : UpToDateStatusType . UpstreamOutOfDate ,
1153
- upstreamProjectName : ref . path
1154
- } ;
1155
- }
1156
-
1157
- // If the upstream project's newest file is older than our oldest output, we
1158
- // can't be out of date because of it
1159
- if ( refStatus . newestInputFileTime && refStatus . newestInputFileTime <= oldestOutputFileTime ) {
1160
- continue ;
1161
- }
1162
-
1163
- // If the upstream project has only change .d.ts files, and we've built
1164
- // *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
1165
- if ( refStatus . newestDeclarationFileContentChangedTime && refStatus . newestDeclarationFileContentChangedTime <= oldestOutputFileTime ) {
1166
- pseudoUpToDate = true ;
1167
- upstreamChangedProject = ref . path ;
1168
- continue ;
1169
- }
1170
-
1171
- // We have an output older than an upstream output - we are out of date
1172
- Debug . assert ( oldestOutputFileName !== undefined , "Should have an oldest output filename here" ) ;
1173
- return {
1174
- type : UpToDateStatusType . OutOfDateWithUpstream ,
1175
- outOfDateOutputFileName : oldestOutputFileName ,
1176
- newerProjectName : ref . path
1177
- } ;
1178
- }
1179
- }
1180
-
1181
- if ( missingOutputFileName !== undefined ) {
1182
- return {
1183
- type : UpToDateStatusType . OutputMissing ,
1184
- missingOutputFileName
1185
- } ;
1186
- }
1187
-
1188
- if ( isOutOfDateWithInputs ) {
1189
- return {
1190
- type : UpToDateStatusType . OutOfDateWithSelf ,
1191
- outOfDateOutputFileName : oldestOutputFileName ,
1192
- newerInputFileName : newestInputFileName
1193
- } ;
1194
- }
1195
-
1196
- if ( usesPrepend && pseudoUpToDate ) {
1197
- return {
1198
- type : UpToDateStatusType . OutOfDateWithUpstream ,
1199
- outOfDateOutputFileName : oldestOutputFileName ,
1200
- newerProjectName : upstreamChangedProject !
1201
- } ;
1202
- }
1203
-
1204
- // Up to date
1205
- return {
1206
- type : pseudoUpToDate ? UpToDateStatusType . UpToDateWithUpstreamTypes : UpToDateStatusType . UpToDate ,
1207
- newestDeclarationFileContentChangedTime,
1208
- newestInputFileTime,
1209
- newestOutputFileTime,
1210
- newestInputFileName,
1211
- newestOutputFileName,
1212
- oldestOutputFileName
1213
- } ;
1196
+ export interface UpToDateHost {
1197
+ fileExists ( fileName : string ) : boolean ;
1198
+ getModifiedTime ( fileName : string ) : Date | undefined ;
1199
+ getUnchangedTime ?( fileName : string ) : Date | undefined ;
1200
+ getLastStatus ?( fileName : string ) : UpToDateStatus | undefined ;
1201
+ setLastStatus ?( fileName : string , status : UpToDateStatus ) : void ;
1202
+ parseConfigFile ?( configFilePath : ResolvedConfigFileName ) : ParsedCommandLine | undefined ;
1214
1203
}
1215
1204
1216
1205
export function getAllProjectOutputs ( project : ParsedCommandLine ) : ReadonlyArray < string > {
0 commit comments