| ... | ... | @@ -1,3 +1,5 @@ |
| 1 | import { spawnSync } from "node:child_process"; |
| 2 | |
| 1 | 3 | import electronmon from "electronmon"; |
| 2 | 4 | import waitOn from "wait-on"; |
| 3 | 5 | |
| ... | ... | @@ -18,7 +20,7 @@ await waitOn({ |
| 18 | 20 | const childEnv = { ...process.env }; |
| 19 | 21 | delete childEnv.ELECTRON_RUN_AS_NODE; |
| 20 | 22 | |
| 21 | | await electronmon({ |
| 23 | const monitor = await electronmon({ |
| 22 | 24 | cwd: desktopDir, |
| 23 | 25 | args: ["dist-electron/main.js"], |
| 24 | 26 | env: { |
| ... | ... | @@ -27,3 +29,45 @@ await electronmon({ |
| 27 | 29 | }, |
| 28 | 30 | electronPath: resolveElectronPath(), |
| 29 | 31 | }); |
| 32 | |
| 33 | let shuttingDown = false; |
| 34 | |
| 35 | function killChildTree(signal) { |
| 36 | if (process.platform === "win32") { |
| 37 | return; |
| 38 | } |
| 39 | |
| 40 | // electronmon may leave Electron orphaned when parent shutdown races; kill direct children as fallback. |
| 41 | spawnSync("pkill", [`-${signal}`, "-P", String(process.pid)], { stdio: "ignore" }); |
| 42 | } |
| 43 | |
| 44 | async function shutdown(exitCode) { |
| 45 | if (shuttingDown) return; |
| 46 | shuttingDown = true; |
| 47 | |
| 48 | try { |
| 49 | await Promise.race([ |
| 50 | monitor.destroy(), |
| 51 | new Promise((resolve) => setTimeout(resolve, 1_500)), |
| 52 | ]); |
| 53 | } catch { |
| 54 | // Best effort only; fallback process kill below handles stubborn children. |
| 55 | } |
| 56 | |
| 57 | killChildTree("TERM"); |
| 58 | setTimeout(() => { |
| 59 | killChildTree("KILL"); |
| 60 | }, 1_200).unref(); |
| 61 | |
| 62 | process.exit(exitCode); |
| 63 | } |
| 64 | |
| 65 | process.once("SIGINT", () => { |
| 66 | void shutdown(130); |
| 67 | }); |
| 68 | process.once("SIGTERM", () => { |
| 69 | void shutdown(143); |
| 70 | }); |
| 71 | process.once("SIGHUP", () => { |
| 72 | void shutdown(129); |
| 73 | }); |