import { existsSync, mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath, pathToFileURL } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev"; const requiredFiles = [ "src/bun/index.js", "electrobun.config.ts", "package.json", ]; for (const relativePath of requiredFiles) { const filePath = path.join(rootDir, relativePath); if (!existsSync(filePath)) { console.error(`pre-build: required file not found: ${relativePath}`); process.exit(1); } } if (buildEnv === "stable" && process.env.ELECTROBUN_OS === "macos") { const codesignVars = [ "ELECTROBUN_DEVELOPER_ID", "ELECTROBUN_APPLEID", "ELECTROBUN_APPLEIDPASS", "ELECTROBUN_TEAMID", ]; const missing = codesignVars.filter((name) => !process.env[name]); if (missing.length > 0) { console.warn( `pre-build: stable macOS build without code signing credentials (${missing.join(", ")}); Electrobun will skip signing.`, ); } } const buildMacosEffects = spawnSync( "bun", [path.join(rootDir, "scripts/build-macos-effects.mjs")], { cwd: rootDir, stdio: "inherit", env: process.env }, ); if (buildMacosEffects.status !== 0) { process.exit(buildMacosEffects.status ?? 1); } if (process.platform === "darwin") { const { validateMacosEffectsDylib, outFile } = await import( pathToFileURL( path.join(rootDir, "scripts/build-macos-effects.mjs"), ).href ); if (!validateMacosEffectsDylib({ dylibPath: outFile })) { console.error( "pre-build: macOS window effects dylib is missing or invalid; blur and traffic lights will not work in packaged builds.", ); process.exit(1); } } const prepareIcons = spawnSync( "bun", [ path.join(rootDir, "scripts/prepare-app-icons.mjs"), "assets/logos/farmcontrolicon.png", ], { cwd: rootDir, stdio: "inherit", env: process.env }, ); if (prepareIcons.status !== 0) { process.exit(prepareIcons.status ?? 1); } const prepareInstallerIcons = spawnSync( "bun", [path.join(rootDir, "scripts/prepare-installer-icons.mjs")], { cwd: rootDir, stdio: "inherit", env: process.env }, ); if (prepareInstallerIcons.status !== 0) { process.exit(prepareInstallerIcons.status ?? 1); } const windowsIconPath = path.join(rootDir, "assets/icon.ico"); if (!existsSync(windowsIconPath)) { console.error( "pre-build: assets/icon.ico not found after prepare-app-icons (required for build.win.icon)", ); process.exit(1); } const writeBuildInfo = spawnSync( "bun", [path.join(rootDir, "scripts/write-build-info.mjs")], { cwd: rootDir, stdio: "inherit", env: process.env }, ); if (writeBuildInfo.status !== 0) { process.exit(writeBuildInfo.status ?? 1); } if (buildEnv === "dev") { console.log( "pre-build: dev environment — skipping production renderer build (use dev:renderer for Vite)", ); // Electrobun still copies dist/mainview into the app bundle; provide a stub so // dev builds don't warn when Vite serves the renderer instead. const stubDir = path.join(rootDir, "dist/mainview"); mkdirSync(stubDir, { recursive: true }); writeFileSync( path.join(stubDir, "index.html"), "
Dev mode — start Vite withbun run dev:renderer.",
);
console.log(`pre-build: validation passed (${buildEnv})`);
process.exit(0);
}
const buildRenderer = spawnSync(
"bun",
[path.join(rootDir, "scripts/build-renderer.mjs")],
{
cwd: rootDir,
stdio: "inherit",
env: {
...process.env,
NODE_ENV: "production",
},
},
);
if (buildRenderer.status !== 0) {
process.exit(buildRenderer.status ?? 1);
}
const rendererIndex = path.join(rootDir, "dist/mainview/index.html");
if (!existsSync(rendererIndex)) {
console.error(`pre-build: renderer output not found: dist/mainview/index.html`);
process.exit(1);
}
console.log(`pre-build: validation and renderer build passed (${buildEnv})`);