Generate typed accessors
reg.Get("components/header") is a stringly-typed API inside an otherwise
type-safe flow: rename a file and the compiler stays silent. The accessor
generator closes that gap. It scans your templates directory and emits a
typed wrapper — one method per template:
home, err := tpl.GetHome() // templates/home.htmlheader, err := tpl.GetComponentsHeader() // templates/components/header.htmlThe generated code calls registry.Get under the hood, so behavior is
identical — this is purely developer ergonomics plus refactor safety.
Run the generator
Section titled “Run the generator”go run github.com/alesr/templator/cmd/generate \ -package main \ -templates ./templates \ -out ./templator_accessors_gen.goOr install it once and run as a command:
go install github.com/alesr/templator/cmd/generate@latestAll flags and defaults are in the Generator CLI reference.
What it generates
Section titled “What it generates”Given:
templates/ home.html about.html components/header.htmlthe generator writes templator_accessors_gen.go (methods are emitted in
the lexical order of the walk, so about sorts before components/
before home):
// Code generated by go generate; DO NOT EDIT.package main
import "github.com/alesr/templator"
// TemplateAccessors provides typed helper accessors over templator Registry.type TemplateAccessors[T any] struct { registry *templator.Registry[T]}
// NewTemplateAccessors creates a TemplateAccessors wrapper for a templator Registry.func NewTemplateAccessors[T any](registry *templator.Registry[T]) *TemplateAccessors[T] { return &TemplateAccessors[T]{registry: registry}}
// GetAbout returns a handler for the about template.func (r *TemplateAccessors[T]) GetAbout() (*templator.Handler[T], error) { return r.registry.Get("about")}
// GetComponentsHeader returns a handler for the components/header template.func (r *TemplateAccessors[T]) GetComponentsHeader() (*templator.Handler[T], error) { return r.registry.Get("components/header")}
// GetHome returns a handler for the home template.func (r *TemplateAccessors[T]) GetHome() (*templator.Handler[T], error) { return r.registry.Get("home")}Naming rule: each path segment is title-cased (English casing, via
golang.org/x/text/cases) and the segments are joined after Get. The
template name keeps its slash-separated form (components/header), which is
exactly what Registry.Get expects (Registry and
handlers). The output
is gofmt-formatted.
Wire it into go:generate
Section titled “Wire it into go:generate”Add the directive to one of your source files so regeneration is one command:
//go:generate go run github.com/alesr/templator/cmd/generate -package main -templates ./templates -out ./templator_accessors_gen.gogo generate ./...Use the accessors
Section titled “Use the accessors”fs := os.DirFS(".")reg, _ := templator.NewRegistry[HomeData](fs)
tpl := NewTemplateAccessors(reg)
home, err := tpl.GetHome()if err != nil { log.Fatal(err)}
// home is *templator.Handler[HomeData] — same full behavior:err = home.Execute(ctx, w, HomeData{Title: "Welcome"})TemplateAccessors[T] keeps the generic type parameter, so it works with
any registry without boxing handlers away from the type-safe Handler[T]
API.
Regeneration habits
Section titled “Regeneration habits”- Commit the generated file — it is small, reviewable, and keeps builds hermetic.
- Regenerate on template inventory changes: adding, removing, or
renaming a template requires a
go generate ./...pass. If you forget, the build fails on stale or missing methods (GetAboutused without existing) — which is the same discipline as any_gen.gofile. - Never hand-edit: the file is gofmt’d, deterministic, and
DO NOT EDIT-marked for a reason; change the templates and regenerate instead.