From f81587888f92b436d9e79ef2a6da0270b9d7c945 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 27 May 2026 13:20:11 -0500 Subject: [PATCH] fix: nan patch for electron build error --- package.json | 2 +- scripts/patch-nan.cjs | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 scripts/patch-nan.cjs diff --git a/package.json b/package.json index e5e1a03c..1914fdd6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", - "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-better-sqlite3.cjs", + "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs", "prebuild": "node scripts/write-electron-build-info.cjs", "lint": "eslint .", "lint:fix": "eslint --fix .", diff --git a/scripts/patch-nan.cjs b/scripts/patch-nan.cjs new file mode 100644 index 00000000..4a69da18 --- /dev/null +++ b/scripts/patch-nan.cjs @@ -0,0 +1,114 @@ +const fs = require("node:fs"); +const path = require("node:path"); + +const nanDir = path.join(__dirname, "..", "node_modules", "nan"); + +if (!fs.existsSync(nanDir)) { + console.log("[patch-nan] nan not found, skipping"); + process.exit(0); +} + +function patchFile(filePath, replacements) { + if (!fs.existsSync(filePath)) return false; + + let source = fs.readFileSync(filePath, "utf8"); + let changed = false; + + for (const { original, patched } of replacements) { + if (source.includes(patched)) continue; + if (!source.includes(original)) continue; + source = source.replace(original, patched); + changed = true; + } + + if (changed) fs.writeFileSync(filePath, source); + return changed; +} + +// 1. nan.h: inject MSVC __builtin_frame_address compat before node.h is included. +const nanHeaderPatched = patchFile(path.join(nanDir, "nan.h"), [ + { + original: `#include + +#define NODE_0_10_MODULE_VERSION 11`, + patched: `#include + +// MSVC lacks __builtin_frame_address; cppgc/heap.h (pulled by node.h) uses it. +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address) +# include +# define __builtin_frame_address(level) _AddressOfReturnAddress() +#endif + +#define NODE_0_10_MODULE_VERSION 11`, + }, +]); + +// cpu-features binding.cc includes before , so the nan.h patch +// above is too late. Patch binding.cc directly to inject the compat define first. +const cpuFeaturesDir = path.join(__dirname, "..", "node_modules", "cpu-features"); +const bindingPath = path.join(cpuFeaturesDir, "src", "binding.cc"); +const bindingPatched = patchFile(bindingPath, [ + { + original: `#include `, + patched: `#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address) +#include +#define __builtin_frame_address(level) _AddressOfReturnAddress() +#endif +#include `, + }, +]); + +// 2. nan_implementation_12_inl.h: replace v8::External::New() with the 3-arg form. +// Electron 42 / V8 13+ requires an ExternalPointerTypeTag as the third argument. +const implPath = path.join(nanDir, "nan_implementation_12_inl.h"); +let implPatched = false; +if (fs.existsSync(implPath)) { + let src = fs.readFileSync(implPath, "utf8"); + const before = src; + + const TAG = "static_cast(0)"; + if (!src.includes(TAG)) { + src = src.replace( + /v8::External::New\(v8::Isolate::GetCurrent\(\),\s*value\)/g, + `v8::External::New(v8::Isolate::GetCurrent(), value, ${TAG})` + ); + src = src.replace( + /v8::External::New\(isolate,\s*reinterpret_cast\(callback\)\)/g, + `v8::External::New(isolate, reinterpret_cast(callback), ${TAG})` + ); + } + + if (src !== before) { + fs.writeFileSync(implPath, src); + implPatched = true; + } +} + +// 3. nan_callbacks_12_inl.h: replace ->Value() with ->Value(tag) on v8::External. +// The new API requires an ExternalPointerTypeTag argument. +const callbacksPath = path.join(nanDir, "nan_callbacks_12_inl.h"); +let callbacksPatched = false; +if (fs.existsSync(callbacksPath)) { + let src = fs.readFileSync(callbacksPath, "utf8"); + const before = src; + + const TAG = "static_cast(0)"; + if (!src.includes(TAG)) { + // Pattern: .As()->Value()) — always followed by )) + src = src.replace( + /\.As\(\)->Value\(\)\)/g, + `.As()->Value(${TAG}))` + ); + } + + if (src !== before) { + fs.writeFileSync(callbacksPath, src); + callbacksPatched = true; + } +} + +if (nanHeaderPatched || bindingPatched || implPatched || callbacksPatched) { + console.log("[patch-nan] Applied compatibility patches for Electron 42 / V8 13+"); +} else { + console.log("[patch-nan] Already patched or target code not found"); +}