← back to Cli Printing Press
refactor(cli): split generator vision render stages (#314)
52a3dbc5882353749617bdb2d3ab23313c5af5e7 · 2026-04-26 15:03:57 -0700 · Trevin Chow
Continue decomposing generator orchestration by giving the vision/root rendering tail named stages. This keeps generated output unchanged while making future store, MCP, workflow, and promoted-command changes easier to review in isolation.
Files touched
M internal/generator/generator.go
Diff
commit 52a3dbc5882353749617bdb2d3ab23313c5af5e7
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sun Apr 26 15:03:57 2026 -0700
refactor(cli): split generator vision render stages (#314)
Continue decomposing generator orchestration by giving the vision/root rendering tail named stages. This keeps generated output unchanged while making future store, MCP, workflow, and promoted-command changes easier to review in isolation.
---
internal/generator/generator.go | 110 +++++++++++++++++++++++++++++-----------
1 file changed, 81 insertions(+), 29 deletions(-)
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index f62c7aba..82cee2e5 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -1293,9 +1293,33 @@ func (g *Generator) renderMCPEntrypoint() error {
}
func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand, promotedResourceNames map[string]bool) error {
- hasPromoted := len(promotedCommands) > 0
+ schema := g.schemaWithDependentParents()
- // Vision features: profile already computed in early profiling above
+ if err := g.renderStoreFiles(schema); err != nil {
+ return err
+ }
+
+ visionData := g.visionRenderData(schema)
+ if err := g.renderVisionCommands(visionData); err != nil {
+ return err
+ }
+ workflowConstructors, err := g.renderWorkflowFiles(visionData)
+ if err != nil {
+ return err
+ }
+ insightConstructors := g.renderInsightFiles()
+
+ if err := g.renderMCPToolFiles(schema); err != nil {
+ return err
+ }
+ if err := g.renderPromotedCommandFiles(promotedCommands); err != nil {
+ return err
+ }
+
+ return g.renderRootProjectFiles(promotedCommands, promotedResourceNames, workflowConstructors, insightConstructors)
+}
+
+func (g *Generator) schemaWithDependentParents() []TableDef {
schema := BuildSchema(g.Spec)
// Add parent_id column to tables for dependent (parent-child) sync resources
@@ -1328,6 +1352,10 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
+ return schema
+}
+
+func (g *Generator) renderStoreFiles(schema []TableDef) error {
// Create store directory if needed
if g.VisionSet.Store {
if err := os.MkdirAll(filepath.Join(g.OutputDir, "internal", "store"), 0755); err != nil {
@@ -1355,17 +1383,24 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
- // Render vision CLI commands
- visionCmds := map[string]string{
- "export.go.tmpl": filepath.Join("internal", "cli", "export.go"),
- "import.go.tmpl": filepath.Join("internal", "cli", "import.go"),
- "search.go.tmpl": filepath.Join("internal", "cli", "search.go"),
- "sync.go.tmpl": filepath.Join("internal", "cli", "sync.go"),
- "tail.go.tmpl": filepath.Join("internal", "cli", "tail.go"),
- "analytics.go.tmpl": filepath.Join("internal", "cli", "analytics.go"),
- }
+ return nil
+}
- // Build GraphQL field path mapping for sync templates
+type visionRenderData struct {
+ *spec.APISpec
+ SyncableResources []profiler.SyncableResource
+ DependentSyncResources []profiler.DependentResource
+ SearchableFields map[string][]string
+ Tables []TableDef
+ Pagination profiler.PaginationProfile
+ SearchEndpointPath string
+ SearchQueryParam string
+ SearchEndpointMethod string
+ SearchBodyFields []profiler.SearchBodyField
+ GraphQLFieldPaths map[string]string
+}
+
+func (g *Generator) visionRenderData(schema []TableDef) visionRenderData {
gqlFieldPaths := map[string]string{}
for rName, r := range g.Spec.Resources {
if ep, ok := r.Endpoints["list"]; ok && ep.ResponsePath != "" {
@@ -1373,19 +1408,7 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
- visionData := struct {
- *spec.APISpec
- SyncableResources []profiler.SyncableResource
- DependentSyncResources []profiler.DependentResource
- SearchableFields map[string][]string
- Tables []TableDef
- Pagination profiler.PaginationProfile
- SearchEndpointPath string
- SearchQueryParam string
- SearchEndpointMethod string
- SearchBodyFields []profiler.SearchBodyField
- GraphQLFieldPaths map[string]string
- }{
+ return visionRenderData{
APISpec: g.Spec,
SyncableResources: g.profile.SyncableResources,
DependentSyncResources: g.profile.DependentSyncResources,
@@ -1398,6 +1421,18 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
SearchBodyFields: g.profile.SearchBodyFields,
GraphQLFieldPaths: gqlFieldPaths,
}
+}
+
+func (g *Generator) renderVisionCommands(visionData visionRenderData) error {
+ // Render vision CLI commands
+ visionCmds := map[string]string{
+ "export.go.tmpl": filepath.Join("internal", "cli", "export.go"),
+ "import.go.tmpl": filepath.Join("internal", "cli", "import.go"),
+ "search.go.tmpl": filepath.Join("internal", "cli", "search.go"),
+ "sync.go.tmpl": filepath.Join("internal", "cli", "sync.go"),
+ "tail.go.tmpl": filepath.Join("internal", "cli", "tail.go"),
+ "analytics.go.tmpl": filepath.Join("internal", "cli", "analytics.go"),
+ }
gqlSpec := isGraphQLSpec(g.Spec)
for _, tmplName := range g.VisionSet.TemplateNames() {
@@ -1422,10 +1457,14 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
+ return nil
+}
+
+func (g *Generator) renderWorkflowFiles(visionData visionRenderData) ([]string, error) {
// Render data source resolution template when store is enabled
if g.VisionSet.Store {
if err := g.renderTemplate("data_source.go.tmpl", filepath.Join("internal", "cli", "data_source.go"), visionData); err != nil {
- return fmt.Errorf("rendering data_source: %w", err)
+ return nil, fmt.Errorf("rendering data_source: %w", err)
}
}
@@ -1441,7 +1480,7 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
SearchableFields: g.profile.SearchableFields,
}
if err := g.renderTemplate("channel_workflow.go.tmpl", filepath.Join("internal", "cli", "channel_workflow.go"), workflowData); err != nil {
- return fmt.Errorf("rendering workflow: %w", err)
+ return nil, fmt.Errorf("rendering workflow: %w", err)
}
}
@@ -1459,7 +1498,12 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
+ return renderedWorkflowConstructors, nil
+}
+
+func (g *Generator) renderInsightFiles() []string {
var renderedInsightConstructors []string
+
// Render insight templates
for _, tmpl := range g.VisionSet.Insights {
outName := strings.TrimSuffix(filepath.Base(tmpl), ".tmpl")
@@ -1473,6 +1517,10 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
+ return renderedInsightConstructors
+}
+
+func (g *Generator) renderMCPToolFiles(schema []TableDef) error {
// Render MCP tools registration (needs VisionSet + store data + tool counts for annotations)
if g.VisionSet.MCP {
mcpTotal, mcpPublic := g.Spec.CountMCPTools()
@@ -1513,8 +1561,12 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
+ return nil
+}
+
+func (g *Generator) renderPromotedCommandFiles(promotedCommands []PromotedCommand) error {
// Generate api discovery command when promoted commands exist (lets users browse the raw generated surface)
- if hasPromoted {
+ if len(promotedCommands) > 0 {
if err := g.renderTemplate("api_discovery.go.tmpl", filepath.Join("internal", "cli", "api_discovery.go"), g.Spec); err != nil {
return fmt.Errorf("rendering api discovery: %w", err)
}
@@ -1550,7 +1602,7 @@ func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand,
}
}
- return g.renderRootProjectFiles(promotedCommands, promotedResourceNames, renderedWorkflowConstructors, renderedInsightConstructors)
+ return nil
}
func (g *Generator) renderRootProjectFiles(promotedCommands []PromotedCommand, promotedResourceNames map[string]bool, renderedWorkflowConstructors, renderedInsightConstructors []string) error {
← 79b134aa refactor(cli): split generator render stages (#313)
·
back to Cli Printing Press
·
refactor(cli): split scorecard scoring stages (#315) 015ad88e →