@@ -18,7 +18,7 @@ export interface TaskState {
1818 rawOutput : string ;
1919 output : string ;
2020 time ?: number ;
21- subTasks ? : Task [ ] ;
21+ subTasks : Task [ ] ;
2222}
2323
2424export function task ( command : RunpCommand , allTasks : ( ) => Task [ ] , q = createQueue ( ) ) : Task {
@@ -31,6 +31,7 @@ export function task(command: RunpCommand, allTasks: () => Task[], q = createQue
3131 title : ( name ?? fullCmd ) + cwdDisplay ,
3232 rawOutput : '' ,
3333 output : '' ,
34+ subTasks : command . subCommands ?. map ( ( subCommand ) => task ( subCommand , allTasks , q ) ) ?? [ ] ,
3435 } ) ;
3536
3637 const result = new Promise < RunpResult > ( ( resolve ) => {
@@ -79,91 +80,100 @@ export function task(command: RunpCommand, allTasks: () => Task[], q = createQue
7980 }
8081
8182 const start = performance . now ( ) ;
82- state . set ( 'status' , 'inProgress' ) ;
83-
84- if ( cmd instanceof Function ) {
85- try {
86- await cmd ( {
87- updateStatus ( status ) {
88- state . set ( 'statusString' , status ) ;
89- } ,
90- updateTitle ( title ) {
91- state . set ( 'title' , title ) ;
92- } ,
93- updateOutput ( output ) {
94- state . set ( 'rawOutput' , ( rawOutput ) => rawOutput + output ) ;
95- state . set ( 'output' , output ) ;
96- writeLine ( output , thisTask ) ;
97- } ,
83+ try {
84+ state . set ( 'status' , 'inProgress' ) ;
85+
86+ if ( cmd instanceof Function ) {
87+ function updateStatus ( status : string ) {
88+ state . set ( 'statusString' , status ) ;
89+ }
90+
91+ function updateTitle ( title : string ) {
92+ state . set ( 'title' , title ) ;
93+ }
94+
95+ function updateOutput ( output : string ) {
96+ state . set ( 'rawOutput' , ( rawOutput ) => rawOutput + output ) ;
97+ state . set ( 'output' , output ) ;
98+ writeLine ( output , thisTask ) ;
99+ }
100+
101+ try {
102+ await cmd ( { updateStatus, updateTitle, updateOutput } ) ;
103+ } catch ( error ) {
104+ const message = error instanceof Error ? error . toString ( ) : typeof error === 'object' ? JSON . stringify ( error ) : String ( error ) ;
105+ updateOutput ( message ) ;
106+
107+ throw error ;
108+ }
109+ } else if ( cmd ) {
110+ const isTTY = process . stdout . isTTY || process . env . RUNP_TTY ;
111+
112+ await new Promise < void > ( ( resolve , reject ) => {
113+ const subProcess = spawn ( cmd , args , {
114+ shell : process . platform === 'win32' ,
115+ stdio : 'pipe' ,
116+ cwd,
117+ env : {
118+ ...env ,
119+ FORCE_COLOR : isTTY ? '1' : undefined , // Some libs color output when this env var is set
120+ RUNP : RUNP_TASK_V , // Tell child processes, especially runp itself, that they are running in runp
121+ RUNP_TTY : isTTY ? '1' : undefined , // Runp child processes can print as if they were running in a tty
122+ } ,
123+ } ) ;
124+
125+ const append = ( data : any ) => {
126+ const chunk = data . toString ( ) ;
127+ state . set ( 'rawOutput' , ( rawOutput ) => rawOutput + chunk ) ;
128+ state . set ( 'output' , state . get ( ) . rawOutput . includes ( RUNP_TASK_DELEGATE ) ? '' : state . get ( ) . rawOutput ) ;
129+ if ( ! chunk . includes ( RUNP_TASK_DELEGATE ) ) {
130+ writeLine ?.( data . toString ( ) , thisTask ) ;
131+ }
132+ } ;
133+ subProcess . stdout . on ( 'data' , append ) ;
134+ subProcess . stderr . on ( 'data' , append ) ;
135+
136+ subProcess . on ( 'close' , async ( code ) => {
137+ if ( code ) {
138+ reject ( new Error ( `Process exited with code ${ code } ` ) ) ;
139+ return ;
140+ }
141+
142+ const { rawOutput } = state . get ( ) ;
143+
144+ const delegationStart = rawOutput . indexOf ( RUNP_TASK_DELEGATE ) ;
145+ const delegationEnd = rawOutput . indexOf ( RUNP_TASK_DELEGATE , delegationStart + 1 ) ;
146+ if ( delegationStart >= 0 && delegationEnd > delegationStart ) {
147+ const json = rawOutput . slice ( delegationStart + RUNP_TASK_DELEGATE . length , delegationEnd ) ;
148+ const commands = JSON . parse ( json ) as RunpCommand [ ] ;
149+ const tasks : Task [ ] = commands . map ( ( command ) => task ( command , ( ) => tasks , q ) ) ;
150+
151+ state . set ( 'output' , '' ) ;
152+ state . set ( 'subTasks' , ( subTasks ) => [ ...tasks , ...subTasks ] ) ;
153+ }
154+
155+ resolve ( ) ;
156+ } ) ;
157+
158+ subProcess . on ( 'error' , reject ) ;
98159 } ) ;
99-
100- state . set ( 'status' , 'done' ) ;
101- } catch ( error ) {
102- state . set ( 'status' , 'error' ) ;
103- state . set ( 'output' , String ( error ) ) ;
104- writeLine ( String ( error ) , thisTask ) ;
105- } finally {
106- state . set ( 'time' , performance . now ( ) - start ) ;
107- }
108-
109- return ;
110- }
111-
112- const isTTY = process . stdout . isTTY || process . env . RUNP_TTY ;
113-
114- const subProcess = spawn ( cmd , args , {
115- shell : process . platform === 'win32' ,
116- stdio : 'pipe' ,
117- cwd,
118- env : {
119- ...env ,
120- FORCE_COLOR : isTTY ? '1' : undefined , // Some libs color output when this env var is set
121- RUNP : RUNP_TASK_V , // Tell child processes, especially runp itself, that they are running in runp
122- RUNP_TTY : isTTY ? '1' : undefined , // Runp child processes can print as if they were running in a tty
123- } ,
124- } ) ;
125-
126- const append = ( data : any ) => {
127- const chunk = data . toString ( ) ;
128- state . set ( 'rawOutput' , ( rawOutput ) => rawOutput + chunk ) ;
129- state . set ( 'output' , state . get ( ) . rawOutput . includes ( RUNP_TASK_DELEGATE ) ? '' : state . get ( ) . rawOutput ) ;
130- if ( ! chunk . includes ( RUNP_TASK_DELEGATE ) ) {
131- writeLine ?.( data . toString ( ) , thisTask ) ;
132- }
133- } ;
134- subProcess . stdout . on ( 'data' , append ) ;
135- subProcess . stderr . on ( 'data' , append ) ;
136-
137- subProcess . on ( 'close' , async ( code ) => {
138- const { rawOutput } = state . get ( ) ;
139-
140- const delegationStart = rawOutput . indexOf ( RUNP_TASK_DELEGATE ) ;
141- const delegationEnd = rawOutput . indexOf ( RUNP_TASK_DELEGATE , delegationStart + 1 ) ;
142- if ( delegationStart >= 0 && delegationEnd > delegationStart ) {
143- const json = rawOutput . slice ( delegationStart + RUNP_TASK_DELEGATE . length , delegationEnd ) ;
144- const commands = JSON . parse ( json ) as RunpCommand [ ] ;
145- const tasks : Task [ ] = commands . map ( ( command ) => task ( command , ( ) => tasks , q ) ) ;
146-
147- state . set ( 'output' , '' ) ;
148- state . set ( 'subTasks' , tasks ) ;
149160 }
150161
151162 const { subTasks } = state . get ( ) ;
152- let hasErrors = ! ! code ;
163+ const subResults = await Promise . all ( subTasks . map ( ( task ) => task . run ( writeLine ) ) ) ;
153164
154- if ( subTasks ) {
155- const subResults = await Promise . all ( subTasks . map ( ( task ) => task . run ( writeLine ) ) ) ;
156- hasErrors = subResults . some ( ( x ) => x . result === 'error' ) ;
165+ if ( subResults . some ( ( x ) => x . result === 'error' ) ) {
166+ throw new Error ( 'Subtask failed' ) ;
157167 }
158168
159- state . set ( 'status' , hasErrors ? 'error' : 'done' ) ;
169+ state . set ( 'status' , 'done' ) ;
170+ } catch {
171+ state . set ( 'status' , 'error' ) ;
172+ // state.set('output', String(error));
173+ // writeLine(String(error), thisTask);
174+ } finally {
160175 state . set ( 'time' , performance . now ( ) - start ) ;
161- } ) ;
162-
163- subProcess . on ( 'error' , ( error ) => {
164- state . set ( 'output' , String ( error ) ) ;
165- writeLine ( String ( error ) , thisTask ) ;
166- } ) ;
176+ }
167177 } ) ;
168178
169179 return result ;
0 commit comments