@@ -98,7 +98,7 @@ interface IRunPhasesOptions extends IInitialRunPhasesOptions {
9898 executionManagerOptions : IOperationExecutionManagerOptions ;
9999}
100100
101- interface IExecutionOperationsOptions {
101+ interface IExecuteOperationsOptions {
102102 executeOperationsContext : IExecuteOperationsContext ;
103103 executionManagerOptions : IOperationExecutionManagerOptions ;
104104 ignoreHooks : boolean ;
@@ -131,12 +131,13 @@ interface IPhasedCommandTelemetry {
131131 * and "rebuild" commands are also modeled as phased commands with a single phase that invokes the npm
132132 * "build" script for each project.
133133 */
134- export class PhasedScriptAction extends BaseScriptAction < IPhasedCommandConfig > {
134+ export class PhasedScriptAction extends BaseScriptAction < IPhasedCommandConfig > implements IPhasedCommand {
135135 /**
136136 * @internal
137137 */
138138 public _runsBeforeInstall : boolean | undefined ;
139139 public readonly hooks : PhasedCommandHooks ;
140+ public readonly sessionAbortController : AbortController ;
140141
141142 private readonly _enableParallelism : boolean ;
142143 private readonly _isIncrementalBuildAllowed : boolean ;
@@ -150,6 +151,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
150151 private readonly _knownPhases : ReadonlyMap < string , IPhase > ;
151152 private readonly _terminal : ITerminal ;
152153 private _changedProjectsOnly : boolean ;
154+ private _executionAbortController : AbortController | undefined ;
153155
154156 private readonly _changedProjectsOnlyParameter : CommandLineFlagParameter | undefined ;
155157 private readonly _selectionParameters : SelectionParameterSet ;
@@ -180,6 +182,16 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
180182 this . _runsBeforeInstall = false ;
181183 this . _knownPhases = options . phases ;
182184 this . _changedProjectsOnly = false ;
185+ this . sessionAbortController = new AbortController ( ) ;
186+ this . _executionAbortController = undefined ;
187+
188+ this . sessionAbortController . signal . addEventListener (
189+ 'abort' ,
190+ ( ) => {
191+ this . _executionAbortController ?. abort ( ) ;
192+ } ,
193+ { once : true }
194+ ) ;
183195
184196 this . hooks = new PhasedCommandHooks ( ) ;
185197
@@ -655,9 +667,11 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
655667 }
656668 ) ;
657669
670+ const abortController : AbortController = ( this . _executionAbortController = new AbortController ( ) ) ;
658671 const initialExecuteOperationsContext : IExecuteOperationsContext = {
659672 ...initialCreateOperationsContext ,
660- inputsSnapshot : initialSnapshot
673+ inputsSnapshot : initialSnapshot ,
674+ abortController
661675 } ;
662676
663677 const executionManagerOptions : IOperationExecutionManagerOptions = {
@@ -670,7 +684,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
670684 }
671685 } ;
672686
673- const initialOptions : IExecutionOperationsOptions = {
687+ const initialOptions : IExecuteOperationsOptions = {
674688 executeOperationsContext : initialExecuteOperationsContext ,
675689 ignoreHooks : false ,
676690 operations,
@@ -691,14 +705,12 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
691705 } ;
692706 }
693707
694- private _registerWatchModeInterface (
695- projectWatcher : ProjectWatcher ,
696- abortController : AbortController
697- ) : void {
708+ private _registerWatchModeInterface ( projectWatcher : ProjectWatcher ) : void {
698709 const buildOnceKey : 'b' = 'b' ;
699710 const changedProjectsOnlyKey : 'c' = 'c' ;
700711 const invalidateKey : 'i' = 'i' ;
701712 const quitKey : 'q' = 'q' ;
713+ const abortKey : 'a' = 'a' ;
702714 const toggleWatcherKey : 'w' = 'w' ;
703715 const shutdownProcessesKey : 'x' = 'x' ;
704716
@@ -707,6 +719,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
707719 projectWatcher . setPromptGenerator ( ( isPaused : boolean ) => {
708720 const promptLines : string [ ] = [
709721 ` Press <${ quitKey } > to gracefully exit.` ,
722+ ` Press <${ abortKey } > to abort queued operations. Any that have started will finish.` ,
710723 ` Press <${ toggleWatcherKey } > to ${ isPaused ? 'resume' : 'pause' } .` ,
711724 ` Press <${ invalidateKey } > to invalidate all projects.` ,
712725 ` Press <${ changedProjectsOnlyKey } > to ${
@@ -725,11 +738,15 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
725738 const onKeyPress = ( key : string ) : void => {
726739 switch ( key ) {
727740 case quitKey :
728- terminal . writeLine ( `Exiting watch mode...` ) ;
741+ terminal . writeLine ( `Exiting watch mode and aborting any scheduled work ...` ) ;
729742 process . stdin . setRawMode ( false ) ;
730743 process . stdin . off ( 'data' , onKeyPress ) ;
731744 process . stdin . unref ( ) ;
732- abortController . abort ( ) ;
745+ this . sessionAbortController . abort ( ) ;
746+ break ;
747+ case abortKey :
748+ terminal . writeLine ( `Aborting current iteration...` ) ;
749+ this . _executionAbortController ?. abort ( ) ;
733750 break ;
734751 case toggleWatcherKey :
735752 if ( projectWatcher . isPaused ) {
@@ -768,6 +785,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
768785 process . stdin . setRawMode ( false ) ;
769786 process . stdin . off ( 'data' , onKeyPress ) ;
770787 process . stdin . unref ( ) ;
788+ this . sessionAbortController . abort ( ) ;
771789 process . kill ( process . pid , 'SIGINT' ) ;
772790 break ;
773791 }
@@ -814,8 +832,8 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
814832 '../../logic/ProjectWatcher'
815833 ) ;
816834
817- const abortController : AbortController = new AbortController ( ) ;
818- const abortSignal : AbortSignal = abortController . signal ;
835+ const sessionAbortController : AbortController = this . sessionAbortController ;
836+ const abortSignal : AbortSignal = sessionAbortController . signal ;
819837
820838 const projectWatcher : typeof ProjectWatcher . prototype = new ProjectWatcher ( {
821839 getInputsSnapshotAsync,
@@ -829,7 +847,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
829847
830848 // Ensure process.stdin allows interactivity before using TTY-only APIs
831849 if ( process . stdin . isTTY ) {
832- this . _registerWatchModeInterface ( projectWatcher , abortController ) ;
850+ this . _registerWatchModeInterface ( projectWatcher ) ;
833851 }
834852
835853 const onWaitingForChanges = ( ) : void => {
@@ -877,9 +895,13 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
877895 terminal . writeLine ( ` ${ Colorize . cyan ( name ) } ` ) ;
878896 }
879897
898+ const initialAbortController : AbortController = ( this . _executionAbortController =
899+ new AbortController ( ) ) ;
900+
880901 // Account for consumer relationships
881902 const executeOperationsContext : IExecuteOperationsContext = {
882903 ...initialCreateOperationsContext ,
904+ abortController : initialAbortController ,
883905 changedProjectsOnly : ! ! this . _changedProjectsOnly ,
884906 isInitial : false ,
885907 inputsSnapshot : state ,
@@ -893,7 +915,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
893915 this . hooks . createOperations . promise ( new Set ( ) , executeOperationsContext )
894916 ) ;
895917
896- const executeOptions : IExecutionOperationsOptions = {
918+ const executeOptions : IExecuteOperationsOptions = {
897919 executeOperationsContext,
898920 // For now, don't run pre-build or post-build in watch mode
899921 ignoreHooks : true ,
@@ -928,29 +950,36 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
928950 /**
929951 * Runs a set of operations and reports the results.
930952 */
931- private async _executeOperationsAsync ( options : IExecutionOperationsOptions ) : Promise < void > {
932- const { executionManagerOptions, ignoreHooks, operations, stopwatch, terminal } = options ;
953+ private async _executeOperationsAsync ( options : IExecuteOperationsOptions ) : Promise < void > {
954+ const {
955+ executeOperationsContext,
956+ executionManagerOptions,
957+ ignoreHooks,
958+ operations,
959+ stopwatch,
960+ terminal
961+ } = options ;
933962
934963 const executionManager : OperationExecutionManager = new OperationExecutionManager (
935964 operations ,
936965 executionManagerOptions
937966 ) ;
938967
939- const { isInitial, isWatch } = options . executeOperationsContext ;
968+ const { isInitial, isWatch, abortController , invalidateOperation } = executeOperationsContext ;
940969
941970 let success : boolean = false ;
942971 let result : IExecutionResult | undefined ;
943972
944973 try {
945974 const definiteResult : IExecutionResult = await measureAsyncFn (
946975 `${ PERF_PREFIX } :executeOperationsInner` ,
947- ( ) => executionManager . executeAsync ( )
976+ ( ) => executionManager . executeAsync ( abortController )
948977 ) ;
949978 success = definiteResult . status === OperationStatus . Success ;
950979 result = definiteResult ;
951980
952981 await measureAsyncFn ( `${ PERF_PREFIX } :afterExecuteOperations` , ( ) =>
953- this . hooks . afterExecuteOperations . promise ( definiteResult , options . executeOperationsContext )
982+ this . hooks . afterExecuteOperations . promise ( definiteResult , executeOperationsContext )
954983 ) ;
955984
956985 stopwatch . stop ( ) ;
@@ -980,6 +1009,20 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
9801009 }
9811010 }
9821011
1012+ this . _executionAbortController = undefined ;
1013+
1014+ if ( invalidateOperation ) {
1015+ const operationResults : ReadonlyMap < Operation , IOperationExecutionResult > | undefined =
1016+ result ?. operationResults ;
1017+ if ( operationResults ) {
1018+ for ( const [ operation , { status } ] of operationResults ) {
1019+ if ( status === OperationStatus . Aborted ) {
1020+ invalidateOperation ( operation , 'aborted' ) ;
1021+ }
1022+ }
1023+ }
1024+ }
1025+
9831026 if ( ! ignoreHooks ) {
9841027 measureFn ( `${ PERF_PREFIX } :doAfterTask` , ( ) => this . _doAfterTask ( ) ) ;
9851028 }
0 commit comments