feat: add modusgraphgen code generator#10
feat: add modusgraphgen code generator#10mlwelles wants to merge 10 commits intomatthewmcneely:mainfrom
Conversation
Merge the standalone modusGraphGen code generator into the modusGraph repository as cmd/modusgraphgen/. The tool parses Go structs with json/dgraph tags and generates typed CRUD clients, query builders, auto-paging iterators, functional options, and a Kong CLI. Code is organized under cmd/modusgraphgen/internal/ with model, parser, and generator packages. Test fixtures are self-contained in testdata/. The generated code imports github.com/matthewmcneely/modusgraph and requires no additional dependencies beyond the Go standard library.
There was a problem hiding this comment.
4 issues found across 59 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmd/modusgraphgen/internal/generator/templates/options.go.tmpl">
<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/options.go.tmpl:6">
P2: time import detection only checks types starting with `time.`, so pointer/slice/map time types (e.g., `*time.Time`, `[]time.Time`) won't trigger the import and will generate code that references `time` without importing it.</violation>
</file>
<file name="cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl">
<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl:68">
P2: Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.</violation>
</file>
<file name="cmd/modusgraphgen/internal/parser/parser.go">
<violation number="1" location="cmd/modusgraphgen/internal/parser/parser.go:1">
P2: Parser iteration over Go maps (`pkgs` and `pkgAST.Files`) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.</violation>
</file>
<file name="cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl">
<violation number="1" location="cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl:3">
P2: iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| {{$name := .Entity.Name}} | ||
| {{$fields := scalarFields .Entity.Fields}} | ||
| {{- $needsTime := false}} | ||
| {{- range $fields}}{{if hasPrefix .GoType "time."}}{{$needsTime = true}}{{end}}{{end}} |
There was a problem hiding this comment.
P2: time import detection only checks types starting with time., so pointer/slice/map time types (e.g., *time.Time, []time.Time) won't trigger the import and will generate code that references time without importing it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/options.go.tmpl, line 6:
<comment>time import detection only checks types starting with `time.`, so pointer/slice/map time types (e.g., `*time.Time`, `[]time.Time`) won't trigger the import and will generate code that references `time` without importing it.</comment>
<file context>
@@ -0,0 +1,34 @@
+{{$name := .Entity.Name}}
+{{$fields := scalarFields .Entity.Fields}}
+{{- $needsTime := false}}
+{{- range $fields}}{{if hasPrefix .GoType "time."}}{{$needsTime = true}}{{end}}{{end}}
+{{- $extImports := externalImports $fields .Imports}}
+{{if or $needsTime (gt (len $extImports) 0)}}
</file context>
|
|
||
| func (c *{{.Name}}AddCmd) Run(client *{{$.Name}}.Client) error { | ||
| v := &{{$.Name}}.{{.Name}}{ | ||
| {{- range scalarFields .Fields}}{{if and (not .IsUID) (not .IsDType) (eq .GoType "string")}} |
There was a problem hiding this comment.
P2: Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/cli.go.tmpl, line 68:
<comment>Non-string scalar CLI inputs are ignored because only string-typed fields are copied into the entity in the generated Add command.</comment>
<file context>
@@ -0,0 +1,129 @@
+
+func (c *{{.Name}}AddCmd) Run(client *{{$.Name}}.Client) error {
+ v := &{{$.Name}}.{{.Name}}{
+{{- range scalarFields .Fields}}{{if and (not .IsUID) (not .IsDType) (eq .GoType "string")}}
+ {{.Name}}: c.{{.Name}},
+{{- end}}{{end}}
</file context>
| @@ -0,0 +1,354 @@ | |||
| // Package parser extracts entity and field metadata from Go source files by | |||
There was a problem hiding this comment.
P2: Parser iteration over Go maps (pkgs and pkgAST.Files) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/parser/parser.go:
<comment>Parser iteration over Go maps (`pkgs` and `pkgAST.Files`) is unsorted, making package selection and entity ordering nondeterministic across runs. This can yield unstable or incorrect generated output when multiple packages or files are present.</comment>
<file context>
@@ -0,0 +1,354 @@
+// Package parser extracts entity and field metadata from Go source files by
+// inspecting struct declarations and their struct tags. It uses go/ast and
+// go/parser to walk the AST, then builds a model.Package for the generator.
+package parser
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
</file context>
| @@ -0,0 +1,63 @@ | |||
| package {{.Name}} | |||
|
|
|||
| import ( | |||
There was a problem hiding this comment.
P2: iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/modusgraphgen/internal/generator/templates/iter.go.tmpl, line 3:
<comment>iter.go.tmpl always emits context/iter imports even when .Entities is empty, producing an iter_gen.go with unused imports and no declarations. This can break builds for empty schemas.</comment>
<file context>
@@ -0,0 +1,63 @@
+package {{.Name}}
+
+import (
+ "context"
+ "iter"
</file context>
There was a problem hiding this comment.
1 issue found across 63 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".gitignore">
<violation number="1" location=".gitignore:27">
P2: Unscoped `.gitignore` pattern `query` will ignore untracked files under existing `cmd/query`, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| .osgrep | ||
| .opencode | ||
| .claude | ||
| query |
There was a problem hiding this comment.
P2: Unscoped .gitignore pattern query will ignore untracked files under existing cmd/query, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .gitignore, line 27:
<comment>Unscoped `.gitignore` pattern `query` will ignore untracked files under existing `cmd/query`, making future additions easy to miss. Scope the ignore to the repo root if that's the intent.</comment>
<file context>
@@ -24,3 +24,4 @@ cpu_profile.prof
.osgrep
.opencode
.claude
+query
</file context>
| query | |
| /query |
Remove .idea/.gitignore, modusgraphgen binary, and benchmark JSON files that were accidentally committed. Update .gitignore to prevent recurrence.
|
@mlwelles Can you create a README in the cmd/modusgraph-gen folder that provides an overview of when/how to use this? For instance, how does it improve upon one simply creating a type TestEntity struct {
Name string `json:"name,omitempty" dgraph:"index=exact"`
Description string `json:"description,omitempty" dgraph:"index=term"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UID string `json:"uid,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
}and then |
Summary
cmd/modusgraphgen/, a code generation tool that reads Go structs withjson/dgraphtags and produces typed CRUD clients, query builders, auto-paging iterators, functional options, and a Kong CLIgo/ast,go/parser,text/template,embed)cmd/modusgraphgen/internal/withmodel,parser, andgeneratorpackagestestdata/with golden file regression tests.osgrep,.opencode,.claudeto.gitignoreUsage
//go:generate go run github.com/matthewmcneely/modusgraph/cmd/modusgraphgenOr install directly:
What Gets Generated
client_gen.goClientwith sub-clients per entitypage_options_gen.goFirst(n)andOffset(n)paginationiter_gen.goSearchIterandListIteriterators<entity>_gen.goGet,Add,Update,Delete,Search,List<entity>_options_gen.go<entity>_query_gen.gocmd/<pkg>/main.goTesting
All parser and generator tests pass. Existing modusGraph tests unaffected.
Summary by cubic
Adds modusgraph-gen, a standard-library code generator that reads Go structs with json/dgraph tags and generates a typed client, CRUD APIs, fluent query builders, auto-paging iterators, functional options, and an optional Kong CLI with a raw DQL query command. Also updates .gitignore and removes stray binaries/IDE files; README and golden tests updated; no new dependencies.
New Features
Migration
Written for commit d6f4145. Summary will update on new commits.