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>
364 lines
6.9 KiB
Markdown
364 lines
6.9 KiB
Markdown
# 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](https://github.com/unjs/unbuild).
|
|
|
|
**Recommendation:** Use [plugins](advanced-plugins.md) for most extensions. Use hooks for simple custom tasks or Rolldown plugin injection.
|
|
|
|
## Usage Patterns
|
|
|
|
### Object Syntax
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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:**
|
|
```ts
|
|
{
|
|
options: ResolvedConfig,
|
|
hooks: Hookable
|
|
}
|
|
```
|
|
|
|
**Use cases:**
|
|
- Setup tasks
|
|
- Validation
|
|
- Environment preparation
|
|
|
|
**Example:**
|
|
```ts
|
|
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:**
|
|
```ts
|
|
{
|
|
options: ResolvedConfig,
|
|
buildOptions: BuildOptions,
|
|
hooks: Hookable
|
|
}
|
|
```
|
|
|
|
**Use cases:**
|
|
- Modify build options per format
|
|
- Inject plugins dynamically
|
|
- Format-specific setup
|
|
|
|
**Example:**
|
|
```ts
|
|
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:**
|
|
```ts
|
|
{
|
|
options: ResolvedConfig,
|
|
chunks: RolldownChunk[],
|
|
hooks: Hookable
|
|
}
|
|
```
|
|
|
|
**Use cases:**
|
|
- Post-processing
|
|
- Asset copying
|
|
- Notifications
|
|
- Deployment
|
|
|
|
**Example:**
|
|
```ts
|
|
hooks: {
|
|
'build:done': async (context) => {
|
|
console.log(`Built ${context.chunks.length} chunks`)
|
|
|
|
// Copy additional files
|
|
await copyAssets()
|
|
|
|
// Send notification
|
|
notifyBuildComplete()
|
|
},
|
|
}
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Build Notifications
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
export default defineConfig({
|
|
hooks: {
|
|
'build:prepare': async (context) => {
|
|
await fetchRemoteConfig()
|
|
await initializeDatabase()
|
|
},
|
|
'build:done': async (context) => {
|
|
await uploadToS3(context.chunks)
|
|
await invalidateCDN()
|
|
},
|
|
},
|
|
})
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```ts
|
|
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](https://github.com/unjs/hookable) for hooks. Additional methods:
|
|
|
|
```ts
|
|
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
|
|
|
|
1. **Use plugins** for most extensions
|
|
2. **Hooks for simple tasks** like notifications or file copying
|
|
3. **Async hooks supported** for I/O operations
|
|
4. **Don't throw errors** unless you want to fail the build
|
|
5. **Context is mutable** in `build:before` for advanced use cases
|
|
6. **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](advanced-plugins.md) - Plugin system
|
|
- [Rolldown Options](advanced-rolldown-options.md) - Build options
|
|
- [Watch Mode](option-watch-mode.md) - Development workflow
|