import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, } from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; import { tmpdir } from "node:os"; import { fileURLToPath } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev"; const targetOs = process.env.ELECTROBUN_OS || "macos"; const wrapperPath = process.env.ELECTROBUN_WRAPPER_BUNDLE_PATH; if (buildEnv === "dev" || targetOs !== "macos") { console.log("expand-macos-bundle: skipping (dev or non-macOS build)"); process.exit(0); } if (!wrapperPath || !existsSync(wrapperPath)) { console.error("expand-macos-bundle: ELECTROBUN_WRAPPER_BUNDLE_PATH not set or missing"); process.exit(1); } const resolvedWrapperPath = path.resolve(wrapperPath); const contentsPath = path.join(resolvedWrapperPath, "Contents"); const resourcesPath = path.join(contentsPath, "Resources"); const metadataPath = path.join(resourcesPath, "metadata.json"); if (!existsSync(metadataPath)) { console.log("expand-macos-bundle: no extractor metadata, assuming already expanded"); process.exit(0); } const metadata = JSON.parse(readFileSync(metadataPath, "utf8")); const hash = metadata.hash; const appName = metadata.name || "Farm Control Server"; const tarZstPath = path.resolve(resourcesPath, `${hash}.tar.zst`); if (!existsSync(tarZstPath)) { console.log(`expand-macos-bundle: ${hash}.tar.zst not found, skipping`); process.exit(0); } function decompressTarZst(inputPath, outputPath) { const systemZstd = spawnSync("zstd", ["--version"], { encoding: "utf8" }); if (systemZstd.status === 0) { return spawnSync("zstd", ["-d", "-f", "-o", outputPath, inputPath], { stdio: "inherit", }); } const hostArch = process.arch === "arm64" ? "arm64" : "x64"; const zigZstd = path.join( rootDir, "node_modules/electrobun/dist-macos-" + hostArch, "zig-zstd", ); if (existsSync(zigZstd)) { return spawnSync( zigZstd, ["decompress", "-i", inputPath, "-o", outputPath, "--no-timing"], { stdio: "inherit" }, ); } console.error( "expand-macos-bundle: zstd not found (install zstd or use electrobun dist binaries)", ); process.exit(1); } const workDir = path.join(tmpdir(), `farmcontrol-server-expand-${hash}`); const tarPath = path.join(workDir, `${hash}.tar`); rmSync(workDir, { recursive: true, force: true }); mkdirSync(workDir, { recursive: true }); const decompress = decompressTarZst(tarZstPath, tarPath); if (decompress.status !== 0) { console.error("expand-macos-bundle: zstd decompression failed"); process.exit(decompress.status ?? 1); } const extractTar = spawnSync("tar", ["-xf", tarPath, "-C", workDir], { stdio: "inherit", }); if (extractTar.status !== 0) { console.error("expand-macos-bundle: tar extraction failed"); process.exit(extractTar.status ?? 1); } const innerAppPath = path.join(workDir, `${appName}.app`); if (!existsSync(innerAppPath)) { const appBundle = readdirSync(workDir).find((entry) => entry.endsWith(".app")); if (!appBundle) { console.error("expand-macos-bundle: extracted app bundle not found"); process.exit(1); } } const resolvedInnerApp = existsSync(innerAppPath) ? innerAppPath : path.join(workDir, readdirSync(workDir).find((e) => e.endsWith(".app"))); const innerContentsPath = path.join(resolvedInnerApp, "Contents"); function replaceDirectory(sourceDir, destinationDir) { rmSync(destinationDir, { recursive: true, force: true }); cpSync(sourceDir, destinationDir, { recursive: true, dereference: true }); } replaceDirectory( path.join(innerContentsPath, "MacOS"), path.join(contentsPath, "MacOS"), ); rmSync(resourcesPath, { recursive: true, force: true }); cpSync(path.join(innerContentsPath, "Resources"), resourcesPath, { recursive: true, dereference: true, }); const innerFrameworks = path.join(innerContentsPath, "Frameworks"); const wrapperFrameworks = path.join(contentsPath, "Frameworks"); if (existsSync(innerFrameworks)) { replaceDirectory(innerFrameworks, wrapperFrameworks); } cpSync(path.join(innerContentsPath, "Info.plist"), path.join(contentsPath, "Info.plist")); rmSync(workDir, { recursive: true, force: true }); if (process.platform === "darwin") { spawnSync("xattr", ["-cr", resolvedWrapperPath], { stdio: "inherit" }); } console.log(`expand-macos-bundle: pre-expanded ${resolvedWrapperPath}`);