From fdc2712084ee2af256eba848ae56cf75ec441a9a Mon Sep 17 00:00:00 2001 From: john Date: Sun, 12 Jul 2026 12:04:02 +0800 Subject: [PATCH] fix: bundle Linux ARM64 resvg runtime dependency --- scripts/build-portal-runtime.mjs | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/scripts/build-portal-runtime.mjs b/scripts/build-portal-runtime.mjs index 6914e37..a1b7879 100755 --- a/scripts/build-portal-runtime.mjs +++ b/scripts/build-portal-runtime.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node import fs from 'node:fs/promises'; +import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawn } from 'node:child_process'; @@ -16,6 +17,7 @@ const skipBuild = process.argv.includes('--skip-build'); const skipNodeModules = process.argv.includes('--skip-node-modules'); const runtimeNodeTarget = process.env.PORTAL_RUNTIME_NODE_TARGET || 'node24'; const runtimeInstallMode = process.env.PORTAL_RUNTIME_INSTALL_MODE || 'bundle-node-modules'; +const linuxArm64ResvgPackage = '@resvg/resvg-js-linux-arm64-gnu'; const externalPackages = [ '@img/sharp-darwin-arm64', @@ -195,6 +197,39 @@ async function copyNodeModules() { console.log('==> 拷贝生产运行依赖 node_modules'); await copyDir(resolvedNodeModulesDir, path.join(runtimeRoot, 'node_modules')); await rewriteNodeModulesSymlinks(path.join(runtimeRoot, 'node_modules'), resolvedNodeModulesDir); + await installLinuxArm64ResvgBinary(); +} + +// The runtime artifact is assembled on macOS but runs in a Linux ARM64 Colima VM. +// pnpm therefore copies the macOS optional resvg binary only. Keep the target +// native package alongside the copied node_modules so the dynamic require in +// @resvg/resvg-js resolves in production. +async function installLinuxArm64ResvgBinary() { + const resvgPackageJson = JSON.parse( + await fs.readFile(path.join(nodeModulesDir, '@resvg', 'resvg-js', 'package.json'), 'utf8'), + ); + const version = resvgPackageJson.optionalDependencies?.[linuxArm64ResvgPackage]; + if (!version) { + throw new Error(`无法确定 ${linuxArm64ResvgPackage} 的版本`); + } + + const stagingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-resvg-linux-arm64-')); + const targetDir = path.join(runtimeRoot, 'node_modules', '@resvg', 'resvg-js-linux-arm64-gnu'); + try { + console.log(`==> 补齐 Linux ARM64 resvg 原生依赖 (${version})`); + await run('npm', ['pack', `${linuxArm64ResvgPackage}@${version}`, '--pack-destination', stagingDir]); + const archive = (await fs.readdir(stagingDir)).find((name) => name.endsWith('.tgz')); + if (!archive) throw new Error(`未生成 ${linuxArm64ResvgPackage} 安装包`); + await remove(targetDir); + await fs.mkdir(targetDir, { recursive: true }); + await run('tar', ['-xzf', path.join(stagingDir, archive), '--strip-components=1', '-C', targetDir]); + const files = await fs.readdir(targetDir); + if (!files.some((name) => name.endsWith('.node'))) { + throw new Error(`${linuxArm64ResvgPackage} 缺少原生 .node 文件`); + } + } finally { + await remove(stagingDir); + } } async function rewriteNodeModulesSymlinks(runtimeNodeModulesDir, sourceNodeModulesDir) {