From 7c397e3f8a432e0d46e7fdd9b79a7981212094de Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Tue, 28 Jul 2026 01:47:33 +0800 Subject: [PATCH] fix: preserve architecture in unpacked ASAR path (#1094) --- electron/backend-paths.cjs | 8 +++++ electron/main.cjs | 6 ++-- .../tests/electron/backend-paths.test.ts | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 electron/backend-paths.cjs create mode 100644 src/backend/tests/electron/backend-paths.test.ts diff --git a/electron/backend-paths.cjs b/electron/backend-paths.cjs new file mode 100644 index 00000000..b99791ab --- /dev/null +++ b/electron/backend-paths.cjs @@ -0,0 +1,8 @@ +function getUnpackedAppRoot(appRoot) { + return appRoot.replace( + /app(-[a-z0-9]+)?\.asar(?!\.unpacked)/, + "app$1.asar.unpacked", + ); +} + +module.exports = { getUnpackedAppRoot }; diff --git a/electron/main.cjs b/electron/main.cjs index 2e20608e..64a3255b 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -12,6 +12,7 @@ const { nativeImage, } = require("electron"); const path = require("path"); +const { getUnpackedAppRoot } = require("./backend-paths.cjs"); const fs = require("fs"); const os = require("os"); const https = require("https"); @@ -800,10 +801,7 @@ function getBackendPaths() { // fork() does not go through Electron's asar redirector — use the unpacked path. // On macOS multi-arch builds (mergeASARs: false), electron-builder names the ASAR // app-arm64.asar / app-x64.asar instead of app.asar, so match all variants. - const unpackedRoot = appRoot.replace( - /app(-[a-z0-9]+)?\.asar(?!\.unpacked)/, - "app.asar.unpacked", - ); + const unpackedRoot = getUnpackedAppRoot(appRoot); const backendDir = path.join(unpackedRoot, "dist", "backend", "backend"); return { entryPath: path.join(backendDir, "starter.js"), diff --git a/src/backend/tests/electron/backend-paths.test.ts b/src/backend/tests/electron/backend-paths.test.ts new file mode 100644 index 00000000..a0647e4d --- /dev/null +++ b/src/backend/tests/electron/backend-paths.test.ts @@ -0,0 +1,33 @@ +import { createRequire } from "node:module"; +import { describe, expect, it } from "vitest"; + +const require = createRequire(import.meta.url); +const { getUnpackedAppRoot } = + require("../../../../electron/backend-paths.cjs") as { + getUnpackedAppRoot: (appRoot: string) => string; + }; + +describe("getUnpackedAppRoot", () => { + it.each([ + [ + "/Applications/Termix.app/Contents/Resources/app.asar", + "/Applications/Termix.app/Contents/Resources/app.asar.unpacked", + ], + [ + "/Applications/Termix.app/Contents/Resources/app-arm64.asar", + "/Applications/Termix.app/Contents/Resources/app-arm64.asar.unpacked", + ], + [ + "/Applications/Termix.app/Contents/Resources/app-x64.asar", + "/Applications/Termix.app/Contents/Resources/app-x64.asar.unpacked", + ], + ])("maps %s to its matching unpacked directory", (appRoot, expected) => { + expect(getUnpackedAppRoot(appRoot)).toBe(expected); + }); + + it("does not append the suffix twice", () => { + const appRoot = + "/Applications/Termix.app/Contents/Resources/app-arm64.asar.unpacked"; + expect(getUnpackedAppRoot(appRoot)).toBe(appRoot); + }); +});