diff --git a/surfsense_desktop/scripts/build-electron.mjs b/surfsense_desktop/scripts/build-electron.mjs new file mode 100644 index 000000000..b571979b1 --- /dev/null +++ b/surfsense_desktop/scripts/build-electron.mjs @@ -0,0 +1,68 @@ +import { build } from 'esbuild'; +import fs from 'fs'; +import path from 'path'; + +/** + * electron-builder cannot follow symlinks when packaging into ASAR. + * Next.js standalone output contains symlinks in node_modules that + * must be replaced with real copies before packaging. + * Pattern from CodePilot (github.com/op7418/CodePilot). + */ +function resolveStandaloneSymlinks() { + const standaloneModules = path.join( + '..', 'surfsense_web', '.next', 'standalone', '.next', 'node_modules' + ); + if (!fs.existsSync(standaloneModules)) return; + + const entries = fs.readdirSync(standaloneModules); + for (const entry of entries) { + const fullPath = path.join(standaloneModules, entry); + const stat = fs.lstatSync(fullPath); + if (stat.isSymbolicLink()) { + const target = fs.readlinkSync(fullPath); + const resolved = path.resolve(standaloneModules, target); + if (fs.existsSync(resolved)) { + fs.rmSync(fullPath, { recursive: true, force: true }); + fs.cpSync(resolved, fullPath, { recursive: true }); + console.log(`Resolved symlink: ${entry} -> ${target}`); + } + } + } +} + +async function buildElectron() { + if (fs.existsSync('dist')) { + fs.rmSync('dist', { recursive: true }); + console.log('Cleaned dist/'); + } + fs.mkdirSync('dist', { recursive: true }); + + const shared = { + bundle: true, + platform: 'node', + target: 'node18', + external: ['electron'], + sourcemap: true, + minify: false, + }; + + await build({ + ...shared, + entryPoints: ['src/main.ts'], + outfile: 'dist/main.js', + }); + + await build({ + ...shared, + entryPoints: ['src/preload.ts'], + outfile: 'dist/preload.js', + }); + + console.log('Electron build complete'); + resolveStandaloneSymlinks(); +} + +buildElectron().catch((err) => { + console.error(err); + process.exit(1); +});