# 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