Documentation
¶
Overview ¶
Package htmlbind parses typed HTML template sources into an AST.
Index ¶
- Constants
- func CleanOutputName(name string) (string, error)
- func FieldName(name string) string
- 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 ValidateContentHooks(hooks []ContentHook) error
- func ValidateReferenceHooks(hooks []ReferenceHook) error
- type ActionRef
- type AnalysisOption
- type Annotation
- type Asset
- type AssetKind
- type Attribute
- type AttributePart
- type AwaitBinding
- type AwaitNode
- type BinaryExpr
- type BindingProvider
- type Body
- type BuiltinElement
- type BuiltinShape
- type CSRFMode
- type CallExpr
- type CheckNode
- type ClientHandlerRef
- type ClientHandlerSet
- type CommentNode
- type CompileError
- type ComponentNode
- type ComponentScript
- type ConditionalExpr
- type ContentHook
- type ContentRequest
- type ContentResult
- type ConversionInputs
- type Declaration
- type DoctypeNode
- type DynamicReference
- type ElementNode
- type ElementParam
- type ElementPlacement
- type ElementProvider
- type EnumDecl
- type EnumMember
- type Expr
- type ExpressionNode
- type ExternalDecl
- type Field
- type ForNode
- type GenerateOptions
- type HeadEntry
- type HeadNode
- type HookError
- type IdentifierExpr
- type IfNode
- type ImplicitBinding
- type ImportDecl
- type IndexExpr
- type LiteralExpr
- type MemberExpr
- type MessageArg
- type MessageExpr
- type MessageRef
- type MessageSymbol
- type Module
- type Node
- type PackageDecl
- type Parameter
- type ParseError
- type PassthroughElement
- 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
- type ValBinding
- type ValNode
Constants ¶
const ClientHandlerPrefix = "on-"
ClientHandlerPrefix marks an attribute naming a function the component's script block produced, as in on-click="increment".
The hyphen is what makes the namespace free: rule:event-attribute-context excludes a hyphenated on- name from the event handler roster, so onclick keeps meaning inline JavaScript and this spelling means something else entirely. Taking the unhyphenated form instead would silently reinterpret a shipped feature, because a bare identifier is a valid expression statement.
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 DefaultActionSelectorField = "_action"
DefaultActionSelectorField is the hidden field a form carries to say which handler a native submit is for. The form posts to its own page, so the URL no longer identifies the handler and this does.
const DefaultCSRFFieldName = "_csrf"
DefaultCSRFFieldName is the hidden field an unsafe form carries. It is a generation-time name rather than a render-time one, so the whole tag but its value folds into static bytes.
const DefaultClientHandlerAttr = "data-tb-on"
DefaultClientHandlerAttr is the attribute the lowering writes when GenerateOptions.ClientHandlerAttr is empty.
The authored attributes are lowered into this one rather than left in place because CSS has no attribute-name prefix match: finding on-anything means walking every element on every mount and every swap, where one marker is a single indexed query. Leaving them would also claim the namespace rule:event-attribute-context assigns to custom elements.
const DefaultComponentParameterAttr = "data-tb-props"
DefaultComponentParameterAttr is the attribute a component's emitted parameters are written to when GenerateOptions.ComponentParameterAttr is empty.
It rides the same root element as the declaration marker of requirement:scoped-script-declaration, which is why a component declaring a script block already has to render exactly one root: the invariant this needs exists for that marker and is reused rather than added.
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 FieldName ¶ added in v0.5.11
FieldName returns the Go struct field a declared template name becomes: a component parameter, a record field, or a binding.
It is exported because a caller building one of those structs has to spell the field the same way. Route generation does, and it has its own initialism-aware conversion for the structs it emits itself, which is right there and wrong here — the two are different structs and only one of them is this compiler's.
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 ValidateContentHooks ¶ added in v0.5.5
func ValidateContentHooks(hooks []ContentHook) error
ValidateContentHooks reports a registration this module cannot act on. It is checked once per generate command so a mistake names whoever wrote the command rather than the first template that happens to write the marker.
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
func ActionRefs(filename string, source []byte, options ...AnalysisOption) ([]ActionRef, error)
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 AnalysisOption ¶ added in v0.5.13
type AnalysisOption func(*analysisOptions)
AnalysisOption configures an analysis-only entry point such as Signatures, ComponentScripts or ActionRefs.
Those read a template without generating one, and a template reading an implicit binding cannot be analyzed without knowing the binding's name: an undeclared name is an unknown identifier, which is what the check exists for. The symbol table needs no equivalent, because a message reference is checked against it at generation rather than during analysis.
func WithAnalysisBindings ¶ added in v0.5.13
func WithAnalysisBindings(bindings []ImplicitBinding) AnalysisOption
WithAnalysisBindings supplies the same list GenerateOptions.ImplicitBindings carries, so an analysis entry point reads what generation will compile.
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
// Owner names the component whose requirement:component-script-block
// declared this file, and is empty for everything else. An empty owner is
// document lifetime: the file evaluates once and is never released. A named
// one binds the file to that component's live instances, which is the link
// requirement:scoped-script-declaration publishes to a caller's runtime.
Owner 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 AttributePart struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Context string `json:"context,omitempty"`
Text string `json:"text,omitempty"`
Expression Expr `json:"expression,omitempty"`
// Start and End are file-global byte offsets of the source this part came
// from, on the same terms as TextNode.Start: excluded from the serialized
// AST, and a range of source rather than of content.
Start int `json:"-"`
End int `json:"-"`
}
type AwaitBinding ¶ added in v0.1.16
type AwaitBinding = syntax.AwaitBinding
type BinaryExpr ¶
type BinaryExpr = syntax.BinaryExpr
type BindingProvider ¶ added in v0.5.13
type BindingProvider struct {
// Package is the import path holding the function. Empty means the
// generated package's own.
Package string
// Alias overrides the import name. Empty uses the last path segment.
Alias string
// Name is the function.
Name string
// Result names the Go type the function returns, qualified the same way.
// Empty means string.
//
// A binding returning something other than a string cannot be interpolated
// into markup — there is no escaping rule for a type this module has never
// seen — so it is usable only as GenerateOptions.MessageContextBinding.
// Reading one in a template is a generation error naming the binding.
Result string
}
BindingProvider names the Go function behind an implicit binding, on the same terms as ElementProvider.
type BuiltinElement ¶ added in v0.3.3
type BuiltinElement struct {
// Name is the bare kebab-case element name an author writes.
Name string
// Params are the declared attributes, in the order a diagnostic lists them.
Params []ElementParam
// Context is the rule:template-context-safety insertion context this element
// may appear in. Empty means html:child.
Context string
// Placement is the region that owns it. A head-only element written in the
// body is a generation error rather than a page that half works.
Placement ElementPlacement
// Vary names the request properties this element's output depends on, such
// as a cookie its provider reads.
//
// It is declared rather than derived, because only the implementation knows
// what its provider reads. An undeclared axis is an invisible dependency: a
// caller cannot build a Vary header for it and a shared cache cannot key on
// it, and neither can find out by looking at the template.
Vary []string
// Assets are the static files this element requires. They join the required
// set of every component that writes it, and their reference tags join its
// head.
Assets []Asset
// Shape is how the output is produced. Empty means BuiltinMarkup.
Shape BuiltinShape
// Markup is the fixed output template. A hole is written {{.Name}} and names
// either a declared parameter or a field of the provider's result; {{.}} is
// the whole provider result, for a provider returning a bare value.
//
// Each hole is escaped for its position, and generation refuses a hole
// anywhere but element text and an attribute value. That is what makes the
// output unable to inject markup even if a provider returns hostile bytes.
Markup string
// Provider supplies the per-request holes. Nil means the element has none,
// and then it costs nothing at render time: with no expression parameter
// either, the whole thing folds into static bytes.
Provider *ElementProvider
}
BuiltinElement is one framework element the generator rewrites.
type BuiltinShape ¶ added in v0.3.3
type BuiltinShape string
BuiltinShape names how a builtin element produces its output.
const BuiltinMarkup BuiltinShape = "markup"
BuiltinMarkup lowers a fixed markup template with named holes. It is the only shape this milestone ships.
The opaque shape — a provider returning a trusted value or a fragment, for output whose structure varies rather than only its values — is designed and not built. Its cost is that the trust assertion moves into framework code and the generator can no longer verify the emitted structure, which is why the verifiable shape went first.
type CSRFMode ¶ added in v0.3.3
type CSRFMode string
CSRFMode says whether generated forms carry a token.
const ( // CSRFAuto is the zero value: every unsafe form gets the hidden field, and // a component reaching one becomes per-request. CSRFAuto CSRFMode = "" // CSRFOff emits no field and marks nothing per-request. // // It is for a deployment that has settled on Origin and Fetch Metadata // checks alone. Those are genuinely close to sufficient for a single origin, // and turning the token off is what gives such a deployment its cacheable // form-bearing components back. // // What it does not turn off is the origin checking itself, which this module // never performs: that is a check on an inbound request before any render, so // it belongs to middleware, and a deployment declines it by not wrapping its // handlers. CSRFOff CSRFMode = "off" )
type ClientHandlerRef ¶ added in v0.5.8
type ClientHandlerRef struct {
// Component is the declaration the reference appears in.
Component string
// Event is the name after the prefix, such as click.
Event string
// Handler is the name the attribute's value gave.
Handler string
// Element is the element carrying the attribute.
Element string
// Pos is the attribute position, for a diagnostic that can quote the source.
Pos Position
}
ClientHandlerRef is one on-prefixed attribute found in a template.
type ClientHandlerSet ¶ added in v0.5.8
type ClientHandlerSet struct {
// Resolved names the handlers the block exposes.
Resolved []string
// Unresolved maps a name the template referenced to why the caller refused
// it. Reporting a refusal here rather than by omission is required: an
// omission cannot be told from a map that was never populated, and the module
// would report every name of a mis-parsed block as unknown.
Unresolved map[string]string
}
ClientHandlerSet is what one component's script block exposes, as the caller resolved it.
The module reads no JavaScript. The caller parses the block reported by ComponentScripts and answers with this, which is the same arrangement GenerateOptions.ServerActions already uses for a URL the compiler cannot compute.
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 ComponentScript ¶ added in v0.5.8
type ComponentScript struct {
// Component is the declaration name.
Component string
// Script is the block's content as authored, with no reinterpretation. It is
// reported here rather than read from the extracted asset because that file
// exists only after the compile that needs this answer, and because finding
// the block in the template source means reimplementing the parser's own
// raw-text boundary.
Script string
// Pos is the block's position, for a diagnostic the caller wants to anchor.
Pos Position
// Handlers are the client handler names this component's markup referenced,
// deduplicated and in source order. A caller resolves these against what the
// block exports and answers through GenerateOptions.ClientHandlers.
Handlers []string
// Parameters names the component's declared parameters, in declaration
// order, so a caller choosing which of them to emit picks from the real set
// rather than from what it believes the signature to be.
Parameters []string
}
ComponentScript is one component declaring a script block, reported so a caller can read the block without parsing the template itself.
It is the seam GenerateOptions.ClientHandlers and GenerateOptions.ComponentParameters are answered from. The module reads no JavaScript: it reports the bytes it already holds, the caller interprets them, and the answer comes back as a compile option, which is the arrangement GenerateOptions.ServerActions already uses for a URL the compiler cannot compute.
func ComponentScripts ¶ added in v0.5.8
func ComponentScripts(filename string, source []byte, options ...AnalysisOption) ([]ComponentScript, error)
ComponentScripts parses and analyzes a template module and returns every component declaring a script block.
Like ActionRefs and 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.
It is called before the caller has resolved anything, so no compile option is taken: a component with no entry in GenerateOptions.ClientHandlers is unchecked, which is what lets this pass run first.
type ConditionalExpr ¶
type ConditionalExpr = syntax.ConditionalExpr
type ContentHook ¶ added in v0.5.5
type ContentHook struct {
// Name identifies the hook in diagnostics.
Name string
// Lang is the exact lang attribute value this hook claims, such as ts.
//
// The set is open: this module validates that a written marker was
// registered and never that it names a language it recognizes.
Lang string
// Extension is what the produced file is written as, without a dot. Empty
// keeps js, which is what a block compiles to unless the caller says
// otherwise.
Extension string
// Transform compiles one block's content.
//
// It is called once per distinct content in the template module being
// compiled, and must be a pure function of what it reads plus its own
// settings, exactly as [ReferenceHook.Transform] must be. The component,
// file, and position on a request are for a diagnostic; deciding an output
// from them breaks that contract.
//
// It compiles and does not bundle. decision:share-by-module-url records
// why: an import specifier left alone is one URL in the browser's module
// map and is evaluated once, so bundling is what would duplicate a shared
// module rather than what avoids it.
Transform func(ContentRequest) (ContentResult, error)
}
ContentHook compiles the body of a component script block whose lang attribute names a language this module does not know.
It is the content-side counterpart of ReferenceHook: that one claims an attribute naming an authored file, and this one claims a block whose bytes are already in hand. The division is the same one decision:transform-seam-ownership draws for the other seam. This module decides which block is claimed, when the transform runs, what its identity is, and what regenerates it; the caller owns the compiler, the settings, and the name the output is written under.
Registration is per generate command, so a project registering none regenerates byte-identical output and pays nothing. No compiler enters this module's dependencies, which is the rule concept:build-time-asset-transforms already states and requirement:browser-runtime-asset-ownership repeats.
type ContentRequest ¶ added in v0.5.5
type ContentRequest struct {
// Hook is the name of the hook that claimed the block.
Hook string
// Lang is the marker the block wrote.
Lang string
// Content is the block's authored body, exactly as written.
Content string
// Component names the component that declared the block.
Component string
// Dir is the directory of the template that declared the block. It is what
// a transform resolving an import specifier resolves against, because the
// template's own location is the only one an author means.
Dir string
// File and Pos locate the block, for a diagnostic the transform returns.
File string
Pos Position
}
ContentRequest is one claimed block handed to a transform.
type ContentResult ¶ added in v0.5.5
type ContentResult struct {
// Content replaces the block's body.
Content string
// Extension overrides the hook's, without a dot. Empty keeps it.
Extension string
// Read lists the files the transform read, so an edit to one regenerates.
// What is named here is what stays honest across builds; what is left out
// is not hashed and will serve a stale result.
Read []string
}
ContentResult is what a transform returns for one block.
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 ElementParam ¶ added in v0.3.3
type ElementParam struct {
// Name is the attribute an author writes, in kebab-case.
Name string
// Type is the template type name: string, int, bool, and the rest of the
// core types.
Type string
// Required refuses a call site that leaves the attribute unset.
Required bool
}
ElementParam is one declared attribute of a builtin element. Its expression is type-checked at the call site exactly as an ordinary element's attribute is.
type ElementPlacement ¶ added in v0.3.3
type ElementPlacement string
ElementPlacement says which region of a document an element belongs in.
const ( // PlaceEither is the zero value: the element may appear in the head or in // the body. PlaceEither ElementPlacement = "" // PlaceHead restricts an element to a head contribution. PlaceHead ElementPlacement = "head" // PlaceBody restricts an element to the document body. PlaceBody ElementPlacement = "body" )
type ElementProvider ¶ added in v0.3.3
type ElementProvider struct {
// Package is the import path of the package holding the function. Empty
// means the generated package's own.
Package string
// Alias overrides the import name. Empty uses the last path segment.
Alias string
// Name is the function.
Name string
// Result names its first result type, qualified the same way. It is needed
// because a hole closure has to be written down, and Go infers a call's type
// arguments but never a function literal's parameter types.
//
// For a single-hole element whose provider returns a bare value rather than
// a struct, this is that value's type and the hole is written {{.}}.
Result string
}
ElementProvider names the Go function supplying a builtin element's per-request values.
The signature is func(context.Context) (V, error). Nothing here checks it: as with a context-taking external, the caller reads its own Go sources and the Go compiler is the thing that rejects a mismatch. A generator that resolved Go symbols would have to load the target package, which is the dependency this package does not take.
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
// ErrorExternals names the synchronous external functions whose Go
// implementation returns a trailing error. A non-nil error from one of them
// fails the render, so such a function may only be called as the whole value
// of a requirement:template-value-binding binding, where the failure has a
// place to go and a name in the source.
//
// Discovered the same way as ContextExternals, from the package's Go
// sources, so the template declaration is unchanged either way.
ErrorExternals map[string]bool
// ImplicitBindings are the names the embedder puts in every template's
// scope, so an application does not thread a framework value through every
// component and every layout in a chain.
//
// A project declaring none generates byte-identical Go, and one declaring
// bindings no template reads imports nothing for them.
ImplicitBindings []ImplicitBinding
// Messages maps a resolved message id to the Go symbol it calls. A
// reference with no entry here is a compile error, so a template naming a
// message nobody resolved never silently emits an empty string; the same
// rule ServerActions follows, and [MessageRefs] reports what needs
// resolving.
Messages map[string]MessageSymbol
// MessageContextBinding names the ImplicitBindings entry whose value is
// written as the leading argument of every message call, for a catalog
// whose generated functions take one. Empty writes no leading argument.
//
// It is a binding rather than a free expression because a cached component
// has to key on what its output depends on, and a message reference names
// nothing the reach walk could find. Routing it through a binding makes the
// dependency structural: `{t title}` reads the binding, so the cache key,
// the vary axis and the context-carrying instruction all follow with no
// rule about messages anywhere. See
// .knowledge decision:implicit-binding-cache-identity.
//
// The binding's provider may return a named type through
// BindingProvider.Result, which is what lets a catalog take its own locale
// type; such a binding cannot also be written into markup.
MessageContextBinding string
// 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)
// ServerActionRefusals maps a handler name the caller resolved and then
// declined to the reason, which a template naming it is refused with.
//
// A refusal is stated rather than left as an absence, because an absent
// name is indistinguishable from one nobody registered: the diagnostic
// would name a missing registration where the truth is that the handler
// exists and this is not how it is reached. It is the shape ClientHandlers
// already takes for an unresolved name, and for the same reason.
//
// The refusals win over the map and the resolver alike, since a name is
// declined whether or not something could have answered for it.
ServerActionRefusals map[string]string
// 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
// ServerActionSelectors maps each handler name to the opaque selector a
// native form submit carries, which is the tail of that handler's endpoint
// URL. Supplying it is what makes a form work with no browser runtime: the
// form posts to its own page and the generated dispatcher branches on this
// value.
//
// A name present in ServerActions and absent here lowers to the URL
// attribute alone, which is the shape a framework resolving an address from
// its own route table gets, since that framework owns the route the form
// would post to.
ServerActionSelectors map[string]string
// ServerActionSelectorField renames the hidden field carrying the selector.
// Empty uses [DefaultActionSelectorField]. It has to agree with whatever
// dispatcher reads it back out.
ServerActionSelectorField string
// ClientHandlers maps each component declaration to what its script block
// exposes, so an on-prefixed attribute naming a function that block does not
// provide fails generation at the attribute rather than at runtime.
//
// The caller reads the block [ComponentScripts] reported and answers here.
// This module reads no JavaScript, so a component absent from the map is
// unchecked: every name it references is accepted and lowered.
ClientHandlers map[string]ClientHandlerSet
// ClientHandlerAttr is the attribute the lowering writes. Empty uses
// [DefaultClientHandlerAttr].
ClientHandlerAttr string
// ComponentParameters names, per component declaration, the parameters to
// emit as JSON onto that component's root element. It exists because a script
// block is extracted to one content-hashed file shared by every instance and
// every render, so there is nothing per-render to interpolate into it, and
// reading a rendered attribute back loses the type.
//
// The caller chooses the set, which is what keeps this opt-in: an emitted
// parameter is in the DOM, where it is readable and editable by the client,
// so a server-authoritative value crosses only because someone named it.
//
// A component with no entry, or an empty one, emits nothing.
ComponentParameters map[string][]string
// ComponentParameterAttr is the attribute that object is written to. Empty
// uses [DefaultComponentParameterAttr].
ComponentParameterAttr 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
// ContentHooks compile the component script blocks whose lang attribute
// they claim, so a block written in TypeScript reaches the browser as
// JavaScript without a compiler entering this module.
//
// Registering none is the ordinary case: a block with no lang marker is
// written exactly as authored, and a marker naming no registered hook is a
// generation error rather than a silent passthrough.
ContentHooks []ContentHook
// CSRFMode turns the automatic CSRF field off. Empty is [CSRFAuto], which
// puts the hidden field in every unsafe form.
CSRFMode CSRFMode
// CSRFFieldName renames the hidden field. Empty uses
// [DefaultCSRFFieldName]. It has to agree with whatever middleware reads the
// token back out.
CSRFFieldName string
// BuiltinElements are the hyphenated elements a framework contributes, each
// rewritten at generation time into plan steps. See [BuiltinElement].
BuiltinElements []BuiltinElement
// PassthroughElements are the hyphenated elements an application uses and
// this package emits verbatim: its Web Components, named exactly or by a
// prefix glob such as "sl-*".
//
// Registering neither leaves the hyphenated space closed and empty, so every
// hyphenated element in a template is a generation error naming the file,
// line, and column. That is the one behavior change for an existing project,
// and it is the point: an unrecognized hyphenated element emitted unchanged
// renders nothing and reports nothing.
PassthroughElements []PassthroughElement
// 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
// LineDirectives maps each emitted instruction back to the template line
// that produced it, so a type error in a template expression names the
// .tb.html file rather than the generated Go one.
//
// It reaches compile time only. Rendering walks the instruction list inside
// the shared coordinator, so a failing render's stack frame is in this
// package and no directive on generated code can move it; see
// requirement:render-error-positions.
//
// It is off by default. Turning it on changes the bytes of every generated
// file carrying a component, and a covered test run reports lines that do
// not exist in the file it names, per rule:line-directive-emission.
LineDirectives bool
// OutputName is the base name of the Go file this output becomes. A mapped
// span ends with a directive naming that file and the line the reader is on,
// and neither is known until the bytes are final.
//
// Leaving it empty returns the output with those directives unresolved, for
// a caller that concatenates several results and resolves the combined file
// itself with generator.ResolveTemplatePositions. A caller writing this
// result as a whole file must name it here.
OutputName string
}
GenerateOptions controls the generated Go file and the static files extracted alongside it.
type HeadEntry ¶ added in v0.3.5
type HeadEntry struct {
// Element is link, script, or style. Nothing else is accepted: a hook
// naming a title, a base, or a meta charset would be rewriting the document
// rather than loading what it produced.
Element string
// Attributes are the attribute values, keyed by name. A value is written as
// a static attribute and escaped for its own position, so a transform hands
// over a plain URL and never a pre-escaped one.
Attributes map[string]string
}
HeadEntry is one tag a conversion needs in the head of the component that referenced 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 ImplicitBinding ¶ added in v0.5.13
type ImplicitBinding struct {
// Name is what an author writes, as a bare identifier. It must not collide
// with a component parameter; one that does is a generation error naming
// the collision, because a silently shadowed framework value renders
// whatever the parameter holds.
Name string
// Provider names the Go function returning the value. It is called with the
// render context and must return a string.
Provider BindingProvider
// PathSegment marks a binding that stands for one URL path segment.
//
// It is the only kind permitted into a URL attribute despite not being
// url-typed, and the only one whose empty value collapses the separator
// before it. Both are scoped to the kind rather than to emptiness: an
// ordinary empty interpolation must keep rendering nothing, or a future
// url-typed value would acquire behavior it never asked for.
//
// The value is percent-encoded at emission, because a binding is
// embedder-supplied but usually request-derived — a language resolved from
// Accept-Language or from a path prefix is attacker-influenced by
// definition. See .knowledge requirement:embedder-implicit-bindings.
PathSegment bool
// VaryAxis is the response header name this binding's value depends on,
// empty for one an application recovers from the URL.
//
// The axis is the embedder's to name because only it knows how the value
// was resolved: an application carrying it in a path prefix declares none,
// since two languages are already two URLs, while one negotiating from a
// request header must declare that header or nothing downstream can cache
// the page correctly. See .knowledge decision:implicit-binding-cache-identity.
VaryAxis string
}
ImplicitBinding is a name the embedder puts in every template's scope, so an application does not thread a framework value through every component and every layout in a chain.
The value is produced by a Go function taking the render context, which is the same shape requirement:render-value-provider already uses for a builtin element. That keeps decision:reflection-free intact: nothing is looked up by string at run time, and a binding a template never writes emits nothing.
This module learns no meaning from a binding. The names, the values and what they stand for are the embedder's, per .knowledge requirement:embedder-implicit-bindings.
type ImportDecl ¶
type ImportDecl = syntax.ImportDecl
type LiteralExpr ¶
type LiteralExpr = syntax.LiteralExpr
type MemberExpr ¶
type MemberExpr = syntax.MemberExpr
type MessageArg ¶ added in v0.5.13
type MessageArg = syntax.MessageArg
type MessageExpr ¶ added in v0.5.13
type MessageExpr = syntax.MessageExpr
type MessageRef ¶ added in v0.5.13
type MessageRef struct {
// Scope is the file's declared `messages` name, empty when none was
// declared.
Scope string
// Written is the id as the author spelled it, and ID is what it resolves
// to. They differ only for an unqualified reference.
Written string
ID string
// Args names the arguments the reference supplies, in source order.
Args []string
Pos Position
}
MessageRef is one reference a template makes, reported so a caller can resolve what it needs before generation and reconcile a template against a catalog. It is the reference half of .knowledge requirement:template-parse-introspection.
func MessageRefs ¶ added in v0.5.13
func MessageRefs(filename string, source []byte) ([]MessageRef, error)
MessageRefs reports every message reference in a source file, with the file's declared scope. It runs the parser and the resolution rule and nothing else, so it answers before any symbol table exists — which is the order a caller needs, since the table is what this report is used to build.
A reference that cannot be resolved, because the file declares no scope, is reported with an empty ID rather than failing, so a caller reconciling a tree of templates sees the whole picture instead of the first mistake.
type MessageSymbol ¶ added in v0.5.13
type MessageSymbol struct {
// Package is the import path holding the function. Empty means the
// generated package's own, like ElementProvider.Package.
Package string
// Alias overrides the import name. Empty uses the last path segment.
Alias string
// Name is the function.
Name string
// Params names the function's parameters in declaration order, excluding
// any leading argument GenerateOptions.MessageContext supplies. A reference
// must name exactly these, which is also what fixes the call's argument
// order: a reference writes its arguments by name and the emitter puts them
// back in the order the function declares.
Params []string
}
MessageSymbol names the Go function one resolved message id calls.
The mapping arrives as data rather than being computed from the id, because an id is not a Go identifier: it may carry hyphens, and how a catalog turns a slug into a symbol is the catalog owner's policy. See .knowledge requirement:message-symbol-resolution id_to_symbol_is_a_supplied_table.
type PackageDecl ¶
type PackageDecl = syntax.PackageDecl
type ParseError ¶
type ParseError = syntax.ParseError
type PassthroughElement ¶ added in v0.3.3
type PassthroughElement struct {
Name string
}
PassthroughElement is a hyphenated element emitted verbatim.
Name is either an exact element name or a prefix glob such as "sl-*", so a component library is declared once rather than per element.
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.
//
// It is called from one goroutine unless the caller asks for more: the
// generator converts ahead of its compile when configured to, and calls
// several transforms at once when it does. Being pure is necessary and not
// sufficient for that - a transform holding a shared scratch buffer is pure
// by the definition above and unsafe by this one - which is why the caller
// opts in rather than getting concurrency by default.
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.
func CollectReferences ¶ added in v0.3.5
func CollectReferences(filename string, source []byte, hooks []ReferenceHook) ([]ReferenceRequest, error)
CollectReferences reports every distinct value the registered hooks would claim in one module, without calling a single transform.
It exists so a caller can convert ahead of compiling instead of inside it. A conversion is seconds and a compile is microseconds, and the compile is sequential because each rewritten value has to fold into the module being compiled; converting what the compile will ask for before it asks takes that cost off a single goroutine without moving the decision, which still belongs to the transform and still depends on the bytes it produced.
The walk is the one the rewrite uses, so a value this misses is a value the rewrite would have missed too. Nothing is validated and nothing is reported: an overlap, a dynamic reference, and a strict-mode failure all belong to the compiling pass, which reports them at the same position in the same order whether or not this ran.
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
// Head declares tags the component's head needs because of this conversion.
//
// A rewrite replaces one attribute on an element that already exists, so a
// conversion producing a file nothing names has no way to get it loaded: a
// TypeScript entry point importing a CSS module emits a companion
// stylesheet, and no rewritten src can introduce its link. That is what
// this is for.
//
// An entry joins the contributions of the component whose template held the
// matched attribute, after everything that component's author wrote, so a
// component rendered on one page in forty contributes to that page only. A
// skip contributes nothing, because a conversion that declined produced no
// file to load.
Head []HeadEntry
}
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
// ActionRefs are the server-action references this module makes, in source
// order, the same value [ActionRefs] returns. It is reported here so a caller
// that already compiled need not parse a second time to learn which element
// kind carries an action, which is what decides whether the handler needs a
// native submit channel at all.
ActionRefs []ActionRef
// ComponentScripts are the components declaring a script block, the same
// value [ComponentScripts] returns. It is reported here so a caller that
// already compiled need not parse a second time.
ComponentScripts []ComponentScript
}
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
func Signatures(filename string, source []byte, options ...AnalysisOption) ([]Signature, error)
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
type TextNode ¶
type TextNode struct {
Kind string `json:"kind"`
Pos Position `json:"pos"`
Text string `json:"text"`
// Start and End are file-global byte offsets of the source this text came
// from, for a tool that rewrites a template in place. They are excluded
// from the serialized AST because they are a tool-facing detail rather than
// part of the parse's published shape, and because adding them there would
// move every parser fixture.
//
// The range is source rather than content: an escaped brace contributes one
// character to Text and two to the range, so a rewriter replacing the range
// replaces the escape as well, which is what an extractor wants. See
// .knowledge requirement:template-parse-introspection.
Start int `json:"-"`
End int `json:"-"`
}
type ValBinding ¶ added in v0.5.10
type ValBinding = syntax.ValBinding