feat: 使用 Effect 模块重构构建脚本并支持多平台并行构建

- 使用 Effect 模块重构构建脚本,引入类型安全的配置与错误处理,实现并行构建多平台目标并输出结构化构建摘要。
- 添加 effect 库及其依赖项以支持函数式编程特性
- 添加 effect 依赖以支持函数式响应式编程特性
This commit is contained in:
2026-01-18 14:18:52 +08:00
parent 71d9f4801a
commit 5855398250
3 changed files with 144 additions and 21 deletions

157
build.ts
View File

@@ -1,6 +1,9 @@
import { $ } from 'bun'
import { Console, Effect } from 'effect'
await $`rm -rf ./out`
// ============================================================================
// Domain Models
// ============================================================================
const targetMap = {
'bun-windows-x64': 'x86_64-pc-windows-msvc',
@@ -12,27 +15,139 @@ const targetMap = {
type BunTarget = keyof typeof targetMap
const targets: BunTarget[] = [
'bun-windows-x64',
'bun-darwin-arm64',
'bun-linux-x64',
]
type BuildConfig = Readonly<{
entrypoint: string
outputDir: string
targets: ReadonlyArray<BunTarget>
}>
const buildTasks = targets.map((bunTarget) =>
Bun.build({
entrypoints: ['./.output/server/index.mjs'],
compile: {
outfile: `server-${targetMap[bunTarget]}`,
target: bunTarget,
type BuildResult = Readonly<{
target: BunTarget
outputs: ReadonlyArray<string>
}>
// ============================================================================
// Configuration
// ============================================================================
const defaultConfig: BuildConfig = {
entrypoint: './.output/server/index.mjs',
outputDir: './out',
targets: ['bun-windows-x64', 'bun-darwin-arm64', 'bun-linux-x64'],
}
// ============================================================================
// Effects
// ============================================================================
/**
* 清理输出目录
*/
const cleanOutputDir = (dir: string) =>
Effect.tryPromise({
try: async () => {
await $`rm -rf ${dir}`
return undefined
},
outdir: './out',
}),
catch: (error: unknown) => ({
_tag: 'CleanError' as const,
message: `无法清理目录 ${dir}`,
cause: error,
}),
}).pipe(Effect.tap(() => Console.log(`✓ 已清理输出目录: ${dir}`)))
/**
* 为单个目标平台构建
*/
const buildForTarget = (
config: BuildConfig,
target: BunTarget,
): Effect.Effect<
BuildResult,
{ _tag: 'BuildError'; target: BunTarget; message: string; cause: unknown }
> =>
Effect.gen(function* () {
yield* Console.log(`🔨 开始构建: ${target}`)
const output = yield* Effect.tryPromise({
try: () =>
Bun.build({
entrypoints: [config.entrypoint],
compile: {
outfile: `server-${targetMap[target]}`,
target: target,
},
outdir: config.outputDir,
}),
catch: (error: unknown) => ({
_tag: 'BuildError' as const,
target,
message: `构建失败: ${target}`,
cause: error,
}),
})
const paths = output.outputs.map((item: { path: string }) => item.path)
return {
target,
outputs: paths,
} satisfies BuildResult
})
/**
* 并行构建所有目标平台
*/
const buildAllTargets = (config: BuildConfig) => {
const effects = config.targets.map((target) => buildForTarget(config, target))
return Effect.all(effects, { concurrency: 'unbounded' })
}
/**
* 输出构建结果摘要
*/
const printBuildSummary = (results: ReadonlyArray<BuildResult>) =>
Effect.gen(function* () {
yield* Console.log('\n📦 构建完成:')
for (const result of results) {
yield* Console.log(` ${result.target}:`)
for (const path of result.outputs) {
yield* Console.log(` - ${path}`)
}
}
})
// ============================================================================
// Main Program
// ============================================================================
const program = Effect.gen(function* () {
const config = defaultConfig
// 1. 清理输出目录
yield* cleanOutputDir(config.outputDir)
// 2. 并行构建所有目标
const results = yield* buildAllTargets(config)
// 3. 输出构建摘要
yield* printBuildSummary(results)
return results
})
// ============================================================================
// Runner
// ============================================================================
const main = program.pipe(
Effect.catchAllCause((cause) =>
Console.error('❌ 构建失败:', cause).pipe(
Effect.flatMap(() => Effect.fail(cause)),
),
),
)
const outputs = await Promise.all(buildTasks)
for (const [index, output] of outputs.entries()) {
const task = buildTasks[index]
if (!task) continue
console.log(output.outputs.map((item) => item.path))
}
Effect.runPromise(main).catch(() => {
process.exit(1)
})