#!/usr/bin/env tsx
/**
* @fileoverview Build-time template embedding script
* Converts .src.md template files to TypeScript string constants
* Runs at build time via tsx to generate embedded template constants
* @module scripts/embed-templates
*/
import * as fs from 'fs/promises';
import * as path from 'path';
import { fileURLToPath } from 'url';
// ESM-compatible __dirname/__filename
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const TEMPLATES_DIR = path.join(__dirname, '../templates');
const OUTPUT_FILE = path.join(__dirname, '../src/notebook/templates.generated.ts');
interface Template {
name: string;
content: string;
}
async function loadTemplates(): Promise<Template[]> {
const files = await fs.readdir(TEMPLATES_DIR);
const templateFiles = files.filter(f => f.endsWith('-template.src.md'));
const templates: Template[] = [];
for (const file of templateFiles) {
const filePath = path.join(TEMPLATES_DIR, file);
const content = await fs.readFile(filePath, 'utf-8');
// Extract template name from filename
// "sequential-feynman-template.src.md" -> "sequential-feynman"
const name = file.replace('-template.src.md', '');
templates.push({ name, content });
}
return templates;
}
function generateTypeScript(templates: Template[]): string {
const lines: string[] = [];
lines.push('/**');
lines.push(' * @fileoverview Auto-generated template definitions');
lines.push(' * DO NOT EDIT MANUALLY - Generated by scripts/embed-templates.ts');
lines.push(' * @module src/notebook/templates.generated');
lines.push(' */');
lines.push('');
// Generate template object
lines.push('export const TEMPLATES = {');
for (const template of templates) {
// Escape backticks and backslashes in content
const escapedContent = template.content
.replace(/\\/g, '\\\\')
.replace(/`/g, '\\`')
.replace(/\$/g, '\\$');
lines.push(` '${template.name}': \`${escapedContent}\`,`);
}
lines.push('} as const;');
lines.push('');
// Generate type
lines.push('export type TemplateName = keyof typeof TEMPLATES;');
lines.push('');
// Generate list of available templates
lines.push('export const AVAILABLE_TEMPLATES: readonly TemplateName[] = [');
for (const template of templates) {
lines.push(` '${template.name}',`);
}
lines.push('] as const;');
lines.push('');
// Helper function
lines.push('/**');
lines.push(' * Get template content by name');
lines.push(' * @throws Error if template not found');
lines.push(' */');
lines.push('export function getTemplate(name: string): string {');
lines.push(' const template = TEMPLATES[name as TemplateName];');
lines.push(' if (!template) {');
lines.push(` throw new Error(\`Template not found: \${name}. Available templates: \${AVAILABLE_TEMPLATES.join(', ')}\`);`);
lines.push(' }');
lines.push(' return template;');
lines.push('}');
return lines.join('\n');
}
async function main(): Promise<void> {
try {
console.log('🔨 Embedding templates...');
const templates = await loadTemplates();
console.log(` Found ${templates.length} template(s):`);
for (const t of templates) {
console.log(` - ${t.name} (${t.content.length} bytes)`);
}
const typescript = generateTypeScript(templates);
await fs.writeFile(OUTPUT_FILE, typescript, 'utf-8');
console.log(`✅ Generated ${OUTPUT_FILE}`);
} catch (error) {
console.error('❌ Failed to embed templates:', error);
process.exit(1);
}
}
main();