Documentation
¶
Overview ¶
Package htmlgen renders print-ready HTML documents from Go html/template skeletons and structured data.
A template is the target HTML with {{ }} actions in it:
<h1>Permit</h1>
<p>Permit No: {{ .permit_number }}</p>
{{- range .items }}
<li>{{ .code }}</li>
{{- end }}
It is the HTML sibling of xmlgen and follows the same shape: bytes in, bytes out, the same helpers, and the same rules for values. The caller assembles everything the document needs into data before calling Generate; the template only arranges and formats it.
Escaping ¶
Escaping is html/template's contextual escaping, unchanged: a value is escaped for the place it is printed — element text, a quoted attribute, a URL, a script or a style block. A value carrying markup cannot forge elements, and a javascript: URL cannot reach an href.
When html/template refuses a value for its context it prints the marker ZgotmplZ in its place. htmlgen does not return such a document: it reports ErrUnsafeValue instead. A template whose contexts html/template cannot determine is reported as ErrUnsafeTemplate.
A caller that needs to print trusted markup or a data: URL passes it as one of html/template's typed strings — template.HTML, template.URL and the rest — from Go code. JSON data cannot produce those types, so nothing a user submitted can opt itself out of escaping.
Values ¶
Before html/template escapes a value, htmlgen converts it to text by the same rules as xmlgen. A key the template references but the data does not have renders as the empty string, as does a JSON null. WithStrictKeys turns the first into an error, which is worth enabling in tests over a template. A map or slice has no text form and is an error rather than Go syntax in the document.
Numbers keep their exact text when data is supplied as JSON bytes, which are decoded with json.Decoder.UseNumber: 10000000 stays 10000000 rather than becoming 1e+07, and 1.50 keeps its trailing zero.
Output ¶
Output is bytes: an HTML document a browser can display and print, or a converter can turn into PDF. htmlgen does not produce PDF itself.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidData is returned when data was supplied as []byte or // json.RawMessage and could not be decoded as JSON. ErrInvalidData = errors.New("htmlgen: data could not be decoded") // ErrParseTemplate is returned when the template text cannot be parsed. A // "function not defined" message means the template calls a resolver that // was not supplied; "no such template" means a {{ template }} call names a // template that is never defined. ErrParseTemplate = errors.New("htmlgen: template could not be parsed") // ErrUnsupportedTemplate is returned when the template contains a // construct htmlgen cannot apply its value rules to, or calls html, js or // urlquery. Every value is already escaped for its context, so applying // one of those as well would escape it twice. ErrUnsupportedTemplate = errors.New("htmlgen: template uses an unsupported construct") // ErrUnsafeTemplate is returned when html/template cannot escape the // template safely — an action whose HTML context is ambiguous, such as a // branch that ends inside an attribute on one side and outside it on the // other, or a template that ends inside a tag or quoted attribute. ErrUnsafeTemplate = errors.New("htmlgen: template cannot be escaped safely") // ErrUnsupportedValue is returned when a template prints a value that has // no meaningful text form — a map, a slice, or a []byte. Printing one // would emit Go syntax into the document. ErrUnsupportedValue = errors.New("htmlgen: value cannot be rendered as text") // ErrUnsafeValue is returned when html/template refused a value for the // context it was printed in — a javascript: URL in an href, an expression // in a style attribute — and substituted its ZgotmplZ marker. The document // would otherwise be returned with that marker in place of the value. ErrUnsafeValue = errors.New("htmlgen: value was rejected as unsafe for its context") // ErrMissingKey is returned only under WithStrictKeys, when the template // references a key the data does not contain. ErrMissingKey = errors.New("htmlgen: data has no entry for a key the template uses") // ErrHelper is returned when a built-in helper is misused — a date that // does not match its layout, a non-numeric value passed to decimal, an odd // number of arguments to lookup. ErrHelper = errors.New("htmlgen: template helper failed") // ErrInvalidResolverName is returned when a resolver's name is empty, is // not a valid Go identifier, or its ResolveFunc is nil. Names are checked // before they reach html/template, which panics on a bad name rather than // returning an error. ErrInvalidResolverName = errors.New("htmlgen: resolver name is not a valid identifier") // ErrReservedResolverName is returned when a resolver's name collides with // a template builtin, with one of html/template's internal escapers, or // with a function htmlgen defines itself, or with the break and continue // keywords. A caller function silently shadows a builtin or a helper, and // demotes a keyword to an ordinary call, so this is rejected rather than // allowed to change what index, printf or break mean inside a template. // html/template's escapers already take precedence over a caller function, // so their names are reserved as a safeguard. ErrReservedResolverName = errors.New("htmlgen: resolver name is reserved") // ErrResolver is returned when a caller-supplied resolver returns an // error. The resolver's own error is wrapped, so errors.Is also matches // the caller's sentinel. ErrResolver = errors.New("htmlgen: resolver returned an error") // ErrRender is returned when template execution fails for any other reason. ErrRender = errors.New("htmlgen: template execution failed") // ErrOutputTooLarge is returned when the rendered document exceeds the // configured size limit. Rendering is aborted rather than completed. ErrOutputTooLarge = errors.New("htmlgen: rendered output exceeds the size limit") )
Sentinel errors returned by the htmlgen package. Use errors.Is to check for these in calling code.
Functions ¶
func Generate ¶
Generate renders tmpl against data and returns the HTML document.
data may be []byte or json.RawMessage, decoded internally so numeric literals keep their exact text, or any value html/template can walk: a map[string]any, a slice, or a struct addressed by Go field name.
Generate is safe for concurrent use, provided any resolvers are.
func GenerateTo ¶
GenerateTo is Generate writing to w.
It does not stream. It renders and checks in full and then writes once, so w receives nothing at all unless the whole document rendered — a caller writing to an http.ResponseWriter cannot emit half a document after a 200.
func Validate ¶
Validate reports whether tmpl is a usable htmlgen template, without rendering it. resolverNames are the names it is allowed to call beyond the built-in helpers; a call to anything else is reported here, as is a template html/template cannot escape safely.
It is for load-time checks — validating a template as it is stored, or a CI sweep over a template directory — so a broken template fails at deploy rather than when someone requests a document.
It does not check the arguments passed to helpers or resolvers — a date that does not match its layout, say — even when they are written into the template as literals. Those, like data problems, only Generate can find.
Types ¶
type Option ¶
type Option func(*options)
Option configures a render.
func WithMaxOutputBytes ¶
WithMaxOutputBytes caps the rendered document. A render that exceeds the cap is abandoned part-way with ErrOutputTooLarge. Zero selects the default of 32 MiB; a negative value removes the cap.
func WithResolvers ¶
WithResolvers supplies caller functions a template may call by name. They are for values that cannot be computed from the data — a lookup against a code list or another service. Formatting is already covered by the built-in helpers, and the document's own data belongs in data, so most templates need none.
func WithStrictKeys ¶
func WithStrictKeys() Option
WithStrictKeys makes a key the template references but the data does not contain an error rather than an empty value.
It is meant for tests over a template and for CI, not for production renders: it fires inside {{ if }} and {{ range }} as well, so a genuinely optional field needs a guard such as {{ if index . "middle_name" }}.
type ResolveFunc ¶
ResolveFunc is a caller-supplied function a template may call by name. It exists for values that cannot be computed from the data alone — a code-list description fetched from a database, a rate looked up in another service. Pure formatting is already covered by the built-in helpers, and the document's own content belongs in the data passed to Generate.
Arguments arrive as the Go values the template evaluated: string, bool, json.Number when htmlgen decoded the data from JSON, float64 when the caller passed an already-decoded map, map[string]any, []any, or nil.
A ResolveFunc must be safe for concurrent use, and must not have durable side effects. The template engine calls a function once per evaluated occurrence and skips occurrences in branches it does not take, so a counter increment would depend on the shape of the template and would run again on a retried render. Mint reference numbers before calling Generate and put them in the data.
type Resolvers ¶
type Resolvers map[string]ResolveFunc
Resolvers maps template-visible names to functions. Each name must be a valid Go identifier and must not collide with a template builtin, the break or continue keywords, an html/template escaper, or a function htmlgen defines itself.
Each resolver is registered under its own name, so {{ codelist ... }} rather than {{ resolve "codelist" ... }}. A template that calls a resolver which was not supplied then fails when the template is parsed, instead of at execution time if and when a branch happens to be taken.