Hooks
robuild 提供了强大的 Hooks 系统,允许你在构建过程的不同阶段执行自定义逻辑。
什么是 Hooks?
Hooks 是构建过程中的生命周期钩子,让你可以:
- 自定义构建流程: 在特定阶段执行自定义代码
- 环境准备: 设置构建环境,清理临时文件
- 后处理: 构建完成后的自定义操作
- 集成外部工具: 与 CI/CD、测试工具等集成
可用的 Hooks
1. start
- 构建开始
在构建开始前执行:
typescript
import { defineConfig } from 'robuild'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
start: (ctx) => {
console.log(`开始构建 ${ctx.pkg.name} v${ctx.pkg.version}`)
console.log(`工作目录: ${ctx.pkgDir}`)
}
}
})
2. end
- 构建结束
在构建完成后执行:
typescript
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
end: (ctx) => {
console.log('构建完成!')
console.log(`输出目录: ${ctx.pkgDir}/dist`)
}
}
})
3. beforeBuild
- 构建前
在每个构建入口开始前执行:
typescript
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
beforeBuild: (ctx, entry) => {
console.log(`准备构建: ${entry.input}`)
// 可以修改构建配置
if (entry.type === 'bundle') {
entry.minify = true
}
}
}
})
4. afterBuild
- 构建后
在每个构建入口完成后执行:
typescript
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
afterBuild: (ctx, entry, result) => {
console.log(`构建完成: ${entry.input}`)
console.log(`输出文件: ${result.outputFiles}`)
}
}
})
Hook 上下文
每个 Hook 都接收一个上下文对象,包含有用的信息:
BuildContext
typescript
interface BuildContext {
pkgDir: string // 项目根目录
pkg: { // package.json 信息
name: string
version: string
[key: string]: any
}
entries: BuildEntry[] // 构建入口列表
config: BuildConfig // 完整配置
}
BuildEntry
typescript
interface BuildEntry {
type: 'bundle' | 'transform'
input: string
outDir?: string
minify?: boolean
stub?: boolean
// ... 其他配置
}
BuildResult
typescript
interface BuildResult {
outputFiles: string[] // 输出文件列表
duration: number // 构建耗时
errors: Error[] // 错误信息
}
实际使用示例
1. 环境检查和准备
typescript
import { defineConfig } from 'robuild'
import { existsSync, mkdirSync } from 'fs'
import { join } from 'path'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
start: (ctx) => {
// 检查必要目录
const distDir = join(ctx.pkgDir, 'dist')
if (!existsSync(distDir)) {
mkdirSync(distDir, { recursive: true })
console.log('创建输出目录:', distDir)
}
// 检查环境变量
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'production'
}
}
}
})
2. 构建后处理
typescript
import { defineConfig } from 'robuild'
import { copyFileSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
end: (ctx) => {
// 复制 README 到输出目录
const readmeSrc = join(ctx.pkgDir, 'README.md')
const readmeDest = join(ctx.pkgDir, 'dist', 'README.md')
if (existsSync(readmeSrc)) {
copyFileSync(readmeSrc, readmeDest)
console.log('复制 README.md 到输出目录')
}
// 生成构建信息
const buildInfo = {
timestamp: new Date().toISOString(),
version: ctx.pkg.version,
nodeVersion: process.version
}
writeFileSync(
join(ctx.pkgDir, 'dist', 'build-info.json'),
JSON.stringify(buildInfo, null, 2)
)
}
}
})
3. 条件构建
typescript
import { defineConfig } from 'robuild'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
beforeBuild: (ctx, entry) => {
// 根据环境变量调整构建配置
if (process.env.NODE_ENV === 'development') {
entry.minify = false
entry.sourcemap = true
} else {
entry.minify = true
entry.sourcemap = false
}
// 根据平台调整配置
if (process.platform === 'win32') {
// Windows 特定配置
entry.rolldown = {
...entry.rolldown,
target: 'node18'
}
}
}
}
})
4. 集成测试
typescript
import { defineConfig } from 'robuild'
import { execSync } from 'child_process'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
afterBuild: async (ctx, entry, result) => {
if (result.errors.length === 0) {
console.log('构建成功,运行测试...')
try {
execSync('npm test', {
cwd: ctx.pkgDir,
stdio: 'inherit'
})
console.log('测试通过!')
} catch (error) {
console.error('测试失败:', error.message)
process.exit(1)
}
}
}
}
})
5. 多阶段构建
typescript
import { defineConfig } from 'robuild'
export default defineConfig({
entries: [
'./src/index.ts',
'./src/runtime/:./dist/runtime'
],
hooks: {
start: (ctx) => {
console.log('开始多阶段构建...')
},
beforeBuild: (ctx, entry) => {
if (entry.type === 'bundle') {
console.log('阶段 1: 打包主文件')
} else if (entry.type === 'transform') {
console.log('阶段 2: 转换运行时文件')
}
},
afterBuild: (ctx, entry, result) => {
console.log(`阶段完成: ${entry.input}`)
},
end: (ctx) => {
console.log('所有阶段构建完成!')
}
}
})
异步 Hooks
Hooks 支持异步操作:
typescript
import { defineConfig } from 'robuild'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
start: async (ctx) => {
// 异步操作
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('环境准备完成')
},
end: async (ctx) => {
// 异步后处理
await uploadToCDN(ctx.pkgDir + '/dist')
console.log('文件已上传到 CDN')
}
}
})
错误处理
1. Hook 错误处理
typescript
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
start: (ctx) => {
try {
// 可能出错的操作
validateEnvironment()
} catch (error) {
console.error('环境检查失败:', error.message)
process.exit(1)
}
}
}
})
2. 构建错误处理
typescript
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
afterBuild: (ctx, entry, result) => {
if (result.errors.length > 0) {
console.error('构建失败:')
result.errors.forEach(error => {
console.error(`- ${error.message}`)
})
// 发送错误通知
sendErrorNotification(result.errors)
}
}
}
})
最佳实践
1. 保持 Hooks 简洁
typescript
// ✅ 好的做法
hooks: {
start: (ctx) => {
console.log(`构建 ${ctx.pkg.name}`)
}
}
// ❌ 避免的做法
hooks: {
start: (ctx) => {
// 避免在 Hook 中放置过多逻辑
// 复杂的逻辑应该提取到单独的函数中
}
}
2. 使用类型安全
typescript
import { defineConfig, type BuildContext } from 'robuild'
export default defineConfig({
entries: ['./src/index.ts'],
hooks: {
start: (ctx: BuildContext) => {
// 享受完整的类型提示
console.log(ctx.pkg.name)
}
}
})
3. 错误处理
typescript
hooks: {
start: (ctx) => {
try {
// 关键操作
prepareEnvironment()
} catch (error) {
console.error('启动失败:', error)
process.exit(1)
}
}
}