1
0
Files
DiAL/scripts/build.ts
lanyuanxiaoyao a5cf6065c2 chore: 强化代码质量与风格检查体系
ESLint 升级到 recommended-type-checked + stylistic-type-checked,
引入 perfectionist 导入排序和 import 插件导入验证。

Prettier 显式声明全部格式化参数,消除跨环境差异。
TypeScript 启用 noUnusedLocals 和 noPropertyAccessFromIndexSignature。
完善 ignore 列表,排除 .agents/、bun.lock、data/ 等。
引入 husky + lint-staged(pre-commit)+ commitlint(commit-msg)。
更新 DEVELOPMENT.md 代码质量章节。
修复所有新增规则检测到的类型和风格违规。
2026-05-12 18:44:59 +08:00

145 lines
4.4 KiB
TypeScript

import { $ } from "bun";
import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
import { dirname, relative, sep } from "node:path";
import { fileURLToPath } from "node:url";
const buildDir = fileURLToPath(new URL("../.build/", import.meta.url));
const webDistDir = fileURLToPath(new URL("../dist/web/", import.meta.url));
const executablePath = fileURLToPath(new URL("../dist/dial-server", import.meta.url));
const generatedAssetsPath = fileURLToPath(new URL("../.build/static-assets.ts", import.meta.url));
const generatedEntryPath = fileURLToPath(new URL("../.build/server-entry.ts", import.meta.url));
await rm(buildDir, { force: true, recursive: true });
await rm(executablePath, { force: true });
await mkdir(buildDir, { recursive: true });
await $`bunx --bun vite build`;
const files = await listFiles(webDistDir);
const indexPath = files.find((file) => normalize(relative(webDistDir, file)) === "index.html");
if (!indexPath) {
throw new Error("Vite build 未生成 dist/web/index.html");
}
const assetFiles = files.filter((file) => file !== indexPath);
await writeGeneratedAssets(indexPath, assetFiles);
await writeGeneratedEntry();
const target = process.env["BUN_TARGET"] ?? process.env["BUILD_TARGET"];
const result = await Bun.build({
compile: target
? {
autoloadBunfig: true,
autoloadDotenv: true,
outfile: executablePath,
target: target as Bun.Build.CompileTarget,
}
: {
autoloadBunfig: true,
autoloadDotenv: true,
outfile: executablePath,
},
entrypoints: [generatedEntryPath],
minify: true,
sourcemap: "linked",
});
if (!result.success) {
await rm(executablePath, { force: true });
throw new Error("Bun executable 构建失败");
}
console.log(`Built executable: ${executablePath}`);
await rm(buildDir, { force: true, recursive: true });
async function listFiles(directory: string): Promise<string[]> {
const entries = await readdir(directory, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const path = `${directory.replace(/\/$/, "")}/${entry.name}`;
if (entry.isDirectory()) {
return listFiles(path);
}
return [path];
}),
);
return files.flat().sort((left, right) => normalize(left).localeCompare(normalize(right)));
}
function normalize(path: string): string {
return path.split(sep).join("/");
}
function toImportPath(path: string): string {
const rel = normalize(relative(buildDir, path));
return rel.startsWith(".") ? rel : `./${rel}`;
}
async function writeGeneratedAssets(indexPath: string, assetFiles: string[]) {
const imports = [
`import type { StaticAssets } from "../src/server/app";`,
`import indexPath from "${toImportPath(indexPath)}" with { type: "file" };`,
...assetFiles.map((file, index) => `import asset${index}Path from "${toImportPath(file)}" with { type: "file" };`),
];
const assetEntries = assetFiles.map((file, index) => {
const urlPath = `/${normalize(relative(webDistDir, file))}`;
return ` ${JSON.stringify(urlPath)}: Bun.file(asset${index}Path),`;
});
const source = `${imports.join("\n")}
export const staticAssets: StaticAssets = {
indexHtml: Bun.file(indexPath),
files: {
${assetEntries.join("\n")}
},
};
`;
await mkdir(dirname(generatedAssetsPath), { recursive: true });
await writeFile(generatedAssetsPath, source);
}
async function writeGeneratedEntry() {
await writeFile(
generatedEntryPath,
`import { loadConfig } from "../src/server/checker/config-loader";
import { ProbeStore } from "../src/server/checker/store";
import { ProbeEngine } from "../src/server/checker/engine";
import { startServer } from "../src/server/server";
import { readRuntimeConfig } from "../src/server/config";
import { registerCheckers } from "../src/server/checker/runner";
import { staticAssets } from "./static-assets";
async function main() {
registerCheckers();
const { configPath } = readRuntimeConfig();
const config = await loadConfig(configPath);
const store = new ProbeStore(config.dataDir + "/probe.db");
store.syncTargets(config.targets);
const engine = new ProbeEngine(store, config.targets, config.maxConcurrentChecks);
engine.start();
startServer({
config: { host: config.host, port: config.port },
mode: "production",
staticAssets,
store,
});
}
void main().catch((error) => {
console.error("启动失败:", error instanceof Error ? error.message : error);
process.exit(1);
});
`,
);
}