mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Strips onnxruntime-node (linux/win32), claude-agent-sdk ripgrep (non-target platforms), node-pty prebuilds, and sharp libvips from the app.asar.unpacked directory during afterPack. Saves ~80 MB on macOS arm64 builds.
152 lines
4.9 KiB
JavaScript
152 lines
4.9 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const EXECUTABLE_NAME = "Paseo";
|
|
const WRAPPER_MODE = 0o755;
|
|
const WRAPPER_SCRIPT = `#!/bin/bash
|
|
exec "$(dirname "$(readlink -f "$0")")/${EXECUTABLE_NAME}.bin" --no-sandbox "$@"
|
|
`;
|
|
|
|
// electron-builder arch enum → Node.js arch string
|
|
const ARCH_MAP = { 0: "ia32", 1: "x64", 2: "armv7l", 3: "arm64", 4: "universal" };
|
|
|
|
const RIPGREP_PLATFORM_DIR = {
|
|
darwin: { arm64: "arm64-darwin", x64: "x64-darwin" },
|
|
linux: { arm64: "arm64-linux", x64: "x64-linux" },
|
|
win32: { arm64: "arm64-win32", x64: "x64-win32" },
|
|
};
|
|
|
|
function rmSafe(target) {
|
|
fs.rmSync(target, { recursive: true, force: true });
|
|
}
|
|
|
|
function pruneChildrenExcept(parent, keep) {
|
|
if (!fs.existsSync(parent)) return;
|
|
for (const entry of fs.readdirSync(parent)) {
|
|
if (!keep.has(entry)) {
|
|
rmSafe(path.join(parent, entry));
|
|
}
|
|
}
|
|
}
|
|
|
|
function pruneOnnxRuntime(nodeModules, platform, arch) {
|
|
const onnxBin = path.join(nodeModules, "onnxruntime-node", "bin", "napi-v6");
|
|
if (!fs.existsSync(onnxBin)) return;
|
|
|
|
const otherPlatforms = ["darwin", "linux", "win32"].filter((p) => p !== platform);
|
|
for (const p of otherPlatforms) {
|
|
rmSafe(path.join(onnxBin, p));
|
|
}
|
|
|
|
pruneChildrenExcept(path.join(onnxBin, platform), new Set([arch]));
|
|
|
|
if (platform === "linux") {
|
|
const archDir = path.join(onnxBin, "linux", arch);
|
|
if (fs.existsSync(archDir)) {
|
|
for (const name of fs.readdirSync(archDir)) {
|
|
if (name.includes("cuda") || name.includes("tensorrt")) {
|
|
fs.rmSync(path.join(archDir, name), { force: true });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function pruneClaudeAgentSdk(nodeModules, platform, arch) {
|
|
const vendorRoot = path.join(nodeModules, "@anthropic-ai", "claude-agent-sdk", "vendor");
|
|
const keepName = RIPGREP_PLATFORM_DIR[platform]?.[arch];
|
|
if (!keepName) return;
|
|
|
|
pruneChildrenExcept(path.join(vendorRoot, "ripgrep"), new Set(["COPYING", keepName]));
|
|
pruneChildrenExcept(path.join(vendorRoot, "tree-sitter-bash"), new Set([keepName]));
|
|
}
|
|
|
|
function pruneNodePty(nodeModules, platform, arch) {
|
|
const prebuilds = path.join(nodeModules, "node-pty", "prebuilds");
|
|
pruneChildrenExcept(prebuilds, new Set([`${platform}-${arch}`]));
|
|
|
|
if (platform !== "win32") {
|
|
rmSafe(path.join(nodeModules, "node-pty", "third_party"));
|
|
}
|
|
}
|
|
|
|
function pruneSharpLibvips(nodeModules, platform, arch) {
|
|
const prefix = `sharp-libvips-${platform}-${arch}`;
|
|
const imgDir = path.join(nodeModules, "@img");
|
|
if (!fs.existsSync(imgDir)) return;
|
|
|
|
for (const entry of fs.readdirSync(imgDir)) {
|
|
if (entry.startsWith("sharp-") && entry !== prefix && !entry.startsWith(`sharp-${platform}-${arch}`)) {
|
|
rmSafe(path.join(imgDir, entry));
|
|
}
|
|
}
|
|
}
|
|
|
|
function pruneNativeModules(appOutDir, platform, arch) {
|
|
const resourcesDir =
|
|
platform === "darwin"
|
|
? path.join(appOutDir, `${EXECUTABLE_NAME}.app`, "Contents", "Resources")
|
|
: path.join(appOutDir, "resources");
|
|
|
|
const nodeModules = path.join(resourcesDir, "app.asar.unpacked", "node_modules");
|
|
if (!fs.existsSync(nodeModules)) return;
|
|
|
|
const before = dirSizeSync(nodeModules);
|
|
|
|
pruneOnnxRuntime(nodeModules, platform, arch);
|
|
pruneClaudeAgentSdk(nodeModules, platform, arch);
|
|
pruneNodePty(nodeModules, platform, arch);
|
|
pruneSharpLibvips(nodeModules, platform, arch);
|
|
|
|
const after = dirSizeSync(nodeModules);
|
|
const savedMB = ((before - after) / 1024 / 1024).toFixed(1);
|
|
console.log(`Pruned native modules: ${savedMB} MB removed (${fmtMB(before)} → ${fmtMB(after)})`);
|
|
}
|
|
|
|
function dirSizeSync(dir) {
|
|
let total = 0;
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true, recursive: true })) {
|
|
if (entry.isFile()) {
|
|
try {
|
|
total += fs.statSync(path.join(entry.parentPath || entry.path, entry.name)).size;
|
|
} catch {}
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
function fmtMB(bytes) {
|
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
}
|
|
|
|
exports.default = async function afterPack(context) {
|
|
const platform = context.electronPlatformName;
|
|
const arch = ARCH_MAP[context.arch] || process.arch;
|
|
|
|
pruneNativeModules(context.appOutDir, platform, arch);
|
|
|
|
if (platform !== "linux") return;
|
|
|
|
const chromeSandbox = path.join(context.appOutDir, "chrome-sandbox");
|
|
if (fs.existsSync(chromeSandbox)) {
|
|
fs.unlinkSync(chromeSandbox);
|
|
console.log("Removed chrome-sandbox from Linux build");
|
|
}
|
|
|
|
const executablePath = path.join(context.appOutDir, EXECUTABLE_NAME);
|
|
const wrappedBinaryPath = path.join(context.appOutDir, `${EXECUTABLE_NAME}.bin`);
|
|
|
|
if (!fs.existsSync(wrappedBinaryPath)) {
|
|
if (!fs.existsSync(executablePath)) {
|
|
throw new Error(`Expected Linux executable at ${executablePath}`);
|
|
}
|
|
|
|
fs.renameSync(executablePath, wrappedBinaryPath);
|
|
console.log(`Renamed ${EXECUTABLE_NAME} to ${EXECUTABLE_NAME}.bin for Linux wrapper`);
|
|
}
|
|
|
|
fs.writeFileSync(executablePath, WRAPPER_SCRIPT, { mode: WRAPPER_MODE });
|
|
fs.chmodSync(executablePath, WRAPPER_MODE);
|
|
console.log(`Created Linux wrapper for ${EXECUTABLE_NAME} with --no-sandbox`);
|
|
};
|