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 // v8::External::New()/->Value() gained a mandatory ExternalPointerTypeTag // argument in V8 15 (Electron 43+). Plain Node (V8 <= 13.x as of Node 24) // still uses the old 2-arg signatures, so this must be conditional rather // than assumed - a build can target either header set. #include #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >= 15 # define NAN_EXTERNAL_TAG_ARG , static_cast(0) # define NAN_EXTERNAL_TAG_PARAM static_cast(0) #else # define NAN_EXTERNAL_TAG_ARG # define NAN_EXTERNAL_TAG_PARAM #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 a form that // passes NAN_EXTERNAL_TAG_ARG - a macro (defined in the nan.h patch above) // that expands to the ExternalPointerTypeTag argument only when the target // V8 headers actually declare it (V8 15+ / Electron 43+). 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; if (!src.includes("NAN_EXTERNAL_TAG_ARG")) { src = src.replace( /v8::External::New\(v8::Isolate::GetCurrent\(\),\s*value(?:,\s*static_cast\(0\))?\)/g, `v8::External::New(v8::Isolate::GetCurrent(), value NAN_EXTERNAL_TAG_ARG)`, ); src = src.replace( /v8::External::New\(isolate,\s*reinterpret_cast\(callback\)(?:,\s*static_cast\(0\))?\)/g, `v8::External::New(isolate, reinterpret_cast(callback) NAN_EXTERNAL_TAG_ARG)`, ); } if (src !== before) { fs.writeFileSync(implPath, src); implPatched = true; } } // 3. nan_callbacks_12_inl.h: replace ->Value() with ->Value(NAN_EXTERNAL_TAG_PARAM) // on v8::External, same conditional-tag reasoning as above. 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; if (!src.includes("NAN_EXTERNAL_TAG_PARAM")) { // Pattern: .As()->Value()) or ->Value()) src = src.replace( /\.As\(\)->Value\((?:static_cast\(0\))?\)\)/g, `.As()->Value(NAN_EXTERNAL_TAG_PARAM))`, ); } 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"); }