Documentation
¶
Overview ¶
Package htmlbind parses typed HTML template sources into an AST.
Index ¶
- Constants
- func CleanOutputName(name string) (string, error)
- func Generate(filename string, source []byte, options GenerateOptions) ([]byte, error)
- func JoinPublicURL(base, name string) string
- func Printer() syntax.BodyPrinter
- func RootPrinter() syntax.RootPrinter
- func ValidateReferenceHooks(hooks []ReferenceHook) error
- type ActionRef
- type Annotation
- type Asset
- type AssetKind
- type Attribute
- type AttributePart
- type AwaitBinding
- type AwaitNode
- type BinaryExpr
- type Body
- type CallExpr
- type CommentNode
- type CompileError
- type ComponentNode
- type ConditionalExpr
- type ConversionInputs
- type Declaration
- type DoctypeNode
- type DynamicReference
- type ElementNode
- type EnumDecl
- type EnumMember
- type Expr
- type ExpressionNode
- type ExternalDecl
- type Field
- type ForNode
- type GenerateOptions
- type HeadNode
- type HookError
- type IdentifierExpr
- type IfNode
- type ImportDecl
- type IndexExpr
- type LiteralExpr
- type MemberExpr
- type Module
- type Node
- type PackageDecl
- type Parameter
- type ParseError
- type Position
- type ProducedFile
- type ReferenceHook
- type ReferenceRequest
- type ReferenceResult
- type Result
- type Rewrite
- type Signature
- type SignatureParam
- type SlotNode
- type TemplateDecl
- type TextNode
- type TypeDecl
- type TypeRef
- type UnaryExpr
Constants ¶
const DefaultActionAttr = "data-tb-action"
DefaultActionAttr is the attribute the lowering writes when GenerateOptions.ServerActionAttr is empty. A framework driving its own client library points it at that library's vocabulary instead.
const DefaultDataAttributePrefix = "tb"
DefaultDataAttributePrefix names the generated data attributes. A project whose markup already uses this prefix overrides it through GenerateOptions.
const DefaultPublicURLBase = "/public/generated"
DefaultPublicURLBase is the URL prefix used when a project configures none. Extraction always happens, so a zero-configuration project still gets working asset URLs.
const ReservedPageFunc = "Load"
ReservedPageFunc is the Go entry point name a route package gives its own page, which is therefore never a server action.
const ServerActionAttr = "server-action"
ServerActionAttr is the reserved attribute naming a Go handler instead of a URL. It never reaches the output; the compiler replaces it with the attribute that carries the handler's endpoint.
See decision:server-action-lowering. The value is a static handler name because the symbol has to resolve at generation, and only the caller can resolve it: the URL depends on the route the template serves, which the compiler cannot see. That is why lowering takes two passes, with ActionRefs reporting what a module references and GenerateOptions.ServerActions carrying the answers back.
Variables ¶
This section is empty.
Functions ¶
func CleanOutputName ¶ added in v0.3.1
CleanOutputName normalizes a produced file name and refuses one that leaves the output root, because a caller writes these files unexamined.
func Generate ¶
func Generate(filename string, source []byte, options GenerateOptions) ([]byte, error)
Generate compiles an HTML template module to Go, discarding the extracted assets. Callers that write files use GenerateModule instead.
func JoinPublicURL ¶ added in v0.2.9
JoinPublicURL appends a generated file name to the configured URL base. The base is used verbatim, so an absolute URL path and a full CDN URL behave the same and no path segment is ever inferred.
func Printer ¶ added in v0.3.1
func Printer() syntax.BodyPrinter
Printer returns the HTML body printer, the printing half of the format parser registered by Parse. It implements rule:html-template-layout.
func RootPrinter ¶ added in v0.3.1
func RootPrinter() syntax.RootPrinter
RootPrinter is the registration the shared module printer needs.
func ValidateReferenceHooks ¶ added in v0.3.1
func ValidateReferenceHooks(hooks []ReferenceHook) error
ValidateReferenceHooks checks a registration set before any template is read, so a malformed hook fails at the generate command rather than at a template position it has nothing to do with.
Two hooks may share an element and attribute pair; whether they collide depends on the values a template writes, so that is checked at use.
Types ¶
type ActionRef ¶ added in v0.2.3
type ActionRef struct {
// Component is the declaration the reference appears in.
Component string
// Handler is the Go function name the attribute named.
Handler string
// Element is the element carrying the attribute, such as form or button.
Element string
// Pos is the attribute position, for a diagnostic that can quote the source.
Pos Position
}
ActionRef is one server-action reference found in a template.
func ActionRefs ¶ added in v0.2.3
ActionRefs parses and analyzes a template module and returns every server-action reference it makes, in source order.
It is the first of the two passes lowering needs: a caller resolves these names against the Go package beside the template, derives an endpoint URL for each, and passes the result to Generate as GenerateOptions.ServerActions.
Like Signatures it runs the same analysis Generate does, so a module that fails to compile fails here with the same diagnostic rather than yielding a partial answer.
type Annotation ¶ added in v0.1.16
type Annotation = syntax.Annotation
type Asset ¶ added in v0.2.9
type Asset struct {
Kind AssetKind
// Base is the file name without its extension.
Base string
// Extension is css for a stylesheet and js for a script, without a dot.
Extension string
Content []byte
// URL is the reference written into the emitted link or script tag.
URL string
}
Asset is one static file extracted from component head declarations. The content hash is part of the name, so a reference URL is immutably cacheable and identical input regenerates identical names and bytes.
type AssetKind ¶ added in v0.2.9
type AssetKind string
AssetKind classifies one extracted static file.
type Attribute ¶
type Attribute struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Name string `json:"name"`
Boolean bool `json:"boolean,omitempty"`
Value []AttributePart `json:"value,omitempty"`
}
type AttributePart ¶
type AwaitBinding ¶ added in v0.1.16
type AwaitBinding = syntax.AwaitBinding
type BinaryExpr ¶
type BinaryExpr = syntax.BinaryExpr
type CommentNode ¶
type CommentNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Text string `json:"text"`
}
func (*CommentNode) NodeType ¶
func (n *CommentNode) NodeType() string
type CompileError ¶
func (*CompileError) Error ¶
func (e *CompileError) Error() string
type ComponentNode ¶
type ComponentNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Name string `json:"name"`
Arguments []Attribute `json:"arguments,omitempty"`
Children []Node `json:"children,omitempty"`
SelfClosing bool `json:"selfClosing,omitempty"`
}
func (*ComponentNode) NodeType ¶
func (n *ComponentNode) NodeType() string
type ConditionalExpr ¶
type ConditionalExpr = syntax.ConditionalExpr
type ConversionInputs ¶ added in v0.3.1
type ConversionInputs struct {
// Sources are the authored files the conversion reads. Their contents are
// digested into the cache key, and they are recorded as build inputs, so an
// edit to one both invalidates the cached result and regenerates.
Sources []string
// Params is everything else the output depends on: the target format, the
// quality setting, the version of the encoder doing the work. Anything left
// out here is something a cache hit will silently ignore.
Params string
}
ConversionInputs is everything one conversion's output depends on, named without doing the conversion.
type Declaration ¶
type Declaration = syntax.Declaration
type DoctypeNode ¶
type DoctypeNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Text string `json:"text"`
}
func (*DoctypeNode) NodeType ¶
func (n *DoctypeNode) NodeType() string
type DynamicReference ¶ added in v0.3.1
DynamicReference is an attribute a hook is registered for whose value is a template expression, and therefore does not exist at generation time.
It is reported rather than ignored: a page half rewritten with nothing said about it is the failure mode this seam exists to avoid. Set StrictReferenceHooks to make it a compile error instead.
type ElementNode ¶
type ElementNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Name string `json:"name"`
Attributes []Attribute `json:"attributes,omitempty"`
Children []Node `json:"children,omitempty"`
SelfClosing bool `json:"selfClosing,omitempty"`
}
func (*ElementNode) NodeType ¶
func (n *ElementNode) NodeType() string
type EnumMember ¶
type EnumMember = syntax.EnumMember
type ExpressionNode ¶
type ExpressionNode = syntax.ExpressionNode
type ExternalDecl ¶
type ExternalDecl = syntax.ExternalDecl
type GenerateOptions ¶
type GenerateOptions struct {
// Package overrides the template package/module declaration.
Package string
// DataAttributePrefix names the generated update protocol data attributes.
// Empty uses DefaultDataAttributePrefix.
DataAttributePrefix string
// Unit names the generation unit whose assets are extracted. Empty derives
// it from the template file name.
Unit string
// PublicURLBase prefixes generated asset file names in head references.
// Empty uses DefaultPublicURLBase. The value is used verbatim, so an
// absolute URL path and a full CDN URL behave the same.
PublicURLBase string
// ContextExternals names the external functions whose Go implementation
// takes a leading context.Context. Those calls receive the boundary's
// context; every other external is called as an ordinary function.
//
// The caller discovers this by reading the package's Go sources, so the
// template declaration stays the same either way and the choice belongs to
// whoever writes the implementation.
ContextExternals map[string]bool
// PreserveWhitespace turns off requirement:static-whitespace-normalization,
// so static output keeps the authoring indentation and newlines byte for
// byte. It exists for a project comparing generated markup against
// pre-existing golden files.
PreserveWhitespace bool
// ServerActions maps each handler name a template reaches through
// ServerActionAttr to the endpoint URL the lowering writes. The caller
// resolves it, because the URL depends on the route the template serves and
// the compiler cannot see that; [ActionRefs] reports what needs resolving.
//
// A reference with no entry here is a compile error, so a template naming a
// handler nobody resolved never silently emits a dead element.
ServerActions map[string]string
// ServerActionResolver answers a name ServerActions does not hold. It is what
// lets a framework address a handler from its own route table, for a template
// that sits outside the tree route discovery walks.
//
// The map wins, so configuring a resolver cannot retarget an action a
// discovered package already declares.
ServerActionResolver func(name string) (url string, ok bool)
// ServerActionAttr is the attribute the lowering writes. Empty uses
// [DefaultActionAttr]. A framework driving an existing client library points
// it at that library's vocabulary, such as hx-post.
ServerActionAttr string
// ReferenceHooks rewrite the static values of the attributes they are
// registered for, before analysis and before asset extraction, and declare
// the conversions those rewrites depend on. They are how a build converts a
// file the template points at, such as an image to a modern format or a
// TypeScript entry point to JavaScript, without this package holding a
// converter or a naming rule.
//
// A hook converts and returns the bytes, so it may decide the rewrite from
// how the conversion turned out. A hook declaring a CacheKey lets the caller
// reuse a stored result instead of converting again.
//
// Registering none leaves output byte-identical.
ReferenceHooks []ReferenceHook
// StrictReferenceHooks turns an expression-valued attribute at a registered
// element and attribute pair into a compile error. It is off by default,
// because a project may legitimately mix authored references with
// user-supplied ones; [Result.DynamicReferences] reports them either way.
StrictReferenceHooks bool
}
GenerateOptions controls the generated Go file and the static files extracted alongside it.
type HeadNode ¶ added in v0.1.15
type HeadNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Children []Node `json:"children,omitempty"`
}
HeadNode is a head element declared outside the document shell. Its children are hoisted into the merged document head instead of being emitted in place.
type HookError ¶ added in v0.3.1
HookError is a registration failure, which has no template position because nothing has been read yet.
type IdentifierExpr ¶
type IdentifierExpr = syntax.IdentifierExpr
type ImportDecl ¶
type ImportDecl = syntax.ImportDecl
type LiteralExpr ¶
type LiteralExpr = syntax.LiteralExpr
type MemberExpr ¶
type MemberExpr = syntax.MemberExpr
type PackageDecl ¶
type PackageDecl = syntax.PackageDecl
type ParseError ¶
type ParseError = syntax.ParseError
type ProducedFile ¶ added in v0.3.1
type ProducedFile struct {
// Name is the file name relative to the output root. It may carry directory
// separators and may not escape the root.
Name string
// MediaType is what the file should be served as, or empty.
MediaType string
Content []byte
}
ProducedFile is one file a conversion produced, ready for the caller to write.
type ReferenceHook ¶ added in v0.3.1
type ReferenceHook struct {
// Name identifies the hook in diagnostics and in the rewrite report.
Name string
// Element is the exact lowercase element name, such as img. A hyphenated
// name is out of scope: that space belongs to the builtin element
// whitelist.
Element string
// Attribute is the exact attribute name, such as src.
Attribute string
// Match reports whether this hook claims a static value. A nil Match claims
// every static value written at the pair.
Match func(value string) bool
// CacheKey names what a conversion of this value depends on, cheaply and
// without converting anything. A caller holding a store of previous results
// uses it to answer without calling Transform at all.
//
// A nil CacheKey means every build converts, which is correct and slow.
//
// The key is only as honest as what it names: an encoder upgrade that no
// Params string mentions serves stale bytes, and that is the caller's to
// state because only the caller knows what its converter depends on.
CacheKey func(ReferenceRequest) (ConversionInputs, error)
// Transform converts one claimed value and returns both the rewrite and the
// files it produced, so the rewrite may depend on how the conversion turned
// out.
//
// It is called once per distinct value in the template module being
// compiled, so a file referenced twenty times on one page is converted once.
// One module is the widest scope this package has, since it compiles them
// one at a time; a caller compiling several wraps the transform in its own
// memo, which is what the generator does.
//
// It must be a pure function of what it reads plus its own settings. The
// file and position on a request are for a diagnostic; deciding an output
// from them breaks that contract, and any cache built on CacheKey with it.
Transform func(ReferenceRequest) (ReferenceResult, error)
}
ReferenceHook matches one element and attribute pair and rewrites the static values written there.
Registration is per generate command, so a project registering none regenerates byte-identical output and pays nothing.
func (ReferenceHook) MarshalJSON ¶ added in v0.3.1
func (h ReferenceHook) MarshalJSON() ([]byte, error)
MarshalJSON gives a hook a stable identity for a caller that hashes its options to decide whether a run can be skipped.
A func value cannot be marshalled at all, so without this the whole options value becomes unhashable and registering one hook would silently turn the incremental skip off. What is emitted is the registration, not the behavior: a transform's behavior is covered by the hash of the generator executable that contains it, so adding, removing, or repointing a hook regenerates and recompiling the command does too.
type ReferenceRequest ¶ added in v0.3.1
type ReferenceRequest struct {
// Hook is the name of the hook that claimed the value.
Hook string
// Element and Attribute are the pair that matched.
Element string
Attribute string
// Value is the attribute value exactly as the template writes it.
Value string
// File and Pos locate the first occurrence, for a diagnostic the transform
// returns.
File string
Pos Position
}
ReferenceRequest is one claimed attribute occurrence handed to a transform.
type ReferenceResult ¶ added in v0.3.1
type ReferenceResult struct {
// Value replaces the attribute value. It is ignored when Skip is set.
Value string
// Skip leaves the attribute exactly as written.
Skip bool
// Reason explains a skip in the rewrite report.
Reason string
// Files are the files this conversion produced. They may outnumber the
// rewrite: a source map is produced and no attribute names it.
Files []ProducedFile
// Read lists files the transform read beyond the sources its CacheKey
// named, such as the modules a TypeScript entry point imports. What is
// named by neither is not hashed, and an edit to it will not regenerate.
Read []string
}
ReferenceResult is what a transform returns for one value.
Skip and Value are the two outcomes: a skip leaves the markup alone and says why, which is how a transform declines a conversion that was not worth it - an encode larger than its source, a format already at the target, a vector image. Declining is not an error and is not a silent no-op, and it is cached like any other outcome, so the losing encode runs once and never again.
type Result ¶ added in v0.2.9
type Result struct {
GoSource []byte
Assets []Asset
// Produced holds the files the hooks' conversions created, sorted by name.
// They may outnumber the rewrites, because a conversion may write a file no
// attribute names, and the caller writes them so they join the run's
// declared outputs rather than appearing behind it.
Produced []ProducedFile
// Rewrites reports what the hooks did, including what they declined and
// why. A build-time rewrite is invisible in the template, so the build is
// the only place it can be seen.
Rewrites []Rewrite
// ReadSet lists the files the transforms reported reading beyond the sources
// their cache keys named. What is named by neither is not hashed, and an
// edit to it will not regenerate.
ReadSet []string
// DynamicReferences are the attributes a hook was registered for whose
// value is an expression, and so could not be rewritten.
DynamicReferences []DynamicReference
}
Result is one compiled template module: the generated Go source and the static files requirement:static-asset-extraction pulled out of it.
func GenerateModule ¶ added in v0.2.9
func GenerateModule(filename string, source []byte, options GenerateOptions) (Result, error)
GenerateModule parses, validates, and compiles an HTML template module to Go plus its extracted stylesheet and script files.
Each component becomes an immutable render plan: an instruction list typed by its parameter struct, executed by the shared htmlbind coordinator. Generated code owns no response concerns, so it depends on neither net/http nor any content negotiation.
type Rewrite ¶ added in v0.3.1
type Rewrite struct {
Hook string
Element string
Attribute string
// From is the authored value and To is what replaced it. To equals From for
// a skip.
From string
To string
// Occurrences counts the attributes this one conversion rewrote. A transform
// call count is a count of distinct values, not of elements.
Occurrences int
// Skipped and Reason report a declined rewrite.
Skipped bool
Reason string
// Pos is the first occurrence in the template.
Pos Position
}
Rewrite records one distinct value a hook handled, for the build report. An author cannot see a build-time rewrite by reading the template, so the build is the only place it is visible.
type Signature ¶ added in v0.2.0
type Signature struct {
// Name is the declaration name as written in the template.
Name string
// Exported reports the export modifier.
Exported bool
// Parameters are in declaration order.
Parameters []SignatureParam
}
Signature is one declaration's contract stated in Go terms.
It exists so a caller that generates code around a template, such as a filesystem router, can read what a component takes without reimplementing the template type system. The Go types here are exactly the ones the generated parameter struct declares.
func Signatures ¶ added in v0.2.0
Signatures parses and analyzes a template module and returns the Go-typed signature of every component it declares, in declaration order.
It runs the same analysis Generate does, so a module that fails to compile fails here with the same diagnostic rather than yielding a partial answer.
type SignatureParam ¶ added in v0.2.0
type SignatureParam struct {
// Name is the parameter name as written in the template.
Name string
// GoType is the Go type of the generated parameter struct field. An async
// parameter is already wrapped, so it reads htmlbind.Pending[T].
GoType string
// TemplateType is the type as written in the template, kept for diagnostics
// that should quote the source rather than its lowering.
TemplateType string
// Async marks a parameter the caller settles through htmlbind.Pending.
Async bool
// Slot marks an html parameter, which a wrapper fills rather than a caller
// passing data.
Slot bool
}
SignatureParam is one declared parameter of a Signature.
type SlotNode ¶ added in v0.1.15
type SlotNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Name string `json:"name,omitempty"`
Required bool `json:"required,omitempty"`
Default []Node `json:"default,omitempty"`
}
SlotNode marks where a bound html parameter is inserted. Name is empty for the reserved children parameter. Default holds the content rendered when the bound argument is absent.
type TemplateDecl ¶
type TemplateDecl = syntax.TemplateDecl