Files
SRCmail/scripts/assemble-standalone.mjs
T
Bernd Rodler 218a584fb3 feat(electron): walking skeleton for the desktop shell
Phase 1 step 1 of VNCprodbuild: electron/main.ts boots the same Next.js
"standalone" server artifact the Dockerfile already produces (next.config.ts's
output: "standalone") as a child process on a random localhost port, then
opens a BrowserWindow at it. electron/preload.ts is a contextBridge stub
(window.vnc.isElectron) for now.

scripts/assemble-standalone.mjs copies public/ and .next/static into
.next/standalone, mirroring what the Dockerfile does by hand, since `next
build` deliberately leaves both out of the standalone output.
scripts/build-electron.mjs bundles main.ts/preload.ts to CommonJS via esbuild
(already a devDependency).

New npm scripts: build:standalone, build:electron, electron:dev.
electron-builder.config.js is intentionally minimal - no signing, no
platform targets yet, just enough to prove the concept end to end.

Also fixes a pre-existing repo-wide lint gap: vnc/plugins/smime is an
independent sub-package (own package.json/esbuild build, browser-only
globals) that was never added to eslint's ignores alongside repos:: and
examples/**, so `npm run lint` - and the husky pre-commit hook - was failing
on every commit regardless of what changed. Excluded it the same way those
are, and added node globals for scripts/**/*.mjs so the new build helpers
above lint cleanly too.

Verified manually: npm run build:standalone && npm run build:electron &&
electron . boots the server and opens a window with no errors.
2026-08-04 12:41:35 +02:00

30 lines
1.3 KiB
JavaScript

#!/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 });
console.log("Assembled standalone server at", standaloneDir);