diff --git a/README.md b/README.md index f27404c..15cd0c3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Wrapper de Electron para empaquetar una instalación de NodeCG que incluya el bundle `scoreko-dev`, inspirado en `opeik/runback-electron` pero actualizado a Electron + TypeScript moderno. +Por defecto, el wrapper arranca NodeCG con **Node del sistema** (`node`), pensado para que uses Node 22 de forma consistente en todo el stack. + ## Qué hace - Arranca `lib/nodecg/index.js` como proceso hijo desde Electron. @@ -56,6 +58,31 @@ npm rebuild better-sqlite3 --update-binary 3. Si persiste, elimina `node_modules` del workspace que falla y vuelve a instalar. + +## Runtime de NodeCG (Node 22 recomendado) + +Por defecto: +- `NODECG_USE_SYSTEM_NODE=true` +- `NODECG_NODE_BINARY=node` + +Así NodeCG se ejecuta con tu Node del sistema (recomendado: Node 22). + +Ejemplo en PowerShell para forzar Node 22: + +```powershell +$env:NODECG_USE_SYSTEM_NODE="true" +$env:NODECG_NODE_BINARY="C:\Program Files\nodejs\node.exe" +node -v # debería mostrar v22.x +npm run dev +``` + +Si quieres volver al Node interno de Electron: + +```powershell +$env:NODECG_USE_SYSTEM_NODE="false" +npm run dev +``` + ## Variables de entorno opcionales Estas variables se leen en `src/main/main.ts`. @@ -65,6 +92,8 @@ Estas variables se leen en `src/main/main.ts`. - `SCOREKO_DASHBOARD_ROUTE` (default: `dashboard/index.html`) - `ELECTRON_LOAD_DELAY_MS` (default: `2500`) - `NODECG_STARTUP_TIMEOUT_MS` (default: `30000`) +- `NODECG_USE_SYSTEM_NODE` (default: `true`) +- `NODECG_NODE_BINARY` (default: `node`) Ejemplo (PowerShell): diff --git a/package.json b/package.json index 39f9771..5a47400 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ } }, "engines": { - "node": ">=20" + "node": ">=22" }, "dependencies": { "source-map-support": "^0.5.21" diff --git a/src/main/main.ts b/src/main/main.ts index cdf51b7..130d533 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,5 +1,5 @@ import { app, BrowserWindow, dialog, shell } from "electron"; -import { ChildProcess, fork } from "node:child_process"; +import { ChildProcess, spawn } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -9,6 +9,8 @@ const DEFAULT_BUNDLE_NAME = process.env.NODECG_BUNDLE_NAME ?? "scoreko-dev"; const DEFAULT_DASHBOARD_ROUTE = process.env.SCOREKO_DASHBOARD_ROUTE ?? "dashboard/index.html"; const LOAD_DELAY_MS = Number.parseInt(process.env.ELECTRON_LOAD_DELAY_MS ?? "2500", 10); const STARTUP_TIMEOUT_MS = Number.parseInt(process.env.NODECG_STARTUP_TIMEOUT_MS ?? "30000", 10); +const USE_SYSTEM_NODE = (process.env.NODECG_USE_SYSTEM_NODE ?? "true").toLowerCase() !== "false"; +const NODE_BINARY = process.env.NODECG_NODE_BINARY ?? "node"; const isDev = !app.isPackaged; const rootPath = isDev ? path.resolve(__dirname, "../..") : process.resourcesPath; @@ -130,15 +132,14 @@ function enrichNodeCGFailureMessage(baseMessage: string): string { return [ baseMessage, "", - "Detectado error de módulos nativos compilados para otra versión de Node/Electron (NODE_MODULE_VERSION).", - "Esto suele pasar cuando NodeCG/bundles fueron instalados con Node 22 pero Electron corre con Node 20 (o viceversa).", + "Detectado error de módulos nativos compilados para otra versión de Node (NODE_MODULE_VERSION).", + USE_SYSTEM_NODE + ? "Estás en modo Node del sistema: asegúrate de lanzar con Node 22 y recompilar dependencias nativas." + : "Estás en modo Node de Electron: alinea versiones o cambia NODECG_USE_SYSTEM_NODE=true.", "", "Solución recomendada (en lib/nodecg):", - " 1) borrar node_modules y lockfile del workspace que falla (si aplica)", - " 2) npm install", - " 3) npm rebuild better-sqlite3 --update-binary", - "", - "También puedes alinear versiones: usar una versión de Electron cuyo Node interno coincida con el usado para compilar addons.", + " 1) npm install", + " 2) npm rebuild better-sqlite3 --update-binary", ].join("\n"); } @@ -148,15 +149,20 @@ function enrichNodeCGFailureMessage(baseMessage: string): string { function startNodeCG(): ChildProcess { validateNodeCGInstall(); - const child = fork(path.join(nodecgPath, "index.js"), { + const indexPath = path.join(nodecgPath, "index.js"); + const runtimeBinary = USE_SYSTEM_NODE ? NODE_BINARY : process.execPath; + const runtimeName = USE_SYSTEM_NODE ? `system node (${NODE_BINARY})` : "electron internal node"; + + const child = spawn(runtimeBinary, [indexPath], { cwd: nodecgPath, env: { ...process.env, - ELECTRON_RUN_AS_NODE: "1", NODE_ENV: isDev ? "development" : "production", NODECG_PORT: DEFAULT_NODECG_PORT, + ...(USE_SYSTEM_NODE ? {} : { ELECTRON_RUN_AS_NODE: "1" }), }, - stdio: ["ignore", "pipe", "pipe", "ipc"], + stdio: ["ignore", "pipe", "pipe"], + shell: process.platform === "win32", }); child.stdout?.on("data", (chunk) => { @@ -171,7 +177,7 @@ function startNodeCG(): ChildProcess { lastNodeCGOutput = `${lastNodeCGOutput}${text}`.slice(-20000); }); - log(`NodeCG started with pid=${child.pid}`); + log(`NodeCG started with pid=${child.pid} using ${runtimeName}`); child.on("exit", (code, signal) => { log(`NodeCG exited code=${code} signal=${signal ?? "none"}`);