skills/dagger-codegen/references/go-templates.md
Load when: Editing Go templates, debugging generated output, or confused about which template produces what.
cmd/codegen/generator/go/templates/src/
├── dagger.gen.go.tmpl # Entry point
├── _dagger.gen.go/ # Sub-templates
│ ├── module.go.tmpl # Module: imports + calls ModuleMainSrc()
│ ├── defs.go.tmpl # Type definitions, Client struct
│ └── client.go.tmpl # Standalone: Connect(), Close()
├── _types/ # Type-specific
│ ├── object.go.tmpl # Object methods
│ ├── scalar.go.tmpl
│ ├── input.go.tmpl
│ └── enum.go.tmpl
├── dag/
│ └── dag.gen.go.tmpl # Global dag.* helpers (non-module only)
└── internal/dagger/
└── dagger.gen.go.tmpl # Module types package
Every template decision uses these booleans:
| Condition | True When | Effect |
|---|---|---|
IsModuleCode | ModuleConfig != nil && ModuleName != "" | Generates module runtime |
IsStandaloneClient | ClientConfig != nil | Includes Connect/Close |
IsPartial | First pass of two-pass | Skips main() generation |
IsModuleCode = true)Two output files:
| File | Package | Contains |
|---|---|---|
dagger.gen.go | main | main(), invoke() dispatch |
internal/dagger/dagger.gen.go | dagger | Type definitions |
Why two? User code in main imports internal/dagger. Types live there to avoid namespace pollution.
IsStandaloneClient = true)| File | Package | Contains |
|---|---|---|
dagger.gen.go | dagger | Types + Connect() + Close() |
dag/dag.gen.go | dag | Global dag.* helpers |
IsModuleCode = false, IsStandaloneClient = false)Same as standalone client, minus Connect()/Close().
Go modules need two passes because generated code depends on user types.
Pass 0 (Partial):
dagger.gen.gomain.go if missingIsPartial() = true → no main() yetPass 1 (Complete):
invoke() dispatchIsPartial() = falseDefined in cmd/codegen/generator/go/templates/functions.go:54:
| Function | Purpose |
|---|---|
IsModuleCode() | Check if generating module |
IsStandaloneClient() | Check if generating client |
IsPartial() | Check if first pass |
FormatName(s) | GraphQL → Go name |
FormatReturnType(f) | Field → return type |
ModuleMainSrc() | Generate main() + invoke() |
IsArgOptional(arg) | Check if argument is optional (has default OR nullable type) |
HasOptionals(args) | Check if any argument in list is optional |
internal/dagger/dagger.gen.go, not rootdagClientcmd/codegen after changesIsArgOptional $arg not $arg.TypeRef.IsOptional. The latter only checks if the type is nullable, missing arguments with default values. Similarly, use HasOptionals $field.Args not $field.Args.HasOptionals for consistency with the helper function.