From 3140463dbd922d7c723cb95b7216c318d1984011 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:00:54 +0200 Subject: [PATCH] fix(cli): support multiple debug targets --- .changeset/calm-adults-provide.md | 5 ++ packages/cli/src/serve/keyboard.ts | 90 ++++++++++++++++++++++++------ packages/cli/src/start.ts | 11 +++- packages/test-app/ios/Podfile.lock | 4 +- 4 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 .changeset/calm-adults-provide.md diff --git a/.changeset/calm-adults-provide.md b/.changeset/calm-adults-provide.md new file mode 100644 index 000000000..0a123721c --- /dev/null +++ b/.changeset/calm-adults-provide.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/cli": patch +--- + +Support multiple debug targets diff --git a/packages/cli/src/serve/keyboard.ts b/packages/cli/src/serve/keyboard.ts index e329962ba..e7525199c 100644 --- a/packages/cli/src/serve/keyboard.ts +++ b/packages/cli/src/serve/keyboard.ts @@ -1,33 +1,93 @@ import { info } from "@rnx-kit/console"; import type { MetroTerminal } from "@rnx-kit/metro-service"; +import * as fs from "node:fs"; +import type { Server } from "node:http"; +import * as path from "node:path"; import readline from "node:readline"; import qrcode from "qrcode"; import type { DevServerMiddleware } from "./types"; -type Options = { +type OpenDebuggerKeyboardHandler = { + handleOpenDebugger: () => Promise; + maybeHandleTargetSelection: (key: string) => boolean; + dismiss: () => void; +}; + +type Params = { devServerUrl: string; help: () => void; messageSocketEndpoint: DevServerMiddleware["messageSocketEndpoint"]; - terminal: MetroTerminal["terminal"]; + metroTerminal: MetroTerminal; + reactNativePath: string; }; -export function attachKeyHandlers({ +function createOpenDebuggerKeyboardHandler({ devServerUrl, - help, - messageSocketEndpoint, - terminal, -}: Options) { + metroTerminal: { reporter }, + reactNativePath, +}: Params): OpenDebuggerKeyboardHandler { + const resolvedPath = fs.lstatSync(reactNativePath).isSymbolicLink() + ? path.resolve( + path.dirname(reactNativePath), + fs.readlinkSync(reactNativePath) + ) + : reactNativePath; + try { + // Available starting with 0.76 + const cliPlugin = require.resolve( + "@react-native/community-cli-plugin/package.json", + { paths: [resolvedPath] } + ); + const OpenDebuggerKeyboardHandler = require( + `${path.dirname(cliPlugin)}/dist/commands/start/OpenDebuggerKeyboardHandler` + ); + return new OpenDebuggerKeyboardHandler({ devServerUrl, reporter }); + } catch (_) { + return { + handleOpenDebugger: () => { + info("Opening debugger..."); + fetch(devServerUrl + "/open-debugger", { method: "POST" }); + return Promise.resolve(); + }, + maybeHandleTargetSelection: (_: string): boolean => false, + dismiss: () => undefined, + }; + } +} + +export function attachKeyHandlers(server: Server, params: Params) { + const openDebuggerKeyboardHandler = createOpenDebuggerKeyboardHandler(params); + const { + devServerUrl, + help, + messageSocketEndpoint, + metroTerminal: { terminal }, + } = params; + + process.on("SIGINT", () => { + openDebuggerKeyboardHandler.dismiss(); + process.stdin.pause(); + process.stdin.setRawMode(false); + info("Exiting..."); + server.close(); + server.closeAllConnections?.(); // This method was added in Node v18.2.0 + + // Even when we close all connections, clients may keep the server alive. + process.exit(); + }); + process.stdin.setRawMode(true); process.stdin.on("keypress", (_key, data) => { const { ctrl, name } = data; + if (openDebuggerKeyboardHandler.maybeHandleTargetSelection(name)) { + return; + } + if (ctrl === true) { switch (name) { case "c": - info("Exiting..."); - process.exit(); - break; - case "z": - process.emit("SIGTSTP", "SIGTSTP"); + case "d": + process.emit("SIGINT"); break; } } else { @@ -41,11 +101,9 @@ export function attachKeyHandlers({ help(); break; - case "j": { - info("Opening debugger..."); - fetch(devServerUrl + "/open-debugger", { method: "POST" }); + case "j": + openDebuggerKeyboardHandler.handleOpenDebugger(); break; - } case "q": { const url = `${devServerUrl}/index.bundle`; diff --git a/packages/cli/src/start.ts b/packages/cli/src/start.ts index 47cb77c45..9311f2c10 100644 --- a/packages/cli/src/start.ts +++ b/packages/cli/src/start.ts @@ -225,7 +225,16 @@ export async function rnxStart( // in interactive mode, listen for keyboard events from stdin and bind // them to specific actions. if (interactive) { - attachKeyHandlers({ devServerUrl, help, messageSocketEndpoint, terminal }); + attachKeyHandlers(serverInstance, { + devServerUrl, + help, + messageSocketEndpoint, + metroTerminal: { + terminal, + reporter: terminalReporter, + }, + reactNativePath: ctx.reactNativePath, + }); } } diff --git a/packages/test-app/ios/Podfile.lock b/packages/test-app/ios/Podfile.lock index 783a795f8..628a42838 100644 --- a/packages/test-app/ios/Podfile.lock +++ b/packages/test-app/ios/Podfile.lock @@ -1484,7 +1484,7 @@ PODS: - ReactTestApp-MSAL (4.0.0): - MSAL - ReactTestApp-Resources (1.0.0-dev) - - RNWWebStorage (0.3.0): + - RNWWebStorage (0.3.1): - DoubleConversion - glog - RCT-Folly (= 2024.01.01.00) @@ -1786,7 +1786,7 @@ SPEC CHECKSUMS: ReactTestApp-DevSupport: 74676edd899013becce4eaecc5eabba1fc51e26e ReactTestApp-MSAL: a7ac8e821fce95fc4e27cd91cf2a931b277ffef3 ReactTestApp-Resources: a4cc1f968cd26bdbd18ee0bfbd0ab8dd0ea90101 - RNWWebStorage: 39af6c7aa24a9360372280338e0f5900630779a2 + RNWWebStorage: 16ea67c1467a5b91c2859c490bec800f001adf78 RNXAuth: 7716515bc74149d226d798138f2d76af9f34427f SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 4ef80d96a5534f0e01b3055f17d1e19a9fc61b63