Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.9 KiB
6.9 KiB
Lifecycle Hooks
Extend the build process with lifecycle hooks.
Overview
Hooks provide a way to inject custom logic at specific stages of the build lifecycle. Inspired by unbuild.
Recommendation: Use plugins for most extensions. Use hooks for simple custom tasks or Rolldown plugin injection.
Usage Patterns
Object Syntax
export default defineConfig({
entry: ['src/index.ts'],
hooks: {
'build:prepare': async (context) => {
console.log('Build starting...')
},
'build:done': async (context) => {
console.log('Build complete!')
},
},
})
Function Syntax
export default defineConfig({
entry: ['src/index.ts'],
hooks(hooks) {
hooks.hook('build:prepare', () => {
console.log('Preparing build...')
})
hooks.hook('build:before', (context) => {
console.log(`Building format: ${context.format}`)
})
},
})
Available Hooks
build:prepare
Called before the build process starts.
When: Once per build session
Context:
{
options: ResolvedConfig,
hooks: Hookable
}
Use cases:
- Setup tasks
- Validation
- Environment preparation
Example:
hooks: {
'build:prepare': async (context) => {
console.log('Starting build for:', context.options.entry)
await cleanOldFiles()
},
}
build:before
Called before each Rolldown build.
When: Once per format (ESM, CJS, etc.)
Context:
{
options: ResolvedConfig,
buildOptions: BuildOptions,
hooks: Hookable
}
Use cases:
- Modify build options per format
- Inject plugins dynamically
- Format-specific setup
Example:
hooks: {
'build:before': async (context) => {
console.log(`Building ${context.buildOptions.format} format...`)
// Add format-specific plugin
if (context.buildOptions.format === 'iife') {
context.buildOptions.plugins.push(browserPlugin())
}
},
}
build:done
Called after the build completes.
When: Once per build session
Context:
{
options: ResolvedConfig,
chunks: RolldownChunk[],
hooks: Hookable
}
Use cases:
- Post-processing
- Asset copying
- Notifications
- Deployment
Example:
hooks: {
'build:done': async (context) => {
console.log(`Built ${context.chunks.length} chunks`)
// Copy additional files
await copyAssets()
// Send notification
notifyBuildComplete()
},
}
Common Patterns
Build Notifications
export default defineConfig({
hooks: {
'build:prepare': () => {
console.log('🚀 Starting build...')
},
'build:done': (context) => {
const size = context.chunks.reduce((sum, c) => sum + c.code.length, 0)
console.log(`✅ Build complete! Total size: ${size} bytes`)
},
},
})
Conditional Plugin Injection
export default defineConfig({
hooks(hooks) {
hooks.hook('build:before', (context) => {
// Add minification only for production
if (process.env.NODE_ENV === 'production') {
context.buildOptions.plugins.push(minifyPlugin())
}
})
},
})
Custom File Copy
import { copyFile } from 'fs/promises'
export default defineConfig({
hooks: {
'build:done': async (context) => {
// Copy README to dist
await copyFile('README.md', `${context.options.outDir}/README.md`)
},
},
})
Build Metrics
export default defineConfig({
hooks: {
'build:prepare': (context) => {
context.startTime = Date.now()
},
'build:done': (context) => {
const duration = Date.now() - context.startTime
console.log(`Build took ${duration}ms`)
// Log chunk sizes
context.chunks.forEach((chunk) => {
console.log(`${chunk.fileName}: ${chunk.code.length} bytes`)
})
},
},
})
Format-Specific Logic
export default defineConfig({
format: ['esm', 'cjs', 'iife'],
hooks: {
'build:before': (context) => {
const format = context.buildOptions.format
if (format === 'iife') {
// Browser-specific setup
context.buildOptions.globalName = 'MyLib'
} else if (format === 'cjs') {
// Node-specific setup
context.buildOptions.platform = 'node'
}
},
},
})
Deployment Hook
export default defineConfig({
hooks: {
'build:done': async (context) => {
if (process.env.DEPLOY === 'true') {
console.log('Deploying to CDN...')
await deployToCDN(context.options.outDir)
}
},
},
})
Advanced Usage
Multiple Hooks
export default defineConfig({
hooks(hooks) {
// Register multiple hooks
hooks.hook('build:prepare', setupEnvironment)
hooks.hook('build:prepare', validateConfig)
hooks.hook('build:before', injectPlugins)
hooks.hook('build:before', logFormat)
hooks.hook('build:done', generateManifest)
hooks.hook('build:done', notifyComplete)
},
})
Async Hooks
export default defineConfig({
hooks: {
'build:prepare': async (context) => {
await fetchRemoteConfig()
await initializeDatabase()
},
'build:done': async (context) => {
await uploadToS3(context.chunks)
await invalidateCDN()
},
},
})
Error Handling
export default defineConfig({
hooks: {
'build:done': async (context) => {
try {
await riskyOperation()
} catch (error) {
console.error('Hook failed:', error)
// Don't throw - allow build to complete
}
},
},
})
Hookable API
tsdown uses hookable for hooks. Additional methods:
export default defineConfig({
hooks(hooks) {
// Register hook
hooks.hook('build:done', handler)
// Register hook once
hooks.hookOnce('build:prepare', handler)
// Remove hook
hooks.removeHook('build:done', handler)
// Clear all hooks for event
hooks.removeHooks('build:done')
// Call hooks manually
await hooks.callHook('build:done', context)
},
})
Tips
- Use plugins for most extensions
- Hooks for simple tasks like notifications or file copying
- Async hooks supported for I/O operations
- Don't throw errors unless you want to fail the build
- Context is mutable in
build:beforefor advanced use cases - Multiple hooks allowed for the same event
Troubleshooting
Hook Not Called
- Verify hook name is correct
- Check hook is registered in config
- Ensure async hooks are awaited
Build Fails in Hook
- Add try/catch for error handling
- Don't throw unless intentional
- Log errors for debugging
Context Undefined
- Check which hook you're using
- Verify context properties available for that hook
Related
- Plugins - Plugin system
- Rolldown Options - Build options
- Watch Mode - Development workflow