@@ -807,57 +807,146 @@ function debounce(fn: (...args) => void, time: number) {
807807 } ;
808808}
809809
810+ const WATCHER_RECONNECT_CONFIG = {
811+ initialDelayMs : 100 ,
812+ maxDelayMs : 5000 ,
813+ backoffMultiplier : 1.5 ,
814+ maxAttempts : 30 ,
815+ } ;
816+
817+ async function waitForWatcherReconnectWithExponentialBackoff (
818+ config = WATCHER_RECONNECT_CONFIG
819+ ) : Promise < boolean > {
820+ let delay = config . initialDelayMs ;
821+ let attempts = 0 ;
822+
823+ while ( attempts < config . maxAttempts ) {
824+ try {
825+ // Try to register file watcher
826+ const testUnregister = await daemonClient . registerFileWatcher (
827+ {
828+ watchProjects : 'all' ,
829+ includeGlobalWorkspaceFiles : true ,
830+ allowPartialGraph : true ,
831+ } ,
832+ ( ) => {
833+ // This is just a test connection, close it immediately
834+ }
835+ ) ;
836+ testUnregister ( ) ;
837+ return true ;
838+ } catch ( e ) {
839+ attempts ++ ;
840+ if ( attempts >= config . maxAttempts ) {
841+ return false ;
842+ }
843+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
844+ delay = Math . min ( delay * config . backoffMultiplier , config . maxDelayMs ) ;
845+ }
846+ }
847+
848+ return false ;
849+ }
850+
810851function createFileWatcher ( ) {
811- return daemonClient . registerFileWatcher (
812- {
813- watchProjects : 'all' ,
814- includeGlobalWorkspaceFiles : true ,
815- allowPartialGraph : true ,
816- } ,
817- debounce ( async ( error , changes ) => {
818- if ( error === 'closed' ) {
819- output . error ( { title : `Watch error: Daemon closed the connection` } ) ;
852+ let unregisterCurrentWatcher : ( ( ) => void ) | undefined ;
853+
854+ const watcherCallback = debounce ( async ( error , changes ) => {
855+ if ( error === 'closed' ) {
856+ output . note ( {
857+ title : `Daemon connection closed. Attempting to reconnect...` ,
858+ } ) ;
859+
860+ // Try to reconnect with exponential backoff
861+ const reconnected = await waitForWatcherReconnectWithExponentialBackoff ( ) ;
862+
863+ if ( reconnected ) {
864+ output . note ( {
865+ title : 'Reconnected to daemon. Resuming file watching.' ,
866+ } ) ;
867+ // Re-register the watcher with the new daemon connection
868+ unregisterCurrentWatcher = await daemonClient . registerFileWatcher (
869+ {
870+ watchProjects : 'all' ,
871+ includeGlobalWorkspaceFiles : true ,
872+ allowPartialGraph : true ,
873+ } ,
874+ watcherCallback
875+ ) ;
876+ } else {
877+ output . error ( {
878+ title : `Failed to reconnect to daemon. Stopping file watcher.` ,
879+ } ) ;
820880 process . exit ( 1 ) ;
821- } else if ( error ) {
822- output . error ( { title : `Watch error: ${ error ?. message ?? 'Unknown' } ` } ) ;
823- } else if ( changes !== null && changes . changedFiles . length > 0 ) {
824- output . note ( { title : 'Recalculating project graph...' } ) ;
825-
826- const { projectGraphClientResponse, sourceMapResponse } =
827- await createProjectGraphAndSourceMapClientResponse ( ) ;
828-
829- if (
830- projectGraphClientResponse . hash !==
831- currentProjectGraphClientResponse . hash &&
832- sourceMapResponse
833- ) {
834- if ( projectGraphClientResponse . errors ?. length > 0 ) {
835- projectGraphClientResponse . errors . forEach ( ( e ) => {
836- output . error ( {
837- title : e . message ,
838- bodyLines : [ e . stack ] ,
839- } ) ;
840- } ) ;
841- output . warn ( {
842- title : `${
843- projectGraphClientResponse . errors . length > 1
844- ? `${ projectGraphClientResponse . errors . length } errors`
845- : `An error`
846- } occured while processing the project graph. Showing partial graph.`,
881+ }
882+ } else if ( error ) {
883+ output . error ( { title : `Watch error: ${ error ?. message ?? 'Unknown' } ` } ) ;
884+ } else if ( changes !== null && changes . changedFiles . length > 0 ) {
885+ output . note ( { title : 'Recalculating project graph...' } ) ;
886+
887+ const { projectGraphClientResponse, sourceMapResponse } =
888+ await createProjectGraphAndSourceMapClientResponse ( ) ;
889+
890+ if (
891+ projectGraphClientResponse . hash !==
892+ currentProjectGraphClientResponse . hash &&
893+ sourceMapResponse
894+ ) {
895+ if ( projectGraphClientResponse . errors ?. length > 0 ) {
896+ projectGraphClientResponse . errors . forEach ( ( e ) => {
897+ output . error ( {
898+ title : e . message ,
899+ bodyLines : [ e . stack ] ,
847900 } ) ;
848- }
849- output . note ( { title : 'Graph changes updated.' } ) ;
901+ } ) ;
902+ output . warn ( {
903+ title : `${
904+ projectGraphClientResponse . errors . length > 1
905+ ? `${ projectGraphClientResponse . errors . length } errors`
906+ : `An error`
907+ } occured while processing the project graph. Showing partial graph.`,
908+ } ) ;
909+ }
910+ output . note ( { title : 'Graph changes updated.' } ) ;
850911
851- currentProjectGraphClientResponse = projectGraphClientResponse ;
852- currentSourceMapsClientResponse = sourceMapResponse ;
853- // Clear task graph cache when project graph changes
854- clearTaskGraphCache ( ) ;
912+ currentProjectGraphClientResponse = projectGraphClientResponse ;
913+ currentSourceMapsClientResponse = sourceMapResponse ;
914+ // Clear task graph cache when project graph changes
915+ clearTaskGraphCache ( ) ;
916+ } else {
917+ output . note ( { title : 'No graph changes found.' } ) ;
918+ }
919+ }
920+ } , 500 ) ;
921+
922+ return daemonClient
923+ . registerFileWatcher (
924+ {
925+ watchProjects : 'all' ,
926+ includeGlobalWorkspaceFiles : true ,
927+ allowPartialGraph : true ,
928+ } ,
929+ async ( error , changes ) => {
930+ if ( error === 'closed' ) {
931+ // Store the unregister callback before attempting reconnection
932+ const previousUnregister = unregisterCurrentWatcher ;
933+ await watcherCallback ( error , changes ) ;
934+ if (
935+ unregisterCurrentWatcher &&
936+ unregisterCurrentWatcher !== previousUnregister
937+ ) {
938+ // Successfully reconnected with a new watcher
939+ return ;
940+ }
855941 } else {
856- output . note ( { title : 'No graph changes found.' } ) ;
942+ await watcherCallback ( error , changes ) ;
857943 }
858944 }
859- } , 500 )
860- ) ;
945+ )
946+ . then ( ( unregister ) => {
947+ unregisterCurrentWatcher = unregister ;
948+ return unregister ;
949+ } ) ;
861950}
862951
863952async function createProjectGraphAndSourceMapClientResponse (
0 commit comments