fix: patch node-domexception in bundled openclaw 3.1 to avoid ESM loader crash (#264)
This commit is contained in:
committed by
GitHub
Unverified
parent
e40f4b2163
commit
f17ffe32c7
@@ -138,6 +138,44 @@ function cleanupNativePlatformPackages(nodeModulesDir, platform, arch) {
|
|||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Broken module patcher ─────────────────────────────────────────────────────
|
||||||
|
// Some bundled packages have transpiled CJS that sets `module.exports = exports.default`
|
||||||
|
// without ever assigning `exports.default`, leaving module.exports === undefined.
|
||||||
|
// This causes `TypeError: Cannot convert undefined or null to object` in Node.js 22+
|
||||||
|
// ESM interop (translators.js hasOwnProperty call). We patch these after copying.
|
||||||
|
|
||||||
|
const MODULE_PATCHES = {
|
||||||
|
// node-domexception@1.0.0: index.js sets module.exports = undefined.
|
||||||
|
// Node.js 18+ ships DOMException as a built-in; this shim re-exports it.
|
||||||
|
'node-domexception/index.js': [
|
||||||
|
"'use strict';",
|
||||||
|
'// Shim: original transpiled file sets module.exports = exports.default (undefined).',
|
||||||
|
'// Node.js 18+ has DOMException as a built-in global.',
|
||||||
|
'const dom = globalThis.DOMException ||',
|
||||||
|
' class DOMException extends Error {',
|
||||||
|
" constructor(msg, name) { super(msg); this.name = name || 'Error'; }",
|
||||||
|
' };',
|
||||||
|
'module.exports = dom;',
|
||||||
|
'module.exports.DOMException = dom;',
|
||||||
|
'module.exports.default = dom;',
|
||||||
|
].join('\n') + '\n',
|
||||||
|
};
|
||||||
|
|
||||||
|
function patchBrokenModules(nodeModulesDir) {
|
||||||
|
const { writeFileSync } = require('fs');
|
||||||
|
let count = 0;
|
||||||
|
for (const [rel, content] of Object.entries(MODULE_PATCHES)) {
|
||||||
|
const target = join(nodeModulesDir, rel);
|
||||||
|
if (existsSync(target)) {
|
||||||
|
writeFileSync(target, content, 'utf8');
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
console.log(`[after-pack] 🩹 Patched ${count} broken module(s) in ${nodeModulesDir}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Plugin bundler ───────────────────────────────────────────────────────────
|
// ── Plugin bundler ───────────────────────────────────────────────────────────
|
||||||
// Bundles a single OpenClaw plugin (and its transitive deps) from node_modules
|
// Bundles a single OpenClaw plugin (and its transitive deps) from node_modules
|
||||||
// directly into the packaged resources directory. Mirrors the logic in
|
// directly into the packaged resources directory. Mirrors the logic in
|
||||||
@@ -289,6 +327,10 @@ exports.default = async function afterPack(context) {
|
|||||||
cpSync(src, dest, { recursive: true });
|
cpSync(src, dest, { recursive: true });
|
||||||
console.log('[after-pack] ✅ openclaw node_modules copied.');
|
console.log('[after-pack] ✅ openclaw node_modules copied.');
|
||||||
|
|
||||||
|
// Patch broken modules whose CJS transpiled output sets module.exports = undefined,
|
||||||
|
// causing TypeError in Node.js 22+ ESM interop.
|
||||||
|
patchBrokenModules(dest);
|
||||||
|
|
||||||
// 1.1 Bundle OpenClaw plugins directly from node_modules into packaged resources.
|
// 1.1 Bundle OpenClaw plugins directly from node_modules into packaged resources.
|
||||||
// This is intentionally done in afterPack (not extraResources) because:
|
// This is intentionally done in afterPack (not extraResources) because:
|
||||||
// - electron-builder silently skips extraResources entries whose source
|
// - electron-builder silently skips extraResources entries whose source
|
||||||
|
|||||||
@@ -366,7 +366,50 @@ const sizeAfter = getDirSize(OUTPUT);
|
|||||||
echo` Removed ${cleanedCount} files/directories`;
|
echo` Removed ${cleanedCount} files/directories`;
|
||||||
echo` Size: ${formatSize(sizeBefore)} → ${formatSize(sizeAfter)} (saved ${formatSize(sizeBefore - sizeAfter)})`;
|
echo` Size: ${formatSize(sizeBefore)} → ${formatSize(sizeAfter)} (saved ${formatSize(sizeBefore - sizeAfter)})`;
|
||||||
|
|
||||||
// 7. Verify the bundle
|
// 7. Patch known broken packages
|
||||||
|
//
|
||||||
|
// Some packages in the ecosystem have transpiled CJS output that sets
|
||||||
|
// `module.exports = exports.default` without ever assigning `exports.default`,
|
||||||
|
// resulting in `module.exports = undefined`. This causes a TypeError in
|
||||||
|
// Node.js 22+ ESM interop when the translators try to call hasOwnProperty on
|
||||||
|
// the undefined exports object.
|
||||||
|
//
|
||||||
|
// We patch these files in-place after the copy so the bundle is safe to run.
|
||||||
|
function patchBrokenModules(nodeModulesDir) {
|
||||||
|
const patches = {
|
||||||
|
// node-domexception@1.0.0: transpiled index.js leaves module.exports = undefined.
|
||||||
|
// Node.js 18+ ships DOMException as a built-in global, so a simple shim works.
|
||||||
|
'node-domexception/index.js': [
|
||||||
|
`'use strict';`,
|
||||||
|
`// Shim: the original transpiled file sets module.exports = exports.default`,
|
||||||
|
`// (which is undefined), causing TypeError in Node.js 22+ ESM interop.`,
|
||||||
|
`// Node.js 18+ has DOMException as a built-in global.`,
|
||||||
|
`const dom = globalThis.DOMException ||`,
|
||||||
|
` class DOMException extends Error {`,
|
||||||
|
` constructor(msg, name) { super(msg); this.name = name || 'Error'; }`,
|
||||||
|
` };`,
|
||||||
|
`module.exports = dom;`,
|
||||||
|
`module.exports.DOMException = dom;`,
|
||||||
|
`module.exports.default = dom;`,
|
||||||
|
].join('\n'),
|
||||||
|
};
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
for (const [rel, content] of Object.entries(patches)) {
|
||||||
|
const target = path.join(nodeModulesDir, rel);
|
||||||
|
if (fs.existsSync(target)) {
|
||||||
|
fs.writeFileSync(target, content + '\n', 'utf8');
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
echo` 🩹 Patched ${count} broken module(s) in node_modules`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patchBrokenModules(outputNodeModules);
|
||||||
|
|
||||||
|
// 8. Verify the bundle
|
||||||
const entryExists = fs.existsSync(path.join(OUTPUT, 'openclaw.mjs'));
|
const entryExists = fs.existsSync(path.join(OUTPUT, 'openclaw.mjs'));
|
||||||
const distExists = fs.existsSync(path.join(OUTPUT, 'dist', 'entry.js'));
|
const distExists = fs.existsSync(path.join(OUTPUT, 'dist', 'entry.js'));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user