feat: 使用 Effect 模块重构构建脚本并支持多平台并行构建
- 使用 Effect 模块重构构建脚本,引入类型安全的配置与错误处理,实现并行构建多平台目标并输出结构化构建摘要。 - 添加 effect 库及其依赖项以支持函数式编程特性 - 添加 effect 依赖以支持函数式响应式编程特性
This commit is contained in:
155
build.ts
155
build.ts
@@ -1,6 +1,9 @@
|
|||||||
import { $ } from 'bun'
|
import { $ } from 'bun'
|
||||||
|
import { Console, Effect } from 'effect'
|
||||||
|
|
||||||
await $`rm -rf ./out`
|
// ============================================================================
|
||||||
|
// Domain Models
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
const targetMap = {
|
const targetMap = {
|
||||||
'bun-windows-x64': 'x86_64-pc-windows-msvc',
|
'bun-windows-x64': 'x86_64-pc-windows-msvc',
|
||||||
@@ -12,27 +15,139 @@ const targetMap = {
|
|||||||
|
|
||||||
type BunTarget = keyof typeof targetMap
|
type BunTarget = keyof typeof targetMap
|
||||||
|
|
||||||
const targets: BunTarget[] = [
|
type BuildConfig = Readonly<{
|
||||||
'bun-windows-x64',
|
entrypoint: string
|
||||||
'bun-darwin-arm64',
|
outputDir: string
|
||||||
'bun-linux-x64',
|
targets: ReadonlyArray<BunTarget>
|
||||||
]
|
}>
|
||||||
|
|
||||||
const buildTasks = targets.map((bunTarget) =>
|
type BuildResult = Readonly<{
|
||||||
Bun.build({
|
target: BunTarget
|
||||||
entrypoints: ['./.output/server/index.mjs'],
|
outputs: ReadonlyArray<string>
|
||||||
compile: {
|
}>
|
||||||
outfile: `server-${targetMap[bunTarget]}`,
|
|
||||||
target: bunTarget,
|
// ============================================================================
|
||||||
|
// 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)
|
Effect.runPromise(main).catch(() => {
|
||||||
|
process.exit(1)
|
||||||
for (const [index, output] of outputs.entries()) {
|
})
|
||||||
const task = buildTasks[index]
|
|
||||||
if (!task) continue
|
|
||||||
console.log(output.outputs.map((item) => item.path))
|
|
||||||
}
|
|
||||||
|
|||||||
7
bun.lock
7
bun.lock
@@ -33,6 +33,7 @@
|
|||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
"babel-plugin-react-compiler": "^1.0.0",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
|
"effect": "^3.19.14",
|
||||||
"nitro": "npm:nitro-nightly@latest",
|
"nitro": "npm:nitro-nightly@latest",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
@@ -628,6 +629,8 @@
|
|||||||
|
|
||||||
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
||||||
|
|
||||||
|
"effect": ["effect@3.19.14", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="],
|
||||||
|
|
||||||
"encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
|
"encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
|
||||||
@@ -648,6 +651,8 @@
|
|||||||
|
|
||||||
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||||
|
|
||||||
|
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
@@ -800,6 +805,8 @@
|
|||||||
|
|
||||||
"prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="],
|
"prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="],
|
||||||
|
|
||||||
|
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
||||||
|
|
||||||
"radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="],
|
"radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="],
|
||||||
|
|
||||||
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
|
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
"babel-plugin-react-compiler": "^1.0.0",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
|
"effect": "^3.19.14",
|
||||||
"nitro": "npm:nitro-nightly@latest",
|
"nitro": "npm:nitro-nightly@latest",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user