| @@ -1,19 +1,84 @@ |
| 1 | import { spawn } from "node:child_process"; | 1 | import { spawn } from "node:child_process"; |
| | 2 | import net from "node:net"; |
| 2 | | 3 | |
| 3 | import waitOn from "wait-on"; | 4 | import waitOn from "wait-on"; |
| 4 | | 5 | |
| 5 | const port = Number(process.env.ELECTRON_RENDERER_PORT ?? 5173); | 6 | const port = Number(process.env.ELECTRON_RENDERER_PORT ?? 5173); |
| 6 | const devServerUrl = `http://localhost:${port}`; | 7 | const STARTUP_TIMEOUT_MS = Number(process.env.T3CODE_ELECTRON_STARTUP_TIMEOUT_MS ?? 120_000); |
| 7 | | 8 | |
| | 9 | async function canConnect(host, probePort, timeoutMs = 1_000) { |
| | 10 | return new Promise((resolve) => { |
| | 11 | const socket = new net.Socket(); |
| | 12 | let settled = false; |
| | 13 | |
| | 14 | const finish = (value) => { |
| | 15 | if (settled) return; |
| | 16 | settled = true; |
| | 17 | socket.destroy(); |
| | 18 | resolve(value); |
| | 19 | }; |
| | 20 | |
| | 21 | socket.setTimeout(timeoutMs); |
| | 22 | socket.once("connect", () => finish(true)); |
| | 23 | socket.once("timeout", () => finish(false)); |
| | 24 | socket.once("error", () => finish(false)); |
| | 25 | socket.connect(probePort, host); |
| | 26 | }); |
| | 27 | } |
| | 28 | |
| | 29 | function waitForDevServer(probePort, timeoutMs) { |
| | 30 | const startedAt = Date.now(); |
| | 31 | const candidates = [ |
| | 32 | { host: "127.0.0.1", url: `http://127.0.0.1:${probePort}` }, |
| | 33 | { host: "::1", url: `http://[::1]:${probePort}` }, |
| | 34 | { host: "localhost", url: `http://localhost:${probePort}` }, |
| | 35 | ]; |
| | 36 | |
| | 37 | return new Promise((resolve, reject) => { |
| | 38 | const tick = () => { |
| | 39 | void Promise.all( |
| | 40 | candidates.map(async (candidate) => ({ |
| | 41 | candidate, |
| | 42 | connected: await canConnect(candidate.host, probePort), |
| | 43 | })), |
| | 44 | ) |
| | 45 | .then((results) => { |
| | 46 | const ready = results.find((entry) => entry.connected); |
| | 47 | if (ready) { |
| | 48 | resolve(ready.candidate.url); |
| | 49 | return; |
| | 50 | } |
| | 51 | |
| | 52 | if (Date.now() - startedAt >= timeoutMs) { |
| | 53 | reject( |
| | 54 | new Error( |
| | 55 | `[dev-electron] timed out after ${timeoutMs}ms waiting for renderer dev server on port ${probePort}`, |
| | 56 | ), |
| | 57 | ); |
| | 58 | return; |
| | 59 | } |
| | 60 | |
| | 61 | setTimeout(tick, 250); |
| | 62 | }) |
| | 63 | .catch((error) => { |
| | 64 | reject(error); |
| | 65 | }); |
| | 66 | }; |
| | 67 | |
| | 68 | tick(); |
| | 69 | }); |
| | 70 | } |
| | 71 | |
| | 72 | console.log("[dev-electron] waiting for desktop/server bundles"); |
| 8 | await waitOn({ | 73 | await waitOn({ |
| 9 | resources: [ | 74 | resources: ["file:dist-electron/main.js", "file:dist-electron/preload.js", "file:../server/dist/index.mjs"], |
| 10 | `tcp:${port}`, | 75 | timeout: STARTUP_TIMEOUT_MS, |
| 11 | "file:dist-electron/main.js", | | |
| 12 | "file:dist-electron/preload.js", | | |
| 13 | "file:../server/dist/index.mjs", | | |
| 14 | ], | | |
| 15 | }); | 76 | }); |
| 16 | | 77 | |
| | 78 | console.log(`[dev-electron] waiting for renderer dev server on port ${port}`); |
| | 79 | const devServerUrl = await waitForDevServer(port, STARTUP_TIMEOUT_MS); |
| | 80 | console.log(`[dev-electron] launching electron with renderer url ${devServerUrl}`); |
| | 81 | |
| 17 | const command = process.platform === "win32" ? "electronmon.cmd" : "electronmon"; | 82 | const command = process.platform === "win32" ? "electronmon.cmd" : "electronmon"; |
| 18 | const childEnv = { ...process.env }; | 83 | const childEnv = { ...process.env }; |
| 19 | delete childEnv.ELECTRON_RUN_AS_NODE; | 84 | delete childEnv.ELECTRON_RUN_AS_NODE; |
| @@ -29,3 +94,8 @@ const child = spawn(command, ["dist-electron/main.js"], { · 9 unmodified lines |
| 29 | child.on("exit", (code) => { | 94 | child.on("exit", (code) => { |
| 30 | process.exit(code ?? 0); | 95 | process.exit(code ?? 0); |
| 31 | }); | 96 | }); |
| | 97 | |
| | 98 | child.on("error", (error) => { |
| | 99 | console.error("[dev-electron] failed to launch electronmon:", error.message); |
| | 100 | process.exit(1); |
| | 101 | }); |