11import { info } from "@rnx-kit/console" ;
22import type { MetroTerminal } from "@rnx-kit/metro-service" ;
3+ import * as fs from "node:fs" ;
4+ import type { Server } from "node:http" ;
5+ import * as path from "node:path" ;
36import readline from "node:readline" ;
47import qrcode from "qrcode" ;
58import type { DevServerMiddleware } from "./types" ;
69
7- type Options = {
10+ type OpenDebuggerKeyboardHandler = {
11+ handleOpenDebugger : ( ) => Promise < void > ;
12+ maybeHandleTargetSelection : ( key : string ) => boolean ;
13+ dismiss : ( ) => void ;
14+ } ;
15+
16+ type Params = {
817 devServerUrl : string ;
918 help : ( ) => void ;
1019 messageSocketEndpoint : DevServerMiddleware [ "messageSocketEndpoint" ] ;
11- terminal : MetroTerminal [ "terminal" ] ;
20+ metroTerminal : MetroTerminal ;
21+ reactNativePath : string ;
1222} ;
1323
14- export function attachKeyHandlers ( {
24+ function createOpenDebuggerKeyboardHandler ( {
1525 devServerUrl,
16- help,
17- messageSocketEndpoint,
18- terminal,
19- } : Options ) {
26+ metroTerminal : { reporter } ,
27+ reactNativePath,
28+ } : Params ) : OpenDebuggerKeyboardHandler {
29+ const resolvedPath = fs . lstatSync ( reactNativePath ) . isSymbolicLink ( )
30+ ? path . resolve (
31+ path . dirname ( reactNativePath ) ,
32+ fs . readlinkSync ( reactNativePath )
33+ )
34+ : reactNativePath ;
35+ try {
36+ // Available starting with 0.76
37+ const cliPlugin = require . resolve (
38+ "@react-native/community-cli-plugin/package.json" ,
39+ { paths : [ resolvedPath ] }
40+ ) ;
41+ const OpenDebuggerKeyboardHandler = require (
42+ `${ path . dirname ( cliPlugin ) } /dist/commands/start/OpenDebuggerKeyboardHandler`
43+ ) ;
44+ return new OpenDebuggerKeyboardHandler ( { devServerUrl, reporter } ) ;
45+ } catch ( _ ) {
46+ return {
47+ handleOpenDebugger : ( ) => {
48+ info ( "Opening debugger..." ) ;
49+ fetch ( devServerUrl + "/open-debugger" , { method : "POST" } ) ;
50+ return Promise . resolve ( ) ;
51+ } ,
52+ maybeHandleTargetSelection : ( _ : string ) : boolean => false ,
53+ dismiss : ( ) => undefined ,
54+ } ;
55+ }
56+ }
57+
58+ export function attachKeyHandlers ( server : Server , params : Params ) {
59+ const openDebuggerKeyboardHandler = createOpenDebuggerKeyboardHandler ( params ) ;
60+ const {
61+ devServerUrl,
62+ help,
63+ messageSocketEndpoint,
64+ metroTerminal : { terminal } ,
65+ } = params ;
66+
67+ process . on ( "SIGINT" , ( ) => {
68+ openDebuggerKeyboardHandler . dismiss ( ) ;
69+ process . stdin . pause ( ) ;
70+ process . stdin . setRawMode ( false ) ;
71+ info ( "Exiting..." ) ;
72+ server . close ( ) ;
73+ server . closeAllConnections ?.( ) ; // This method was added in Node v18.2.0
74+
75+ // Even when we close all connections, clients may keep the server alive.
76+ process . exit ( ) ;
77+ } ) ;
78+
2079 process . stdin . setRawMode ( true ) ;
2180 process . stdin . on ( "keypress" , ( _key , data ) => {
2281 const { ctrl, name } = data ;
82+ if ( openDebuggerKeyboardHandler . maybeHandleTargetSelection ( name ) ) {
83+ return ;
84+ }
85+
2386 if ( ctrl === true ) {
2487 switch ( name ) {
2588 case "c" :
26- info ( "Exiting..." ) ;
27- process . exit ( ) ;
28- break ;
29- case "z" :
30- process . emit ( "SIGTSTP" , "SIGTSTP" ) ;
89+ case "d" :
90+ process . emit ( "SIGINT" ) ;
3191 break ;
3292 }
3393 } else {
@@ -41,11 +101,9 @@ export function attachKeyHandlers({
41101 help ( ) ;
42102 break ;
43103
44- case "j" : {
45- info ( "Opening debugger..." ) ;
46- fetch ( devServerUrl + "/open-debugger" , { method : "POST" } ) ;
104+ case "j" :
105+ openDebuggerKeyboardHandler . handleOpenDebugger ( ) ;
47106 break ;
48- }
49107
50108 case "q" : {
51109 const url = `${ devServerUrl } /index.bundle` ;
0 commit comments