/** * gen command * Generate outputs from specification * Generated from SpecVerse specification */ import { Command } from 'commander'; import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { basename, join } from 'path'; import { EngineRegistry } from '@specverse/entities'; import type { ParserEngine } from '@specverse/types'; /** * Register the gen command on the program. */ export function registerGenCommand(program: Command): void { const cmd = program .command('gen') .description('Generate outputs from specification'); cmd .command('diagrams ') .description('Generate Mermaid diagrams (ER, lifecycle, architecture, etc.)') .option('--type ', 'Diagram type: er, class, event-flow, lifecycle, architecture, deployment, manifest, all', "all") .option('-o, --output ', 'Output directory') .action(async (file: string, options: any) => { try { const registry = new EngineRegistry(); await registry.discover(); const parser = registry.getEngineForCapability('parse') as ParserEngine; if (!parser) { console.error('No parser engine found.'); process.exit(1); } await parser.initialize(); const content = readFileSync(file, 'utf8'); const parseResult = parser.parseContent(content, file); if (parseResult.errors.length > 0) { console.error('Invalid spec:'); parseResult.errors.forEach((e: string) => console.error(' ', e)); process.exit(1); } const gen = registry.getEngineForCapability('generate-diagrams') as any; if (!gen) { console.error('No generators engine found.'); process.exit(1); } await gen.initialize(); const diagrams = await gen.generateDiagrams(parseResult.ast!, { type: options.type || 'all' }); const outputDir = options.output || basename(file, '.specly') + '-diagrams'; if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true }); for (const [diagramType, diagramContent] of diagrams.entries()) { writeFileSync(join(outputDir, diagramType + '.mmd'), diagramContent); console.log(' ' + diagramType); } console.log('Generated ' + diagrams.size + ' diagrams in: ' + outputDir); } catch (error: any) { console.error('Error:', error.message); process.exit(1); } }); cmd .command('docs ') .description('Generate documentation from specification') .option('--format ', 'Output format: markdown, html', "markdown") .option('-o, --output ', 'Output file path') .action(async (file: string, options: any) => { try { const registry = new EngineRegistry(); await registry.discover(); const parser = registry.getEngineForCapability('parse') as ParserEngine; if (!parser) { console.error('No parser engine found.'); process.exit(1); } await parser.initialize(); const content = readFileSync(file, 'utf8'); const parseResult = parser.parseContent(content, file); if (parseResult.errors.length > 0) { console.error('Invalid spec:'); parseResult.errors.forEach((e: string) => console.error(' ', e)); process.exit(1); } const gen = registry.getEngineForCapability('generate-docs') as any; if (!gen) { console.error('No generators engine found.'); process.exit(1); } await gen.initialize(); const docs = await gen.generateDocs(parseResult.ast!, { format: options.format || 'markdown' }); const ext = options.format === 'html' ? '.html' : '.md'; const outputFile = options.output || basename(file, '.specly') + '-docs' + ext; writeFileSync(outputFile, docs); console.log('Documentation generated: ' + outputFile); } catch (error: any) { console.error('Error:', error.message); process.exit(1); } }); cmd .command('uml ') .description('Generate UML diagrams') .option('--type ', 'UML type: class, sequence, activity, all', "all") .action(async (file: string, options: any) => { try { const registry = new EngineRegistry(); await registry.discover(); const parser = registry.getEngineForCapability('parse') as ParserEngine; if (!parser) { console.error('No parser engine found.'); process.exit(1); } await parser.initialize(); const content = readFileSync(file, 'utf8'); const parseResult = parser.parseContent(content, file); if (parseResult.errors.length > 0) { console.error('Invalid spec:'); parseResult.errors.forEach((e: string) => console.error(' ', e)); process.exit(1); } const gen = registry.getEngineForCapability('generate-uml') as any; if (!gen) { console.error('No generators engine found.'); process.exit(1); } await gen.initialize(); const uml = await gen.generateUML(parseResult.ast!, { type: options.type || 'all' }); const outputFile = basename(file, '.specly') + '-uml.puml'; writeFileSync(outputFile, uml); console.log('UML generated: ' + outputFile); } catch (error: any) { console.error('Error:', error.message); process.exit(1); } }); }