# 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