@@ -119,7 +119,7 @@ namespace ts {
119
119
newestDeclarationFileContentChangedTime ?: Date ;
120
120
newestOutputFileTime ?: Date ;
121
121
newestOutputFileName ?: string ;
122
- oldestOutputFileName ? : string ;
122
+ oldestOutputFileName : string ;
123
123
}
124
124
125
125
/**
@@ -332,6 +332,9 @@ namespace ts {
332
332
// TODO: To do better with watch mode and normal build mode api that creates program and emits files
333
333
// This currently helps enable --diagnostics and --extendedDiagnostics
334
334
afterProgramEmitAndDiagnostics ?( program : T ) : void ;
335
+
336
+ // For testing
337
+ now ?( ) : Date ;
335
338
}
336
339
337
340
export interface SolutionBuilderHost < T extends BuilderProgram > extends SolutionBuilderHostBase < T > {
@@ -991,16 +994,40 @@ namespace ts {
991
994
return ;
992
995
}
993
996
997
+ if ( status . type === UpToDateStatusType . UpToDateWithUpstreamTypes ) {
998
+ // Fake build
999
+ updateOutputTimestamps ( proj ) ;
1000
+ return ;
1001
+ }
1002
+
994
1003
const buildResult = buildSingleProject ( resolved ) ;
995
- const dependencyGraph = getGlobalDependencyGraph ( ) ;
996
- const referencingProjects = dependencyGraph . referencingProjectsMap . getValue ( resolved ) ;
1004
+ if ( buildResult & BuildResultFlags . AnyErrors ) return ;
1005
+
1006
+ const { referencingProjectsMap, buildQueue } = getGlobalDependencyGraph ( ) ;
1007
+ const referencingProjects = referencingProjectsMap . getValue ( resolved ) ;
997
1008
if ( ! referencingProjects ) return ;
1009
+
998
1010
// Always use build order to queue projects
999
- for ( const project of dependencyGraph . buildQueue ) {
1011
+ for ( let index = buildQueue . indexOf ( resolved ) + 1 ; index < buildQueue . length ; index ++ ) {
1012
+ const project = buildQueue [ index ] ;
1000
1013
const prepend = referencingProjects . getValue ( project ) ;
1001
- // If the project is referenced with prepend, always build downstream projectm,
1002
- // otherwise queue it only if declaration output changed
1003
- if ( prepend || ( prepend !== undefined && ! ( buildResult & BuildResultFlags . DeclarationOutputUnchanged ) ) ) {
1014
+ if ( prepend !== undefined ) {
1015
+ // If the project is referenced with prepend, always build downstream project,
1016
+ // If declaration output is changed changed, build the project
1017
+ // otherwise mark the project UpToDateWithUpstreamTypes so it updates output time stamps
1018
+ const status = projectStatus . getValue ( project ) ;
1019
+ if ( prepend || ! ( buildResult & BuildResultFlags . DeclarationOutputUnchanged ) ) {
1020
+ if ( status && ( status . type === UpToDateStatusType . UpToDate || status . type === UpToDateStatusType . UpToDateWithUpstreamTypes ) ) {
1021
+ projectStatus . setValue ( project , {
1022
+ type : UpToDateStatusType . OutOfDateWithUpstream ,
1023
+ outOfDateOutputFileName : status . oldestOutputFileName ,
1024
+ newerProjectName : resolved
1025
+ } ) ;
1026
+ }
1027
+ }
1028
+ else if ( status && status . type === UpToDateStatusType . UpToDate ) {
1029
+ status . type = UpToDateStatusType . UpToDateWithUpstreamTypes ;
1030
+ }
1004
1031
addProjToQueue ( project ) ;
1005
1032
}
1006
1033
}
@@ -1110,6 +1137,7 @@ namespace ts {
1110
1137
let declDiagnostics : Diagnostic [ ] | undefined ;
1111
1138
const reportDeclarationDiagnostics = ( d : Diagnostic ) => ( declDiagnostics || ( declDiagnostics = [ ] ) ) . push ( d ) ;
1112
1139
const outputFiles : OutputFile [ ] = [ ] ;
1140
+ // TODO:: handle declaration diagnostics in incremental build.
1113
1141
emitFilesAndReportErrors ( program , reportDeclarationDiagnostics , writeFileName , /*reportSummary*/ undefined , ( name , text , writeByteOrderMark ) => outputFiles . push ( { name, text, writeByteOrderMark } ) ) ;
1114
1142
// Don't emit .d.ts if there are decl file errors
1115
1143
if ( declDiagnostics ) {
@@ -1118,6 +1146,7 @@ namespace ts {
1118
1146
1119
1147
// Actual Emit
1120
1148
const emitterDiagnostics = createDiagnosticCollection ( ) ;
1149
+ const emittedOutputs = createFileMap < true > ( toPath as ToPath ) ;
1121
1150
outputFiles . forEach ( ( { name, text, writeByteOrderMark } ) => {
1122
1151
let priorChangeTime : Date | undefined ;
1123
1152
if ( ! anyDtsChanged && isDeclarationFile ( name ) ) {
@@ -1131,6 +1160,7 @@ namespace ts {
1131
1160
}
1132
1161
}
1133
1162
1163
+ emittedOutputs . setValue ( name , true ) ;
1134
1164
writeFile ( compilerHost , emitterDiagnostics , name , text , writeByteOrderMark ) ;
1135
1165
if ( priorChangeTime !== undefined ) {
1136
1166
newestDeclarationFileContentChangedTime = newer ( priorChangeTime , newestDeclarationFileContentChangedTime ) ;
@@ -1143,9 +1173,13 @@ namespace ts {
1143
1173
return buildErrors ( emitDiagnostics , BuildResultFlags . EmitErrors , "Emit" ) ;
1144
1174
}
1145
1175
1176
+ // Update time stamps for rest of the outputs
1177
+ newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker ( configFile , newestDeclarationFileContentChangedTime , Diagnostics . Updating_unchanged_output_timestamps_of_project_0 , emittedOutputs ) ;
1178
+
1146
1179
const status : UpToDateStatus = {
1147
1180
type : UpToDateStatusType . UpToDate ,
1148
- newestDeclarationFileContentChangedTime : anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime
1181
+ newestDeclarationFileContentChangedTime : anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime ,
1182
+ oldestOutputFileName : outputFiles . length ? outputFiles [ 0 ] . name : getFirstProjectOutput ( configFile )
1149
1183
} ;
1150
1184
diagnostics . removeKey ( proj ) ;
1151
1185
projectStatus . setValue ( proj , status ) ;
@@ -1175,23 +1209,34 @@ namespace ts {
1175
1209
if ( options . dry ) {
1176
1210
return reportStatus ( Diagnostics . A_non_dry_build_would_build_project_0 , proj . options . configFilePath ! ) ;
1177
1211
}
1212
+ const priorNewestUpdateTime = updateOutputTimestampsWorker ( proj , minimumDate , Diagnostics . Updating_output_timestamps_of_project_0 ) ;
1213
+ projectStatus . setValue ( proj . options . configFilePath as ResolvedConfigFilePath , { type : UpToDateStatusType . UpToDate , newestDeclarationFileContentChangedTime : priorNewestUpdateTime } as UpToDateStatus ) ;
1214
+ }
1178
1215
1179
- if ( options . verbose ) {
1180
- reportStatus ( Diagnostics . Updating_output_timestamps_of_project_0 , proj . options . configFilePath ! ) ;
1181
- }
1182
-
1183
- const now = new Date ( ) ;
1216
+ function updateOutputTimestampsWorker ( proj : ParsedCommandLine , priorNewestUpdateTime : Date , verboseMessage : DiagnosticMessage , skipOutputs ?: FileMap < true > ) {
1184
1217
const outputs = getAllProjectOutputs ( proj ) ;
1185
- let priorNewestUpdateTime = minimumDate ;
1186
- for ( const file of outputs ) {
1187
- if ( isDeclarationFile ( file ) ) {
1188
- priorNewestUpdateTime = newer ( priorNewestUpdateTime , host . getModifiedTime ( file ) || missingFileModifiedTime ) ;
1218
+ if ( ! skipOutputs || outputs . length !== skipOutputs . getSize ( ) ) {
1219
+ if ( options . verbose ) {
1220
+ reportStatus ( verboseMessage , proj . options . configFilePath ! ) ;
1189
1221
}
1222
+ const now = host . now ? host . now ( ) : new Date ( ) ;
1223
+ for ( const file of outputs ) {
1224
+ if ( skipOutputs && skipOutputs . hasKey ( file ) ) {
1225
+ continue ;
1226
+ }
1227
+
1228
+ if ( isDeclarationFile ( file ) ) {
1229
+ priorNewestUpdateTime = newer ( priorNewestUpdateTime , host . getModifiedTime ( file ) || missingFileModifiedTime ) ;
1230
+ }
1190
1231
1191
- host . setModifiedTime ( file , now ) ;
1232
+ host . setModifiedTime ( file , now ) ;
1233
+ if ( proj . options . listEmittedFiles ) {
1234
+ writeFileName ( `TSFILE: ${ file } ` ) ;
1235
+ }
1236
+ }
1192
1237
}
1193
1238
1194
- projectStatus . setValue ( proj . options . configFilePath as ResolvedConfigFilePath , { type : UpToDateStatusType . UpToDate , newestDeclarationFileContentChangedTime : priorNewestUpdateTime } as UpToDateStatus ) ;
1239
+ return priorNewestUpdateTime ;
1195
1240
}
1196
1241
1197
1242
function getFilesToClean ( ) : string [ ] {
@@ -1368,6 +1413,20 @@ namespace ts {
1368
1413
}
1369
1414
}
1370
1415
1416
+ function getFirstProjectOutput ( project : ParsedCommandLine ) : string {
1417
+ if ( project . options . outFile || project . options . out ) {
1418
+ return first ( getOutFileOutputs ( project ) ) ;
1419
+ }
1420
+
1421
+ for ( const inputFile of project . fileNames ) {
1422
+ const outputs = getOutputFileNames ( inputFile , project ) ;
1423
+ if ( outputs . length ) {
1424
+ return first ( outputs ) ;
1425
+ }
1426
+ }
1427
+ return Debug . fail ( `project ${ project . options . configFilePath } expected to have atleast one output` ) ;
1428
+ }
1429
+
1371
1430
export function formatUpToDateStatus < T > ( configFileName : string , status : UpToDateStatus , relName : ( fileName : string ) => string , formatMessage : ( message : DiagnosticMessage , ...args : string [ ] ) => T ) {
1372
1431
switch ( status . type ) {
1373
1432
case UpToDateStatusType . OutOfDateWithSelf :
0 commit comments