#!/usr/bin/env node // `next build --webpack` (see next.config.ts's `output: "standalone"`) // emits .next/standalone/server.js but - deliberately, per Next's own docs - // leaves out public/ and .next/static/. The Dockerfile copies both in by // hand for the container image; this does the same thing for local Electron // dev and packaging, so every path boots the exact same artifact. import { cpSync, existsSync, rmSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const rootDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); const standaloneDir = path.join(rootDir, ".next", "standalone"); if (!existsSync(standaloneDir)) { console.error(`Missing ${standaloneDir} - run "next build --webpack" first.`); process.exit(1); } const publicSrc = path.join(rootDir, "public"); const publicDest = path.join(standaloneDir, "public"); rmSync(publicDest, { recursive: true, force: true }); cpSync(publicSrc, publicDest, { recursive: true }); const staticSrc = path.join(rootDir, ".next", "static"); const staticDest = path.join(standaloneDir, ".next", "static"); rmSync(staticDest, { recursive: true, force: true }); cpSync(staticSrc, staticDest, { recursive: true }); // The native SQLCipher prebuilds for the local search index (lib/mail-index/**). // // Next's output file tracing DOES pick up @signalapp/sqlcipher's JS // (package.json + dist/index.cjs) and its node-gyp-build dependency, but NOT // the prebuilds/ directory holding the actual .node binaries - node-gyp-build // resolves those by scanning the directory at runtime, which no static tracer // can follow. Verified by inspecting a real `build:standalone` output: the // package was present, `prebuilds/` was absent, so `require()` would have // failed at runtime in every packaged build. // // Copying the WHOLE prebuilds directory (all six platform/arch pairs, ~11 MB) // rather than just this host's is deliberate: electron-builder cross-builds the // x64 and arm64 macOS targets from one runner (electron-builder.config.js), so // the artifact has to contain a prebuild for an arch this machine isn't. // // Skipped silently when absent - the package is an OPTIONAL dependency and is // legitimately missing on musl/Alpine, where both Dockerfiles build. const sqlcipherSrc = path.join(rootDir, "node_modules", "@signalapp", "sqlcipher", "prebuilds"); if (existsSync(sqlcipherSrc)) { const sqlcipherDest = path.join( standaloneDir, "node_modules", "@signalapp", "sqlcipher", "prebuilds", ); rmSync(sqlcipherDest, { recursive: true, force: true }); cpSync(sqlcipherSrc, sqlcipherDest, { recursive: true }); console.log("Copied @signalapp/sqlcipher prebuilds into the standalone output"); } else { console.log( "@signalapp/sqlcipher not installed (optional dependency) - " + "the encrypted local index will be disabled at runtime", ); } console.log("Assembled standalone server at", standaloneDir);