diff --git a/README.md b/README.md index e2bcabb..2724dc4 100644 --- a/README.md +++ b/README.md @@ -42,23 +42,29 @@ cd ../.. > Si no haces `npm install` dentro de `lib/nodecg`, verás errores como `Cannot find module ... node_modules/nodecg/dist/server/bootstrap.js`. -## ¿Existe Electron con Node 22 para standalone? +## ¿Existe Electron con `NODE_MODULE_VERSION 127`? -Sí. Este proyecto usa Electron `35.x`, que permite mantener un runtime moderno y ejecutar NodeCG con el Node embebido de Electron para app standalone. +No en releases estables de Electron. Revisando la tabla oficial de releases de Electron, no aparece `modules=127`. + +- Electron 32.x usa `NODE_MODULE_VERSION 128` +- Electron 34.x usa `NODE_MODULE_VERSION 132` +- Electron 35.x usa `NODE_MODULE_VERSION 133` + +Por eso, para standalone hay que recompilar addons nativos contra la versión de Electron elegida. ## Error típico: NODE_MODULE_VERSION Si ves un error como `better-sqlite3 ... NODE_MODULE_VERSION`, tienes módulos nativos compilados para una versión distinta de Node. -En ese caso recompila dentro de `lib/nodecg` con el runtime objetivo: +En ese caso recompila contra Electron: ```bash -cd lib/nodecg -npm install -npm rebuild better-sqlite3 --update-binary +npm run rebuild:native ``` -Si sigues en modo standalone (default), asegúrate de no mezclar binarios compilados con otro Node fuera de tu versión de Electron. +Este script ejecuta `electron-rebuild` en `lib/nodecg` y en el workspace SQLite legacy (si existe). + +Si sigues en modo standalone (default), asegúrate de no mezclar binarios compilados con otro runtime distinto al de Electron. ## Runtime de NodeCG diff --git a/package-lock.json b/package-lock.json index cf6bf41..3fd8d9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "source-map-support": "^0.5.21" }, "devDependencies": { + "@electron/rebuild": "^3.7.1", "@types/node": "^22.10.5", "concurrently": "^9.1.2", "electron": "^35.7.5", @@ -95,6 +96,88 @@ "global-agent": "^3.0.0" } }, + "node_modules/@electron/node-gyp": { + "version": "10.2.0-electron.1", + "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^8.1.0", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.2.1", + "nopt": "^6.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/node-gyp/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/node-gyp/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/node-gyp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@electron/notarize": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", @@ -223,12 +306,13 @@ } }, "node_modules/@electron/rebuild": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", - "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.2.tgz", + "integrity": "sha512-19/KbIR/DAxbsCkiaGMXIdPnMCJLkcf8AvGnduJtWBs/CBwiAjY1apCqOLVxrXg+rtXFCngbXhBanWjxLUt1Mg==", "dev": true, "license": "MIT", "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", @@ -237,7 +321,6 @@ "got": "^11.7.0", "node-abi": "^3.45.0", "node-api-version": "^0.2.0", - "node-gyp": "^9.0.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", @@ -1040,6 +1123,35 @@ "electron-builder-squirrel-windows": "25.1.8" } }, + "node_modules/app-builder-lib/node_modules/@electron/rebuild": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", + "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -4417,6 +4529,16 @@ "node": ">=10.4.0" } }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", diff --git a/package.json b/package.json index c4853ad..7682e78 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "watch": "tsc -p tsconfig.json --watch", "dev:electron": "wait-on dist/main/main.js && electron .", "pack": "npm run build && electron-builder --dir", - "dist": "npm run build && electron-builder" + "dist": "npm run build && electron-builder", + "rebuild:native": "node scripts/rebuild-nodecg-native.mjs" }, "build": { "appId": "com.scoreko.desktop", @@ -66,6 +67,7 @@ "electron-builder": "^25.1.8", "rimraf": "^6.0.1", "typescript": "^5.7.3", - "wait-on": "^8.0.1" + "wait-on": "^8.0.1", + "@electron/rebuild": "^3.7.1" } } diff --git a/scripts/rebuild-nodecg-native.mjs b/scripts/rebuild-nodecg-native.mjs new file mode 100644 index 0000000..602f61f --- /dev/null +++ b/scripts/rebuild-nodecg-native.mjs @@ -0,0 +1,46 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; +import { spawn } from "node:child_process"; + +const root = process.cwd(); +const candidates = [ + path.join(root, "lib", "nodecg"), + path.join(root, "lib", "nodecg", "workspaces", "database-adapter-sqlite-legacy"), +]; + +const moduleDirs = candidates.filter((dir) => existsSync(path.join(dir, "package.json"))); + +if (moduleDirs.length === 0) { + console.error("No NodeCG package folders found. Expected lib/nodecg and/or workspaces."); + process.exit(1); +} + +function run(command, args, cwd) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd, + stdio: "inherit", + shell: process.platform === "win32", + env: { + ...process.env, + npm_config_runtime: "electron", + npm_config_build_from_source: "false", + }, + }); + + child.on("exit", (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`${command} ${args.join(" ")} failed with code ${code}`)); + } + }); + }); +} + +for (const dir of moduleDirs) { + console.log(`\n[rebuild-native] Rebuilding native modules in: ${dir}`); + await run("npx", ["electron-rebuild", "--force", "--only", "better-sqlite3"], dir); +} + +console.log("\n[rebuild-native] Done.");