@@ -99,6 +99,10 @@ import {
9999 POST_TASKS_EXECUTION ,
100100 PRE_TASKS_EXECUTION ,
101101} from '../message-types/run-tasks-execution-hooks' ;
102+ import {
103+ isEmitLogMessage ,
104+ isUpdateProgressMessage ,
105+ } from '../message-types/streaming-messages' ;
102106import {
103107 GET_ESTIMATED_TASK_TIMINGS ,
104108 GET_FLAKY_TASKS ,
@@ -159,6 +163,11 @@ export class DaemonClient {
159163 private currentMessage ;
160164 private currentResolve ;
161165 private currentReject ;
166+ // Tracks the spinner owned by the in-flight request so streamed
167+ // progress updates are routed to the caller's spinner instead of
168+ // mutating the process-wide globalSpinner (which may belong to an
169+ // unrelated command).
170+ private currentSpinner : DelayedSpinner | null = null ;
162171
163172 private _enabled : boolean | undefined ;
164173 private _daemonStatus : DaemonStatus = DaemonStatus . DISCONNECTED ;
@@ -307,6 +316,7 @@ export class DaemonClient {
307316 'Calculating the project graph on the Nx Daemon is taking longer than expected. Re-run with NX_DAEMON=false to see more details.' ,
308317 { ciDelay : 60_000 , delay : 30_000 }
309318 ) ;
319+ this . currentSpinner = spinner ;
310320 try {
311321 const response = await this . sendToDaemonViaQueue ( {
312322 type : 'REQUEST_PROJECT_GRAPH' ,
@@ -323,6 +333,7 @@ export class DaemonClient {
323333 }
324334 } finally {
325335 spinner ?. cleanup ( ) ;
336+ this . currentSpinner = null ;
326337 }
327338 }
328339
@@ -760,9 +771,9 @@ export class DaemonClient {
760771 type : 'PROCESS_IN_BACKGROUND' ,
761772 requirePath,
762773 data,
763- // This method is sometimes passed data that cannot be serialized with v8
764- // so we force JSON serialization here
765774 } ,
775+ // This method is sometimes passed data that cannot be serialized with v8
776+ // so we force JSON serialization here
766777 'json'
767778 ) ;
768779 }
@@ -1032,11 +1043,15 @@ export class DaemonClient {
10321043
10331044 private async sendToDaemonViaQueue < T extends DaemonMessage > (
10341045 messageToDaemon : T ,
1035- force ?: 'v8' | 'json'
1046+ parser ?: 'v8' | 'json'
10361047 ) : Promise < any > {
1037- return this . queue . sendToQueue ( ( ) =>
1038- this . sendMessageToDaemon ( messageToDaemon , force )
1039- ) ;
1048+ return this . queue . sendToQueue ( async ( ) => {
1049+ // Set currentSpinner inside the queued function so it's only
1050+ // active while this specific message is in flight — preventing
1051+ // concurrent callers from overwriting each other's spinner
1052+ // reference before their turn arrives.
1053+ return await this . sendMessageToDaemon ( messageToDaemon , parser ) ;
1054+ } ) ;
10401055 }
10411056
10421057 private setUpConnection ( ) {
@@ -1259,6 +1274,19 @@ export class DaemonClient {
12591274 'result-parse-start-' + this . currentMessage . type ,
12601275 'result-parse-end-' + this . currentMessage . type
12611276 ) ;
1277+ // Streaming messages fire side-effects on the client but do not
1278+ // resolve the pending request promise — the daemon can push several
1279+ // of these before finally sending the real response. Progress
1280+ // updates route through the in-flight request's own spinner so
1281+ // we don't stomp on unrelated commands' spinner text.
1282+ if ( isUpdateProgressMessage ( parsedResult ) ) {
1283+ this . currentSpinner ?. setMessage ( parsedResult . message ) ;
1284+ return ;
1285+ }
1286+ if ( isEmitLogMessage ( parsedResult ) ) {
1287+ console [ parsedResult . level ] ( parsedResult . message ) ;
1288+ return ;
1289+ }
12621290 if ( parsedResult . error ) {
12631291 this . currentReject ( parsedResult . error ) ;
12641292 } else {
0 commit comments