@@ -255,7 +255,7 @@ namespace ts {
255255 }
256256
257257 export interface SolutionBuilder {
258- buildAllProjects ( ) : ExitStatus ;
258+ build ( project ?: string , cancellationToken ?: CancellationToken ) : ExitStatus ;
259259 cleanAllProjects ( ) : ExitStatus ;
260260
261261 // Currently used for testing but can be made public if needed:
@@ -264,14 +264,21 @@ namespace ts {
264264 // Testing only
265265 /*@internal */ getUpToDateStatusOfProject ( project : string ) : UpToDateStatus ;
266266 /*@internal */ invalidateProject ( configFileName : string , reloadLevel ?: ConfigFileProgramReloadLevel ) : void ;
267- /*@internal */ buildInvalidatedProject ( ) : void ;
267+ /*@internal */ buildNextInvalidatedProject ( ) : void ;
268268 }
269269
270270 export interface SolutionBuilderWithWatch {
271- buildAllProjects ( ) : ExitStatus ;
271+ build ( project ?: string , cancellationToken ?: CancellationToken ) : ExitStatus ;
272272 /*@internal */ startWatching ( ) : void ;
273273 }
274274
275+ interface InvalidatedProject {
276+ project : ResolvedConfigFileName ;
277+ projectPath : ResolvedConfigFilePath ;
278+ reloadLevel : ConfigFileProgramReloadLevel ;
279+ projectIndex : number ;
280+ }
281+
275282 /**
276283 * Create a function that reports watch status by writing to the system and handles the formating of the diagnostic
277284 */
@@ -386,18 +393,22 @@ namespace ts {
386393 const allWatchedInputFiles = createMap ( ) as ConfigFileMap < Map < FileWatcher > > ;
387394 const allWatchedConfigFiles = createMap ( ) as ConfigFileMap < FileWatcher > ;
388395
396+ let allProjectBuildPending = true ;
397+ let needsSummary = true ;
398+ // let watchAllProjectsPending = watch;
399+
389400 return watch ?
390401 {
391- buildAllProjects ,
402+ build ,
392403 startWatching
393404 } :
394405 {
395- buildAllProjects ,
406+ build ,
396407 cleanAllProjects,
397408 getBuildOrder,
398409 getUpToDateStatusOfProject,
399410 invalidateProject,
400- buildInvalidatedProject ,
411+ buildNextInvalidatedProject ,
401412 } ;
402413
403414 function toPath ( fileName : string ) {
@@ -800,6 +811,7 @@ namespace ts {
800811 configFileCache . delete ( resolved ) ;
801812 buildOrder = undefined ;
802813 }
814+ needsSummary = true ;
803815 clearProjectStatus ( resolved ) ;
804816 addProjToQueue ( resolved , reloadLevel ) ;
805817 enableCache ( ) ;
@@ -823,16 +835,16 @@ namespace ts {
823835 }
824836 }
825837
826- function getNextInvalidatedProject ( ) {
827- Debug . assert ( hasPendingInvalidatedProjects ( ) ) ;
828- return forEach ( getBuildOrder ( ) , ( project , projectIndex ) => {
829- const projectPath = toResolvedConfigFilePath ( project ) ;
830- const reloadLevel = projectPendingBuild . get ( projectPath ) ;
831- if ( reloadLevel !== undefined ) {
832- projectPendingBuild . delete ( projectPath ) ;
833- return { project , projectPath , reloadLevel , projectIndex } ;
834- }
835- } ) ;
838+ function getNextInvalidatedProject ( buildOrder : readonly ResolvedConfigFileName [ ] ) : InvalidatedProject | undefined {
839+ return hasPendingInvalidatedProjects ( ) ?
840+ forEach ( buildOrder , ( project , projectIndex ) => {
841+ const projectPath = toResolvedConfigFilePath ( project ) ;
842+ const reloadLevel = projectPendingBuild . get ( projectPath ) ;
843+ if ( reloadLevel !== undefined ) {
844+ return { project , projectPath, reloadLevel , projectIndex } ;
845+ }
846+ } ) :
847+ undefined ;
836848 }
837849
838850 function hasPendingInvalidatedProjects ( ) {
@@ -846,18 +858,19 @@ namespace ts {
846858 if ( timerToBuildInvalidatedProject ) {
847859 hostWithWatch . clearTimeout ( timerToBuildInvalidatedProject ) ;
848860 }
849- timerToBuildInvalidatedProject = hostWithWatch . setTimeout ( buildInvalidatedProject , 250 ) ;
861+ timerToBuildInvalidatedProject = hostWithWatch . setTimeout ( buildNextInvalidatedProject , 250 ) ;
850862 }
851863
852- function buildInvalidatedProject ( ) {
864+ function buildNextInvalidatedProject ( ) {
853865 timerToBuildInvalidatedProject = undefined ;
854866 if ( reportFileChangeDetected ) {
855867 reportFileChangeDetected = false ;
856868 projectErrorsReported . clear ( ) ;
857869 reportWatchStatus ( Diagnostics . File_change_detected_Starting_incremental_compilation ) ;
858870 }
859- if ( hasPendingInvalidatedProjects ( ) ) {
860- buildNextInvalidatedProject ( ) ;
871+ const invalidatedProject = getNextInvalidatedProject ( getBuildOrder ( ) ) ;
872+ if ( invalidatedProject ) {
873+ buildInvalidatedProject ( invalidatedProject ) ;
861874 if ( hasPendingInvalidatedProjects ( ) ) {
862875 if ( watch && ! timerToBuildInvalidatedProject ) {
863876 scheduleBuildInvalidatedProject ( ) ;
@@ -872,6 +885,7 @@ namespace ts {
872885
873886 function reportErrorSummary ( ) {
874887 if ( watch || host . reportErrorSummary ) {
888+ needsSummary = false ;
875889 // Report errors from the other projects
876890 getBuildOrder ( ) . forEach ( project => {
877891 const projectPath = toResolvedConfigFilePath ( project ) ;
@@ -890,11 +904,11 @@ namespace ts {
890904 }
891905 }
892906
893- function buildNextInvalidatedProject ( ) {
894- const { project, projectPath, reloadLevel, projectIndex } = getNextInvalidatedProject ( ) ! ;
907+ function buildInvalidatedProject ( { project, projectPath, reloadLevel, projectIndex } : InvalidatedProject , cancellationToken ?: CancellationToken ) {
895908 const config = parseConfigFile ( project , projectPath ) ;
896909 if ( ! config ) {
897910 reportParseConfigFileDiagnostic ( projectPath ) ;
911+ projectPendingBuild . delete ( projectPath ) ;
898912 return ;
899913 }
900914
@@ -920,31 +934,36 @@ namespace ts {
920934 // In a dry build, inform the user of this fact
921935 reportStatus ( Diagnostics . Project_0_is_up_to_date , project ) ;
922936 }
937+ projectPendingBuild . delete ( projectPath ) ;
923938 return ;
924939 }
925940
926941 if ( status . type === UpToDateStatusType . UpToDateWithUpstreamTypes && ! options . force ) {
927942 reportAndStoreErrors ( projectPath , config . errors ) ;
928943 // Fake that files have been built by updating output file stamps
929944 updateOutputTimestamps ( config , projectPath ) ;
945+ projectPendingBuild . delete ( projectPath ) ;
930946 return ;
931947 }
932948
933949 if ( status . type === UpToDateStatusType . UpstreamBlocked ) {
934950 reportAndStoreErrors ( projectPath , config . errors ) ;
935951 if ( options . verbose ) reportStatus ( Diagnostics . Skipping_build_of_project_0_because_its_dependency_1_has_errors , project , status . upstreamProjectName ) ;
952+ projectPendingBuild . delete ( projectPath ) ;
936953 return ;
937954 }
938955
939956 if ( status . type === UpToDateStatusType . ContainerOnly ) {
940957 reportAndStoreErrors ( projectPath , config . errors ) ;
941958 // Do nothing
959+ projectPendingBuild . delete ( projectPath ) ;
942960 return ;
943961 }
944962
945963 const buildResult = needsBuild ( status , config ) ?
946- buildSingleProject ( project , projectPath ) : // Actual build
947- updateBundle ( project , projectPath ) ; // Fake that files have been built by manipulating prepend and existing output
964+ buildSingleProject ( project , projectPath , cancellationToken ) : // Actual build
965+ updateBundle ( project , projectPath , cancellationToken ) ; // Fake that files have been built by manipulating prepend and existing output
966+ projectPendingBuild . delete ( projectPath ) ;
948967 // Only composite projects can be referenced by other projects
949968 if ( ! ( buildResult & BuildResultFlags . AnyErrors ) && config . options . composite ) {
950969 queueReferencingProjects ( project , projectPath , projectIndex , ! ( buildResult & BuildResultFlags . DeclarationOutputUnchanged ) ) ;
@@ -1050,7 +1069,7 @@ namespace ts {
10501069 }
10511070 }
10521071
1053- function buildSingleProject ( proj : ResolvedConfigFileName , resolvedPath : ResolvedConfigFilePath ) : BuildResultFlags {
1072+ function buildSingleProject ( proj : ResolvedConfigFileName , resolvedPath : ResolvedConfigFilePath , cancellationToken : CancellationToken | undefined ) : BuildResultFlags {
10541073 if ( options . dry ) {
10551074 reportStatus ( Diagnostics . A_non_dry_build_would_build_project_0 , proj ) ;
10561075 return BuildResultFlags . Success ;
@@ -1112,15 +1131,15 @@ namespace ts {
11121131 // Don't emit anything in the presence of syntactic errors or options diagnostics
11131132 const syntaxDiagnostics = [
11141133 ...program . getConfigFileParsingDiagnostics ( ) ,
1115- ...program . getOptionsDiagnostics ( ) ,
1116- ...program . getGlobalDiagnostics ( ) ,
1117- ...program . getSyntacticDiagnostics ( ) ] ;
1134+ ...program . getOptionsDiagnostics ( cancellationToken ) ,
1135+ ...program . getGlobalDiagnostics ( cancellationToken ) ,
1136+ ...program . getSyntacticDiagnostics ( /*sourceFile*/ undefined , cancellationToken ) ] ;
11181137 if ( syntaxDiagnostics . length ) {
11191138 return buildErrors ( syntaxDiagnostics , BuildResultFlags . SyntaxErrors , "Syntactic" ) ;
11201139 }
11211140
11221141 // Same as above but now for semantic diagnostics
1123- const semanticDiagnostics = program . getSemanticDiagnostics ( ) ;
1142+ const semanticDiagnostics = program . getSemanticDiagnostics ( /*sourceFile*/ undefined , cancellationToken ) ;
11241143 if ( semanticDiagnostics . length ) {
11251144 return buildErrors ( semanticDiagnostics , BuildResultFlags . TypeErrors , "Semantic" ) ;
11261145 }
@@ -1132,7 +1151,14 @@ namespace ts {
11321151 let declDiagnostics : Diagnostic [ ] | undefined ;
11331152 const reportDeclarationDiagnostics = ( d : Diagnostic ) => ( declDiagnostics || ( declDiagnostics = [ ] ) ) . push ( d ) ;
11341153 const outputFiles : OutputFile [ ] = [ ] ;
1135- emitFilesAndReportErrors ( program , reportDeclarationDiagnostics , /*writeFileName*/ undefined , /*reportSummary*/ undefined , ( name , text , writeByteOrderMark ) => outputFiles . push ( { name, text, writeByteOrderMark } ) ) ;
1154+ emitFilesAndReportErrors (
1155+ program ,
1156+ reportDeclarationDiagnostics ,
1157+ /*writeFileName*/ undefined ,
1158+ /*reportSummary*/ undefined ,
1159+ ( name , text , writeByteOrderMark ) => outputFiles . push ( { name, text, writeByteOrderMark } ) ,
1160+ cancellationToken
1161+ ) ;
11361162 // Don't emit .d.ts if there are decl file errors
11371163 if ( declDiagnostics ) {
11381164 program . restoreState ( ) ;
@@ -1221,7 +1247,7 @@ namespace ts {
12211247 return readBuilderProgram ( parsed . options , readFileWithCache ) as any as T ;
12221248 }
12231249
1224- function updateBundle ( proj : ResolvedConfigFileName , resolvedPath : ResolvedConfigFilePath ) : BuildResultFlags {
1250+ function updateBundle ( proj : ResolvedConfigFileName , resolvedPath : ResolvedConfigFilePath , cancellationToken : CancellationToken | undefined ) : BuildResultFlags {
12251251 if ( options . dry ) {
12261252 reportStatus ( Diagnostics . A_non_dry_build_would_update_output_of_project_0 , proj ) ;
12271253 return BuildResultFlags . Success ;
@@ -1241,7 +1267,7 @@ namespace ts {
12411267 } ) ;
12421268 if ( isString ( outputFiles ) ) {
12431269 reportStatus ( Diagnostics . Cannot_update_output_of_project_0_because_there_was_error_reading_file_1 , proj , relName ( outputFiles ) ) ;
1244- return buildSingleProject ( proj , resolvedPath ) ;
1270+ return buildSingleProject ( proj , resolvedPath , cancellationToken ) ;
12451271 }
12461272
12471273 // Actual Emit
@@ -1403,21 +1429,58 @@ namespace ts {
14031429 cacheState = undefined ;
14041430 }
14051431
1406- function buildAllProjects ( ) : ExitStatus {
1407- if ( options . watch ) { reportWatchStatus ( Diagnostics . Starting_compilation_in_watch_mode ) ; }
1408- enableCache ( ) ;
1432+ function build ( project ?: string , cancellationToken ?: CancellationToken ) : ExitStatus {
1433+ // Set initial build if not already built
1434+ if ( allProjectBuildPending ) {
1435+ allProjectBuildPending = false ;
1436+ if ( options . watch ) { reportWatchStatus ( Diagnostics . Starting_compilation_in_watch_mode ) ; }
1437+ enableCache ( ) ;
1438+ const buildOrder = getBuildOrder ( ) ;
1439+ reportBuildQueue ( buildOrder ) ;
1440+ buildOrder . forEach ( configFileName =>
1441+ projectPendingBuild . set ( toResolvedConfigFilePath ( configFileName ) , ConfigFileProgramReloadLevel . None ) ) ;
14091442
1410- const buildOrder = getBuildOrder ( ) ;
1411- reportBuildQueue ( buildOrder ) ;
1412- buildOrder . forEach ( configFileName =>
1413- projectPendingBuild . set ( toResolvedConfigFilePath ( configFileName ) , ConfigFileProgramReloadLevel . None ) ) ;
1443+ if ( cancellationToken ) {
1444+ cancellationToken . throwIfCancellationRequested ( ) ;
1445+ }
1446+ }
14141447
1415- while ( hasPendingInvalidatedProjects ( ) ) {
1416- buildNextInvalidatedProject ( ) ;
1448+ let successfulProjects = 0 ;
1449+ let errorProjects = 0 ;
1450+ const resolvedProject = project && resolveProjectName ( project ) ;
1451+ if ( resolvedProject ) {
1452+ const projectPath = toResolvedConfigFilePath ( resolvedProject ) ;
1453+ const projectIndex = findIndex (
1454+ getBuildOrder ( ) ,
1455+ configFileName => toResolvedConfigFilePath ( configFileName ) === projectPath
1456+ ) ;
1457+ if ( projectIndex === - 1 ) return ExitStatus . InvalidProject_OutputsSkipped ;
1458+ }
1459+ const buildOrder = resolvedProject ? createBuildOrder ( [ resolvedProject ] ) : getBuildOrder ( ) ;
1460+ while ( true ) {
1461+ const invalidatedProject = getNextInvalidatedProject ( buildOrder ) ;
1462+ if ( ! invalidatedProject ) {
1463+ if ( needsSummary ) {
1464+ disableCache ( ) ;
1465+ reportErrorSummary ( ) ;
1466+ }
1467+ break ;
1468+ }
1469+
1470+ buildInvalidatedProject ( invalidatedProject , cancellationToken ) ;
1471+ if ( diagnostics . has ( invalidatedProject . projectPath ) ) {
1472+ errorProjects ++ ;
1473+ }
1474+ else {
1475+ successfulProjects ++ ;
1476+ }
14171477 }
1418- reportErrorSummary ( ) ;
1419- disableCache ( ) ;
1420- return diagnostics . size ? ExitStatus . DiagnosticsPresent_OutputsSkipped : ExitStatus . Success ;
1478+
1479+ return errorProjects ?
1480+ successfulProjects ?
1481+ ExitStatus . DiagnosticsPresent_OutputsGenerated :
1482+ ExitStatus . DiagnosticsPresent_OutputsSkipped :
1483+ ExitStatus . Success ;
14211484 }
14221485
14231486 function needsBuild ( status : UpToDateStatus , config : ParsedCommandLine ) {
0 commit comments