diff --git a/packages/wrangler/src/cli-hotkeys.ts b/packages/wrangler/src/cli-hotkeys.ts new file mode 100644 index 000000000000..a9dfda46c893 --- /dev/null +++ b/packages/wrangler/src/cli-hotkeys.ts @@ -0,0 +1,30 @@ +import readline from "readline"; + +type KeypressEvent = { + name: string; + ctrl: boolean; +}; + +export function hotkeys( + callback: (key: string) => void, + stdin = process.stdin +) { + if (stdin.isTTY) { + readline.emitKeypressEvents(stdin); + stdin.setRawMode(true); + } + + const onKeyPress = (char: string, key: KeypressEvent) => { + if (key && key.ctrl && key.name == "c") { + char = "CTRL+C"; + } + + if (char) { + callback(char); + } + }; + + stdin.on("keypress", onKeyPress); + + return () => stdin.off("keypress", onKeyPress); +} diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 5eb9d832c4a3..7342e5528fa9 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -5,18 +5,21 @@ import { watch } from "chokidar"; import getPort from "get-port"; import { render } from "ink"; import { DevEnv } from "./api"; +import { hotkeys } from "./cli-hotkeys"; import { findWranglerToml, printBindings, readConfig } from "./config"; import { getEntry } from "./deployment-bundle/entry"; import { validateNodeCompat } from "./deployment-bundle/node-compat"; import Dev from "./dev/dev"; import { getVarsForDev } from "./dev/dev-vars"; import { getLocalPersistencePath } from "./dev/get-local-persistence-path"; +import { openInspector } from "./dev/inspect"; import { maybeRegisterLocalWorker } from "./dev/local"; import { startDevServer } from "./dev/start-server"; import { UserError } from "./errors"; import { run } from "./experimental-flags"; import { logger } from "./logger"; import * as metrics from "./metrics"; +import openInBrowser from "./open-in-browser"; import { getAssetPaths, getSiteAssetPaths } from "./sites"; import { getAccountFromCache, loginOrRefreshIfRequired } from "./user"; import { collectKeyValues } from "./utils/collectKeyValues"; @@ -506,6 +509,57 @@ export async function startDev(args: StartDevOptions) { ); }); } + + function printHotkeyInstructions() { + const remote = devEnv.config.latestConfig?.dev?.remote; + const instructions = `[b] open a browser, [d] open devtools, [l] turn ${remote ? "on" : "off"} local mode, [c] clear console, [x] to exit`; + + logger.log( + `╭──${"─".repeat(instructions.length)}──╮\n` + + `│ ${instructions} │\n` + + `╰──${"─".repeat(instructions.length)}──╯\n` + ); + } + + printHotkeyInstructions(); + hotkeys(async (key) => { + switch (key.toLowerCase()) { + case "b": { + const { proxyWorker } = await devEnv.proxy.ready.promise; + const url = await proxyWorker.ready; // TODO: get url from line above when https://github.com/cloudflare/workers-sdk/pull/6124 is merged + await openInBrowser(url.href); + break; + } + case "d": { + // TODO: get inspector url like above when https://github.com/cloudflare/workers-sdk/pull/6124 is merged + // await openInspector(port, props.worker); + break; + } + case "l": { + // TODO: implement forceLocal + devEnv.config.patch({ + dev: { + ...devEnv.config.latestConfig?.dev, + remote: !devEnv.config.latestConfig?.dev?.remote, + }, + }); + console.clear(); + printHotkeyInstructions(); + break; + } + case "c": { + console.clear(); + printHotkeyInstructions(); + break; + } + case "q": + case "x": + case "ctrl+c": { + await devEnv.teardown(); + process.exit(); + } + } + }); } // eslint-disable-next-line no-inner-declarations diff --git a/packages/wrangler/src/dev/dev.tsx b/packages/wrangler/src/dev/dev.tsx index f285ebb5175c..86749c278137 100644 --- a/packages/wrangler/src/dev/dev.tsx +++ b/packages/wrangler/src/dev/dev.tsx @@ -378,6 +378,7 @@ function InteractiveDevSession(props: DevProps) { localProtocol: props.localProtocol, forceLocal: props.forceLocal, worker: props.name, + experimentalDevEnv: props.experimentalDevEnv, }); ip = props.initialIp; @@ -396,26 +397,31 @@ function InteractiveDevSession(props: DevProps) { return ( <> - - [b] - open a browser, - {props.inspect ? ( - <> - [d] - open Devtools, - - ) : null} - {!props.forceLocal ? ( - <> - [l] - {toggles.local ? "turn off" : "turn on"} local mode, - - ) : null} - [c] - clear console, - [x] - to exit - + {!props.experimentalDevEnv && ( + + [b] + open a browser, + {props.inspect ? ( + <> + [d] + open Devtools, + + ) : null} + {!props.forceLocal ? ( + <> + [l] + + {" "} + {toggles.local ? "turn off" : "turn on"} local mode,{" "} + + + ) : null} + [c] + clear console, + [x] + to exit + + )} ); } @@ -986,7 +992,12 @@ function useHotkeys(props: { localProtocol: "http" | "https"; forceLocal: boolean | undefined; worker: string | undefined; + experimentalDevEnv: boolean; }) { + if (props.experimentalDevEnv) { + return props.initial; + } + const { initial, inspectorPort, inspect, localProtocol, forceLocal } = props; // UGH, we should put port in context instead const [toggles, setToggles] = useState(initial);