A small, structure-aware template language for Go. Indentation maps to HTML nesting, so the parser builds a tree and the renderer emits matched tags. Malformed HTML is not expressible.
.hml files are source and runtime input. There is no transpiler, generated
code, or build step. The engine evaluates but never computes; formatting happens
in Go and arrives pre-formatted.
tmpl, err := hml.Parse(src, "show.hml", transforms)
out, err := tmpl.Render(locals, partialFn)See doc.go for the grammar, security model, and value semantics.
A parsed template reports what it reads, so an app can check its locals once at startup rather than one page at a time in production:
tmpl.Names() // free top-level identifiers the template reads
tmpl.Renders() // partials it renders by literal nameNames answers for one file. A partial inherits its caller's locals, so
follow Renders to check a whole page.
The engine ships zero built-ins. Rich text renders through app-registered
transforms, invoked as = name(field), each of which must sanitize its
own output:
transforms := map[string]hml.Transform{
"markdown": func(s string) string {
var buf bytes.Buffer
if err := goldmark.Convert([]byte(s), &buf); err != nil {
return ""
}
return mdPolicy.Sanitize(buf.String())
},
}Unregistered names are parse errors, so the parser stays the linter. The engine itself is stdlib-only.
This repo is also a tree-sitter grammar: grammar.js, an external
scanner for indentation, and highlight and injection queries under
queries/. The parser itself is not committed; tree-sitter generate
writes it, and nvim-treesitter runs that at install time.
Development happens on cibot, a
self-hosted review and CI server, which holds in progress branches.
GitHub receives main and the tags so go get works.
MIT