import { readdir, rm, writeFile } from "node:fs/promises"; import { join, relative, sep } from "node:path"; import { fileURLToPath } from "node:url"; import { validateVersion } from "./bump-version-logic"; export const projectRoot = fileURLToPath(new URL("..", import.meta.url)); export const distWebDir = join(projectRoot, "dist/web"); export const buildDir = join(projectRoot, ".build"); export const packageJsonPath = join(projectRoot, "package.json"); export async function cleanup() { await rm(buildDir, { force: true, recursive: true }); } export async function codeGeneration() { console.log("Step 2/3: Code generation..."); await rm(buildDir, { force: true, recursive: true }); await Bun.write(join(buildDir, ".gitkeep"), ""); const packageJson = (await Bun.file(packageJsonPath).json()) as { version: string }; const version = packageJson.version; if (typeof version !== "string") { console.error("package.json does not have a valid version field"); process.exit(1); } validateVersion(version); const allFiles = await scanDir(distWebDir, "/"); const importLines: string[] = []; const fileEntries: string[] = []; let indexHtmlVar = ""; for (let i = 0; i < allFiles.length; i++) { const urlPath = allFiles[i]!; const varName = `f${i}`; const filePath = toImportSpecifier(buildDir, join(distWebDir, urlPath.slice(1))); importLines.push(`import ${varName} from "./${filePath}" with { type: "file" };`); if (urlPath === "/index.html") { indexHtmlVar = varName; } else { fileEntries.push(` "${urlPath}": Bun.file(${varName}),`); } } if (!indexHtmlVar) { console.error("index.html not found in dist/web/"); process.exit(1); } const staticAssetsTs = [ `import type { StaticAssets } from "../src/server/static";`, "", ...importLines, "", `export const staticAssets: StaticAssets = {`, ` files: {`, ...fileEntries, ` },`, ` indexHtml: Bun.file(${indexHtmlVar}),`, `};`, "", ].join("\n"); await writeFile(join(buildDir, "static-assets.ts"), staticAssetsTs); const serverEntryTs = [ `import { bootstrap } from "../src/server/bootstrap";`, `import { readRuntimeConfig } from "../src/server/config";`, `import { staticAssets } from "./static-assets";`, "", `const APP_VERSION = "${version}" as const;`, "", `async function main() {`, ` const { configPath } = readRuntimeConfig();`, ` await bootstrap({ configPath, mode: "production", staticAssets, version: APP_VERSION });`, `}`, "", `void main().catch((error) => {`, ` console.error("启动失败:", error instanceof Error ? error.message : error);`, ` process.exit(1);`, `});`, "", ].join("\n"); await writeFile(join(buildDir, "server-entry.ts"), serverEntryTs); return version; } export async function scanDir(dir: string, prefix: string): Promise { const entries = await readdir(dir, { withFileTypes: true }); const paths: string[] = []; for (const entry of entries) { const fullPath = join(dir, entry.name); const urlPath = `${prefix}${entry.name}`; if (entry.isDirectory()) { paths.push(...(await scanDir(fullPath, `${urlPath}/`))); } else { paths.push(urlPath); } } return paths; } export function toImportSpecifier(fromDir: string, targetPath: string) { return relative(fromDir, targetPath).split(sep).join("/"); } export async function viteBuild() { console.log("Step 1/3: Vite build..."); const proc = Bun.spawn(["bunx", "--bun", "vite", "build"], { cwd: projectRoot, stderr: "inherit", stdout: "inherit", }); const exitCode = await proc.exited; if (exitCode !== 0) { console.error("Vite build failed"); process.exit(1); } }