Generate code based on other code changing in a typesafe way, enabling experiences like Next.js file-based routing.
Here's an example of how to generate an index.ts
file that exports all components from the src/components
directory whenever a component file is added, removed, or changed:
components.watch.ts
import { createTemplate, generate } from '@rubriclab/codegen'
import { z } from 'zod'
import path from 'node:path'
// Define a schema for the context needed by the template
const indexSchema = z.object({
exports: z.string()
})
// Function to get context based on discovered files
// It extracts component names from filenames (e.g., Button.tsx -> Button)
const getComponentContext = async (files: { name: string; path: string }[]) => {
const exportStatements = files
.map(({ name }) => {
const componentName = path.basename(name, path.extname(name)) // Extract name without extension
const exportPath = `./${componentName}` // Relative path for export
return `export * from '${exportPath}'`
})
.join('\n')
return {
exports: exportStatements
}
}
// Create a template function using the schema
const indexTemplate = createTemplate(
indexSchema,
// exports is typed safely as string[] here
({ exports }) => `// This file is auto-generated by @rubriclab/codegen
${exports}
`
)
// This will continue watching for changes until stopped
await generate({
watch: true, // Set to false for a one-time build
watchDir: `${process.cwd()}/src/components`, // Directory to watch
buildFile: `${process.cwd()}/src/components/index.ts`, // Output index file
template: indexTemplate,
getContext: getComponentContext,
acceptedFileTypes: ['.tsx']
})
console.log('Watching for changes in src/components...')
Then run bun components.watch.ts
to start the watcher. You should see output like this:
Built ~/my-app/src/components/index.ts in 0.9ms
Watching for changes in src/components...
This setup will watch the src/components
directory and regenerate the src/components/index.ts
file using the provided template and context whenever a file change is detected. This keeps your component exports automatically synchronized.
In practice, you might run the watcher in parallel with your dev command:
bun add -d concurrently
concurrently "bun components.watch.ts" "bun dev"