@@ -78,6 +78,11 @@ import {
7878 type SourceMapPayload ,
7979} from './sourceMap/sourceMapLoader' ;
8080import { resolveBrowserViewportPreset } from './viewportPresets' ;
81+ import {
82+ isBrowserWatchCliShortcutsEnabled ,
83+ logBrowserWatchReadyMessage ,
84+ setupBrowserWatchCliShortcuts ,
85+ } from './watchCliShortcuts' ;
8186import { collectWatchTestFiles , planWatchRerun } from './watchRerunPlanner' ;
8287
8388const { createRsbuild, rspack } = rsbuild ;
@@ -317,6 +322,8 @@ type WatchContext = {
317322 lastTestFiles : TestFileInfo [ ] ;
318323 hooksEnabled : boolean ;
319324 cleanupRegistered : boolean ;
325+ cleanupPromise : Promise < void > | null ;
326+ closeCliShortcuts : ( ( ) => void ) | null ;
320327 chunkHashes : Map < string , string > ;
321328 affectedTestFiles : string [ ] ;
322329} ;
@@ -326,6 +333,8 @@ const watchContext: WatchContext = {
326333 lastTestFiles : [ ] ,
327334 hooksEnabled : false ,
328335 cleanupRegistered : false ,
336+ cleanupPromise : null ,
337+ closeCliShortcuts : null ,
329338 chunkHashes : new Map ( ) ,
330339 affectedTestFiles : [ ] ,
331340} ;
@@ -998,27 +1007,39 @@ const destroyBrowserRuntime = async (
9981007 . catch ( ( ) => { } ) ;
9991008} ;
10001009
1001- const registerWatchCleanup = ( ) : void => {
1002- if ( watchContext . cleanupRegistered ) {
1003- return ;
1010+ const cleanupWatchRuntime = ( ) : Promise < void > => {
1011+ if ( watchContext . cleanupPromise ) {
1012+ return watchContext . cleanupPromise ;
10041013 }
10051014
1006- const cleanup = async ( ) => {
1015+ watchContext . cleanupPromise = ( async ( ) => {
1016+ watchContext . closeCliShortcuts ?.( ) ;
1017+ watchContext . closeCliShortcuts = null ;
1018+
10071019 if ( ! watchContext . runtime ) {
10081020 return ;
10091021 }
1022+
10101023 await destroyBrowserRuntime ( watchContext . runtime ) ;
10111024 watchContext . runtime = null ;
1012- } ;
1025+ } ) ( ) ;
1026+
1027+ return watchContext . cleanupPromise ;
1028+ } ;
1029+
1030+ const registerWatchCleanup = ( ) : void => {
1031+ if ( watchContext . cleanupRegistered ) {
1032+ return ;
1033+ }
10131034
10141035 for ( const signal of [ 'SIGINT' , 'SIGTERM' , 'SIGTSTP' ] as const ) {
10151036 process . once ( signal , ( ) => {
1016- void cleanup ( ) ;
1037+ void cleanupWatchRuntime ( ) ;
10171038 } ) ;
10181039 }
10191040
10201041 process . once ( 'exit' , ( ) => {
1021- void cleanup ( ) ;
1042+ void cleanupWatchRuntime ( ) ;
10221043 } ) ;
10231044
10241045 watchContext . cleanupRegistered = true ;
@@ -1718,6 +1739,7 @@ export const runBrowserController = async (
17181739 await notifyTestRunStart ( ) ;
17191740
17201741 const isWatchMode = context . command === 'watch' ;
1742+ const enableCliShortcuts = isWatchMode && isBrowserWatchCliShortcutsEnabled ( ) ;
17211743 const tempDir =
17221744 isWatchMode && watchContext . runtime
17231745 ? watchContext . runtime . tempDir
@@ -1771,6 +1793,12 @@ export const runBrowserController = async (
17711793 if ( isWatchMode ) {
17721794 watchContext . runtime = runtime ;
17731795 registerWatchCleanup ( ) ;
1796+
1797+ if ( enableCliShortcuts && ! watchContext . closeCliShortcuts ) {
1798+ watchContext . closeCliShortcuts = await setupBrowserWatchCliShortcuts ( {
1799+ close : cleanupWatchRuntime ,
1800+ } ) ;
1801+ }
17741802 }
17751803 }
17761804
@@ -2411,6 +2439,7 @@ export const runBrowserController = async (
24112439 ? [ rerunFatalError ]
24122440 : undefined ,
24132441 } ) ;
2442+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
24142443 }
24152444 } ,
24162445 onError : async ( error ) => {
@@ -2455,6 +2484,7 @@ export const runBrowserController = async (
24552484 logger . log (
24562485 color . cyan ( 'No browser test files remain after update.\n' ) ,
24572486 ) ;
2487+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
24582488 return ;
24592489 }
24602490
@@ -2473,6 +2503,7 @@ export const runBrowserController = async (
24732503 'No affected browser test files detected, skipping re-run.\n' ,
24742504 ) ,
24752505 ) ;
2506+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
24762507 return ;
24772508 }
24782509
@@ -2531,11 +2562,7 @@ export const runBrowserController = async (
25312562
25322563 if ( isWatchMode && triggerRerun ) {
25332564 watchContext . hooksEnabled = true ;
2534- logger . log (
2535- color . cyan (
2536- '\nWatch mode enabled - will re-run tests on file changes\n' ,
2537- ) ,
2538- ) ;
2565+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
25392566 }
25402567
25412568 return result ;
@@ -2883,9 +2910,13 @@ export const runBrowserController = async (
28832910 ? [ rerunFatalError ]
28842911 : undefined ,
28852912 } ) ;
2913+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
28862914 }
28872915 } else if ( ! rerunPlan . filesChanged ) {
28882916 logger . log ( color . cyan ( 'Tests will be re-executed automatically\n' ) ) ;
2917+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
2918+ } else {
2919+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
28892920 }
28902921 } ;
28912922 }
@@ -2946,9 +2977,7 @@ export const runBrowserController = async (
29462977 // Enable watch hooks AFTER initial test run to avoid duplicate runs
29472978 if ( isWatchMode && triggerRerun ) {
29482979 watchContext . hooksEnabled = true ;
2949- logger . log (
2950- color . cyan ( '\nWatch mode enabled - will re-run tests on file changes\n' ) ,
2951- ) ;
2980+ logBrowserWatchReadyMessage ( enableCliShortcuts ) ;
29522981 }
29532982
29542983 return result ;
0 commit comments