Looking at your current `TemplateInterpreter` design, you'll need to extend it to handle multi-file output scenarios. Here are several strategies to consider: ## 1. Multi-Output Template Registry Extend your template registry to support templates that define multiple output files: ```go type MultiFileTemplate struct { Name string OutputPath string Content string Dependencies []string } type MultiFileRegistry struct { templates map[string][]MultiFileTemplate funcMap template.FuncMap } ``` ## 2. Template Manifest Approach Create a manifest system where templates can declare their output structure: ```go type TemplateManifest struct { Name string `yaml:"name"` Description string `yaml:"description"` Outputs []OutputFile `yaml:"outputs"` } type OutputFile struct { Path string `yaml:"path"` Template string `yaml:"template"` Condition string `yaml:"condition,omitempty"` } ``` ## 3. Directory-Based Output Strategy Modify your interpreter to return a map of file paths to content: ```go func (ti *TemplateInterpreter) InterpretMultiFile(masonryInput, templateDir string) (map[string]string, error) { // Returns map[outputPath]content } ``` ## 4. Template Composition with File Directives Add special template functions for file operations: ```go // In your funcMap "writeFile": func(path, content string) string { // Queue file for writing return "" }, "includeFile": func(templateName string, data interface{}) string { // Include another template's output }, ``` ## 5. Build Pipeline Approach Create a build pipeline that processes templates in stages: ```go type BuildPipeline struct { stages []BuildStage } type BuildStage struct { Name string Templates []string OutputDir string Condition func(ast lang.AST) bool } ``` ## Recommended Implementation I'd suggest starting with a **Template Manifest + Multi-Output** approach. This gives you flexibility while maintaining the simplicity of your current design: ```go type MultiFileInterpreter struct { registry *TemplateRegistry outputs map[string]string } func (mfi *MultiFileInterpreter) InterpretToFiles(masonryInput, templateDir, manifestFile string) (map[string]string, error) { // Load manifest // Process each output file defined in manifest // Return map of filepath -> content } ``` This approach allows you to: - Keep your existing single-file templates working - Define complex multi-file outputs via manifest files - Maintain template reusability across different output scenarios - Support conditional file generation based on AST content Would you like me to elaborate on any of these strategies or help implement a specific approach? Yeah, can you show me how a Template Manifest with Multi-output might work? I'd like to also see what it would look like for an end user to set up and get working. I'll show you how a Template Manifest with Multi-output approach would work, including the implementation and user experience. ## Implementation First, let's define the manifest structure: ```go type TemplateManifest struct { Name string `yaml:"name"` Description string `yaml:"description"` Outputs []OutputFile `yaml:"outputs"` } type OutputFile struct { Path string `yaml:"path"` Template string `yaml:"template"` Condition string `yaml:"condition,omitempty"` } ``` Now let's extend your `TemplateInterpreter` to support multi-file output: ```go // InterpretToFiles processes a manifest and returns multiple output files func (ti *TemplateInterpreter) InterpretToFiles(masonryFile, templateDir, manifestFile string) (map[string]string, error) { // Load templates from directory err := ti.registry.LoadFromDirectory(templateDir) if err != nil { return nil, fmt.Errorf("error loading templates: %w", err) } // Load manifest manifestPath := filepath.Join(templateDir, manifestFile) manifestData, err := os.ReadFile(manifestPath) if err != nil { return nil, fmt.Errorf("error reading manifest: %w", err) } var manifest TemplateManifest err = yaml.Unmarshal(manifestData, &manifest) if err != nil { return nil, fmt.Errorf("error parsing manifest: %w", err) } // Parse Masonry input masonryInput, err := os.ReadFile(masonryFile) if err != nil { return nil, fmt.Errorf("error reading Masonry file: %w", err) } ast, err := lang.ParseInput(string(masonryInput)) if err != nil { return nil, fmt.Errorf("error parsing Masonry input: %w", err) } // Process each output file outputs := make(map[string]string) for _, output := range manifest.Outputs { // Check condition if specified if output.Condition != "" && !ti.evaluateCondition(output.Condition, ast) { continue } // Execute template content, err := ti.executeTemplate(output.Template, ast) if err != nil { return nil, fmt.Errorf("error executing template %s: %w", output.Template, err) } outputs[output.Path] = content } return outputs, nil } func (ti *TemplateInterpreter) executeTemplate(templateName string, ast lang.AST) (string, error) { if tmpl, exists := ti.registry.templates[templateName]; exists { data := struct { AST lang.AST Registry *TemplateRegistry }{ AST: ast, Registry: ti.registry, } var buf bytes.Buffer err := tmpl.Execute(&buf, data) return buf.String(), err } return "", fmt.Errorf("template %s not found", templateName) } func (ti *TemplateInterpreter) evaluateCondition(condition string, ast lang.AST) bool { // Simple condition evaluation - you can expand this switch condition { case "has_api": return len(ast.APIs) > 0 case "has_ui": return len(ast.UIs) > 0 case "has_database": return len(ast.Databases) > 0 default: return true } } ``` You'll also need to add the yaml import: ```go import "gopkg.in/yaml.v3" ``` ## End User Experience Here's how an end user would set up a React component generator: ### 1. Project Structure ``` templates/react-components/ ├── manifest.yaml ├── component.tmpl ├── component-test.tmpl ├── index.tmpl └── styles.tmpl ``` ### 2. Manifest File (`manifest.yaml`) ```yaml name: "React Component Generator" description: "Generates React components with tests and styles" outputs: - path: "src/components/{{.AST.UIs.0.Name}}/{{.AST.UIs.0.Name}}.tsx" template: "component" condition: "has_ui" - path: "src/components/{{.AST.UIs.0.Name}}/{{.AST.UIs.0.Name}}.test.tsx" template: "component-test" condition: "has_ui" - path: "src/components/{{.AST.UIs.0.Name}}/index.ts" template: "index" condition: "has_ui" - path: "src/components/{{.AST.UIs.0.Name}}/{{.AST.UIs.0.Name}}.module.css" template: "styles" condition: "has_ui" ``` ### 3. Template Files **`component.tmpl`:** ```typescript import React from 'react'; import styles from './{{.AST.UIs.0.Name}}.module.css'; interface {{.AST.UIs.0.Name}}Props { {{- range .AST.UIs.0.Fields}} {{.Name}}: {{goType .Type}}; {{- end}} } export const {{.AST.UIs.0.Name}}: React.FC<{{.AST.UIs.0.Name}}Props> = ({ {{- range $i, $field := .AST.UIs.0.Fields}} {{if $i}},{{end}} {{$field.Name}} {{- end}} }) => { return (