Files
opencode/packages/desktop/electron.vite.config.ts

147 lines
5.3 KiB
TypeScript

import { defineConfig } from "electron-vite"
import type { Plugin } from "vite"
import { BetaNotes } from "./script/beta-notes"
import { pickerPlugin } from "./script/picker"
import { nodePtyPackage } from "./script/utils"
function resolveChannel() {
const raw = process.env.OPENCODE_CHANNEL
if (raw === "local" || raw === "dev" || raw === "beta" || raw === "prod") return raw
if (raw === "latest") return "prod"
return "dev"
}
const appPlugin = (await import("@opencode/app/vite")).default
// Every module the entry reaches through static imports lands in one chunk. Automatic splitting
// otherwise fragments the initial graph into ~50 files shared with lazy routes, and each file costs
// the renderer a main-thread request round trip through the main process before first paint.
// Rolldown hands every chunk-name call a fresh context, so the graph is walked once per output.
const initialGraph = new Set<string>()
const initialChunk: Plugin = {
name: "opencode-desktop:initial-chunk",
renderStart() {
const entries = [...this.getModuleIds()].filter((id) => this.getModuleInfo(id)?.isEntry)
initialGraph.clear()
entries.forEach((id) => initialGraph.add(id))
// A Set's iterator also visits members added while iterating, so this reaches every static import.
for (const id of initialGraph) this.getModuleInfo(id)?.importedIds.forEach((imported) => initialGraph.add(imported))
},
}
export default defineConfig(async ({ command }) => {
const channel = resolveChannel()
const nodePtyPkg = nodePtyPackage()
// Beta builds carry their commit and recent history for the updater's "What's new in beta"; the dev server carries
// them without asking GitHub, so the page can be tried; other channels none.
const betaNotes =
channel === "beta"
? await BetaNotes.enrich(BetaNotes.embed(), { token: process.env.GITHUB_TOKEN })
: command === "serve"
? BetaNotes.embed()
: undefined
return {
main: {
resolve: {
dedupe: ["effect"],
},
define: {
// Local renderer/server mode still uses the dev application identity and updater policy.
"import.meta.env.OPENCODE_CHANNEL": JSON.stringify(channel === "local" ? "dev" : channel),
},
build: {
minify: command === "build",
rolldownOptions: {
input: { index: "src/main/index.ts" },
output: {
format: "es",
// DesktopPaths resolves resources from the main output directory,
// including when the lazy desktop entry shares it with other chunks.
chunkFileNames: "[name]-[hash].js",
},
},
externalizeDeps: {
// Bundle the Effect family together.
exclude: ["effect", "@effect/platform-node", "@effect/platform-node-shared", "drizzle-orm"],
include: [nodePtyPkg],
},
},
plugins: [
{
name: "opencode:node-pty-narrower",
enforce: "pre",
resolveId(s) {
if (s === "@lydell/node-pty") return nodePtyPkg
return undefined
},
},
{
// Rolldown emits its own scoped createRequire runtime for CommonJS dependencies.
// electron-vite's post-bundle regex shim injects a top-level `const require` that collides
// with bundled ESM modules declaring their own `require` (such as @effect/platform-node).
name: "opencode:disable-esm-shim",
configResolved(config) {
const shim = config.plugins.find((plugin) => plugin.name === "vite:esm-shim")
if (shim) delete shim.renderChunk
},
},
],
},
preload: {
build: {
minify: command === "build",
rolldownOptions: {
input: { index: "src/preload/index.ts" },
output: {
format: "cjs",
// The package is "type": "module". Under --no-sandbox Electron loads the preload
// through Node's module loader, which treats a .js file as ESM and fails on
// require("electron"). The sandboxed path ignores the extension.
entryFileNames: "[name].cjs",
},
},
},
},
renderer: {
experimental: {
bundledDev: true,
},
define: {
"import.meta.env.OPENCODE_VERSION": JSON.stringify(process.env.OPENCODE_VERSION),
"import.meta.env.VITE_OPENCODE_CHANNEL": JSON.stringify(channel),
"import.meta.env.OPENCODE_COMMIT": betaNotes ? JSON.stringify(betaNotes.sha) : "undefined",
"import.meta.env.OPENCODE_BETA_NOTES": betaNotes ? JSON.stringify(JSON.stringify(betaNotes)) : "undefined",
"import.meta.env.OPENCODE_TEST_ONBOARDING": JSON.stringify(
command === "serve" && process.env.OPENCODE_TEST_ONBOARDING === "1",
),
},
plugins: [pickerPlugin(), appPlugin, initialChunk],
publicDir: "../../../app/public",
root: "src/renderer",
build: {
minify: command === "build",
sourcemap: true,
rolldownOptions: {
input: {
main: "src/renderer/index.html",
},
output: {
codeSplitting: {
groups: [{ name: (id) => (initialGraph.has(id) ? "app" : null), priority: 10 }],
},
},
},
},
},
}
})