963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
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