mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-09 22:25:02 +03:00
132 lines
5.5 KiB
TypeScript
132 lines
5.5 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import { existsSync } from "node:fs"
|
|
import path from "node:path"
|
|
import { MainConfigFactory } from "electron-vite"
|
|
import { build } from "vite"
|
|
import createConfig from "./electron.vite.config"
|
|
import pkg from "./package.json"
|
|
import { CLI_BINARIES, nodePtyPackage } from "./script/utils"
|
|
|
|
const load = async (command: "build" | "serve") => ({
|
|
config: await createConfig({ command, mode: command === "build" ? "production" : "development" }),
|
|
})
|
|
|
|
// Bundled into the main process, so the manifest must not also ship them as external packages.
|
|
const bundled = ["effect", "@effect/platform-node", "@effect/platform-node-shared", "drizzle-orm"]
|
|
|
|
test("minifies only builds, previews onboarding only in development, and ships sourcemaps and public assets", async () => {
|
|
const previous = process.env.OPENCODE_TEST_ONBOARDING
|
|
const previousTarget = process.env.OPENCODE_TARGET
|
|
|
|
try {
|
|
process.env.OPENCODE_TEST_ONBOARDING = "1"
|
|
|
|
for (const command of ["build", "serve"] as const) {
|
|
const result = await load(command)
|
|
|
|
for (const target of [result.config.main, result.config.preload, result.config.renderer])
|
|
expect(target?.build?.minify).toBe(command === "build")
|
|
expect(result.config.renderer?.define?.["import.meta.env.OPENCODE_TEST_ONBOARDING"]).toBe(
|
|
JSON.stringify(command === "serve"),
|
|
)
|
|
}
|
|
|
|
delete process.env.OPENCODE_TEST_ONBOARDING
|
|
const serve = await load("serve")
|
|
expect(serve.config.renderer?.define?.["import.meta.env.OPENCODE_TEST_ONBOARDING"]).toBe("false")
|
|
|
|
for (const item of CLI_BINARIES) {
|
|
process.env.OPENCODE_TARGET = item.target
|
|
const main = (await load("build")).config.main
|
|
expect(main?.build?.externalizeDeps).toEqual({
|
|
exclude: bundled,
|
|
include: [nodePtyPackage(item.target)],
|
|
})
|
|
}
|
|
} finally {
|
|
delete process.env.OPENCODE_TEST_ONBOARDING
|
|
delete process.env.OPENCODE_TARGET
|
|
|
|
if (previous !== undefined) process.env.OPENCODE_TEST_ONBOARDING = previous
|
|
|
|
if (previousTarget !== undefined) process.env.OPENCODE_TARGET = previousTarget
|
|
}
|
|
|
|
const renderer = (await load("build")).config.renderer
|
|
// Sentry uploads the renderer source maps, then deletes them.
|
|
expect(renderer?.build?.sourcemap).toBe(true)
|
|
|
|
// Vite resolves publicDir from the renderer root, not from the config file.
|
|
const root = renderer?.root ?? ""
|
|
const publicDir = renderer?.publicDir || ""
|
|
expect(existsSync(path.resolve(import.meta.dirname, root, publicDir, "oc-theme-preload.js"))).toBe(true)
|
|
})
|
|
|
|
test("bundles one Effect runtime and Drizzle while keeping native dependencies external", async () => {
|
|
for (const name of bundled) {
|
|
expect(Object.keys(pkg.dependencies)).not.toContain(name)
|
|
expect(Object.keys(pkg.optionalDependencies)).not.toContain(name)
|
|
}
|
|
|
|
expect(Object.keys(pkg.optionalDependencies).filter((name) => name.startsWith("@lydell/node-pty-"))).toEqual([
|
|
"@lydell/node-pty-darwin-arm64",
|
|
"@lydell/node-pty-darwin-x64",
|
|
"@lydell/node-pty-linux-arm64",
|
|
"@lydell/node-pty-linux-x64",
|
|
"@lydell/node-pty-win32-arm64",
|
|
"@lydell/node-pty-win32-x64",
|
|
])
|
|
|
|
const previousTarget = process.env.OPENCODE_TARGET
|
|
// Build against the opposite architecture to prove cross-packaging resolves the target's native package, not the host's.
|
|
const crossTarget = process.arch === "arm64" ? "windows-x64" : "windows-arm64"
|
|
const crossNodePty = nodePtyPackage(crossTarget)
|
|
|
|
try {
|
|
process.env.OPENCODE_TARGET = crossTarget
|
|
const result = await load("build")
|
|
|
|
if (!result.config.main) throw new Error("Missing main-process build configuration")
|
|
|
|
const config = await new MainConfigFactory(
|
|
result.config.main,
|
|
{ configFile: false, mode: "production" },
|
|
{ root: import.meta.dirname },
|
|
).build()
|
|
|
|
config.build = { ...config.build, minify: false, write: false }
|
|
config.logLevel = "silent"
|
|
const output = await build(config)
|
|
|
|
const chunks = (Array.isArray(output) ? output : [output]).flatMap((item) =>
|
|
"output" in item ? item.output.filter((entry) => entry.type === "chunk") : [],
|
|
)
|
|
|
|
expect(chunks.length).toBeGreaterThan(0)
|
|
// Resource resolution must not depend on which lazy entry owns DesktopPaths.
|
|
expect(chunks.every((chunk) => !chunk.fileName.includes("/"))).toBe(true)
|
|
const imports = chunks.flatMap((chunk) => [...chunk.imports, ...chunk.dynamicImports])
|
|
const modules = chunks.flatMap((chunk) => Object.keys(chunk.modules))
|
|
|
|
for (const name of bundled) {
|
|
expect(imports.filter((id) => id === name || id.startsWith(`${name}/`))).toEqual([])
|
|
expect(modules.some((id) => id.includes(`/node_modules/${name}/`))).toBe(true)
|
|
}
|
|
|
|
const effect = modules.filter((id) => id.includes("/node_modules/effect/"))
|
|
expect(new Set(effect.map((id) => id.split("/node_modules/effect/")[0])).size).toBe(1)
|
|
expect(new Set(effect).size).toBe(effect.length)
|
|
expect(imports).toContain("electron")
|
|
expect(imports).toContain("node:sqlite")
|
|
expect(chunks.some((chunk) => chunk.dynamicImports.includes("@zip.js/zip.js"))).toBe(true)
|
|
expect(imports).toContain(crossNodePty)
|
|
expect(imports).not.toContain(`@lydell/node-pty-${process.platform}-${process.arch}`)
|
|
expect(modules.some((id) => id.includes("/node_modules/msgpackr"))).toBe(false)
|
|
expect(chunks.some((chunk) => chunk.code.includes("msgpackr"))).toBe(false)
|
|
} finally {
|
|
delete process.env.OPENCODE_TARGET
|
|
|
|
if (previousTarget !== undefined) process.env.OPENCODE_TARGET = previousTarget
|
|
}
|
|
}, 30_000)
|