Documentation
¶
Overview ¶
Package compiler implements the Liquid AOT template compiler. It pairs .lsx templates with their component source files by filename convention (user_card.lsx ↔ user_card.go defining UserCard) and emits html/template markup as *_gen.go files beside the source.
Index ¶
- Constants
- func Manifest(ctx context.Context, dir string) (*ManifestGraph, []Diagnostic, error)
- func PairedStructName(lsxPath string) string
- func ParseGoFor(expr string) (loopVar, list string, ok bool)
- type ActionContract
- type Code
- type Decl
- type Diagnostic
- type DirectiveUse
- type ExprRef
- type Facts
- func (f *Facts) ActionContracts(structName string, actionNames []string) map[string]ActionContract
- func (f *Facts) Broken() bool
- func (f *Facts) Component(structName string) []Member
- func (f *Facts) SelectorDecls() []SelectorDecl
- func (f *Facts) StructDecl(structName string) (Decl, bool)
- func (f *Facts) Vet(lsxPath, structName string, sa *SourceAnalysis) []Diagnostic
- func (f *Facts) VetSubscriptions() []Diagnostic
- type ManifestAction
- type ManifestComponent
- type ManifestField
- type ManifestGraph
- type Member
- type SelectorDecl
- type Severity
- type SourceAnalysis
Constants ¶
const ( KindChildTag = kindChildTag KindInput = kindInput )
The two DirectiveUse kinds the tag scanner records dynamically rather than from the directive registry: a child-selector element and its [input] bindings.
const ManifestVersion = "v1"
ManifestVersion is the schema code of the manifest envelope. It changes only when the shape of the graph changes incompatibly; agents match against it. The manifest format is committed from 1.0 (D24, D26): "v1" is the stable, backward-compatible schema. (1.0.0 emitted "v0" before the format was committed; the code was corrected to "v1" in the following release.)
Variables ¶
This section is empty.
Functions ¶
func Manifest ¶
func Manifest(ctx context.Context, dir string) (*ManifestGraph, []Diagnostic, error)
Manifest builds the component graph for dir. It first runs the same diagnostic gate as vet: if the directory does not compile, it returns a nil graph and the D13 diagnostics that explain why — no manifest is emitted for a broken package. On a clean gate it returns the graph and no error; the diagnostics slice may still carry advisory warnings (D29), which do not suppress the graph.
func PairedStructName ¶
PairedStructName returns the struct name the .lsx filename convention pairs a template with (user_card.lsx → UserCard).
func ParseGoFor ¶
ParseGoFor splits a *goFor expression against its grammar "let <ident> of <FieldPath>", for language-server features that need the two halves separately.
Types ¶
type ActionContract ¶
type ActionContract struct {
// Guard reports a <Name>Guard convention method — the boundary predicate
// the seam runs before the handler.
Guard bool
// Typed reports a typed payload parameter on the handler itself (#105,
// ADR-0004): the payload type is named to the compiler without a guard, so
// its closed-domain fields enforce and its Validate runs at the seam.
Typed bool
// Domains maps a payload field (named as declared) to the enumerated
// const-set values the seam admits, sorted and de-duplicated; empty when no
// field is closed-domain.
Domains map[string][]string
}
ActionContract is the compiled D30 payload contract for one action: whether it declares a boundary guard, whether its handler takes a typed payload, and the closed-domain constraints the dispatch seam enforces on that payload.
type Code ¶
type Code string
Code is a stable machine-matchable diagnostic identifier (D13). Codes are part of the agent-facing contract: renaming one is a breaking change.
const ( // CodeMalformedTemplate reports .lsx syntax the compiler cannot parse, // such as an interpolation opened with {{ and never closed. CodeMalformedTemplate Code = "LSX001" // CodeMissingPairedSource reports a .lsx file with no paired .go source // file next to it. CodeMissingPairedSource Code = "LSX002" // CodeMissingPairedStruct reports a paired .go file that does not define // the struct the filename convention requires. CodeMissingPairedStruct Code = "LSX003" // CodeUnknownReference reports a template expression referencing a field // or method that does not exist on the paired struct. CodeUnknownReference Code = "LSX004" // CodeMalformedDirective reports a structural directive whose expression // does not match the directive's grammar. CodeMalformedDirective Code = "LSX005" // CodeConflictingDirectives reports an element carrying more than one // structural directive. CodeConflictingDirectives Code = "LSX006" // CodeBrokenPairedPackage reports a paired Go package that fails // type-checking; the position points into the offending Go source. CodeBrokenPairedPackage Code = "LSX007" // CodeInvalidHandler reports an event binding whose target exists but is // not a dispatchable handler method (D11). CodeInvalidHandler Code = "LSX008" // CodeMissingHydroField reports a [hydroId] declaration on a component // whose struct lacks the HydroID string field the framework fills. CodeMissingHydroField Code = "LSX009" // CodeMissingHydroRoot reports an event binding in a template that never // declares a [hydroId] patch boundary. CodeMissingHydroRoot Code = "LSX010" // CodeMissingCSRFField reports a template with a <form> on a component // whose struct lacks the CSRFToken string field the framework fills (D15). CodeMissingCSRFField Code = "LSX011" // CodeUnknownChildSelector reports a child-selector element whose tag no // component in the package declares (D14). CodeUnknownChildSelector Code = "LSX012" // CodeBadInputBinding reports an [input] binding that cannot copy at // render: a malformed expression, a child field that does not exist, or // a parent value not assignable to the child field. CodeBadInputBinding Code = "LSX013" // CodeChildBindingUnsupported reports an event binding or [hydroId] on a // child-selector element, which the child's render replaces wholesale — // such bindings belong inside the child's own template. CodeChildBindingUnsupported Code = "LSX014" // CodeMisplacedDefer reports *liquidDefer off a child-selector element: // only a nested component occurrence can render deferred (D14). CodeMisplacedDefer Code = "LSX015" // CodeMissingDeferHydroField reports *liquidDefer on a component whose // struct lacks the HydroID string field — without it the completion // patch has no boundary to swap at. CodeMissingDeferHydroField Code = "LSX016" // CodeUnmanagedSubscription reports a direct Subscribe call on a Liquid // observable (BehaviorSubject, Derived, or the Observable interface) in // component code, whose lifecycle is not owned by the framework (D25/D29). // A bare call that discards its cancel is a provable leak (error); a call // whose cancel is captured but not session-bound is a warning. The managed // path is liquid.Observe within Subscriptions(). Suppress a deliberate // direct subscription with a //liquid:allow-subscribe comment. CodeUnmanagedSubscription Code = "LSX017" // CodeUnguardedAction reports an action that takes a client payload // (func(e liquid.Event)) but declares no <Name>Guard boundary predicate and // no closed-domain payload field, so nothing constrains its payload values // at the dispatch seam (D30). A non-fatal warning (D13): the value axis of // least privilege is where an agent-written handler is most likely to leave // a gap, so the framework flags it without failing the build. CodeUnguardedAction Code = "LSX018" )
The diagnostic codes liquid build and liquid vet can emit.
type Decl ¶
type Decl struct {
// File, Line and Col (1-based line and byte column) locate the
// declaration in Go source.
File string
Line int
Col int
// Doc is the declaration's doc comment, "" when undocumented.
Doc string
}
Decl locates one Go declaration, as a go-to-definition target.
type Diagnostic ¶
type Diagnostic struct {
File string `json:"file"`
Line int `json:"line"`
Col int `json:"col"`
Severity Severity `json:"severity"`
Code Code `json:"code"`
Message string `json:"message"`
Suggestion string `json:"suggestion"`
}
Diagnostic is one structured compiler finding: the literal contract an agent parses to self-repair (D13), so the field set and JSON names are the API. Line and Col are 1-based; Col counts bytes.
func Build ¶
func Build(ctx context.Context, dir string) ([]Diagnostic, error)
Build compiles every .lsx file under dir, writing a <name>_gen.go file beside each one containing the paired component's Template method. Problems in the input the user can fix come back as diagnostics (no generated file is written for a .lsx with an error diagnostic); the error covers mechanical failures such as unreadable directories.
func PairingDiagnostic ¶
func PairingDiagnostic(lsxPath string) *Diagnostic
PairingDiagnostic reports the LSX002 for a .lsx file whose paired .go source does not exist, or nil when the pairing is in place.
type DirectiveUse ¶
type DirectiveUse struct {
// Name is the canonical spelling — *goIf, *goFor, (click), (submit),
// [hydroId], <form> — or the sentinel KindChildTag / KindInput.
Name string
// Expr is the attribute's trimmed expression, "" when valueless.
Expr string
// Line and Col position the expression (1-based line, byte column).
Line int
Col int
// NameLine and NameCol position the attribute name or tag itself.
NameLine int
NameCol int
// Sel is the child selector involved: the element's own tag for a child
// occurrence, the enclosing element's tag for an [input] binding.
Sel string
// Attr is an [input] binding's child field name, as the author typed it.
Attr string
// Tag is the ordinal of the enclosing tag, shared by directives on the
// same element.
Tag int
}
DirectiveUse is one directive or binding occurrence in raw .lsx source: an attribute (*goIf, (click), [hydroId], [input]) or a tag-level kind (<form>, a child-selector element).
type ExprRef ¶
ExprRef is one {{ ... }} interpolation in raw .lsx source, positioned at the first byte of its trimmed expression (1-based line, 1-based byte column).
type Facts ¶
type Facts struct {
// contains filtered or unexported fields
}
Facts is the go/types view of one component package, loaded once and shared across language-server features. A Facts is immutable after LoadFacts apart from its lazily built doc-comment index.
func LoadFacts ¶
LoadFacts type-checks the Go package in dir and returns its component facts. The error covers mechanical load failures; a package that merely fails type-checking loads with Broken reporting true.
func (*Facts) ActionContracts ¶
func (f *Facts) ActionContracts(structName string, actionNames []string) map[string]ActionContract
ActionContracts returns the payload contract for each named action method on structName. A contract is anchored on the action's <Name>Guard method: the method's presence is the guard axis, and any const-set-typed field of its payload struct is a closed domain (D30). An action with no guard, or whose guard is not the pure-predicate shape, has no entry.
func (*Facts) Broken ¶
Broken reports whether the package failed type-checking; a broken package yields LSX007 diagnostics from Vet and no member or selector facts.
func (*Facts) Component ¶
Component lists structName's exported fields and methods in declaration order, or nil when the package is broken or does not declare the struct.
func (*Facts) SelectorDecls ¶
func (f *Facts) SelectorDecls() []SelectorDecl
SelectorDecls lists the selectors the package declares, sorted by selector, each located at its component struct's declaration.
func (*Facts) StructDecl ¶
StructDecl locates structName's type declaration; ok is false when the package is broken or does not declare it.
func (*Facts) Vet ¶
func (f *Facts) Vet(lsxPath, structName string, sa *SourceAnalysis) []Diagnostic
Vet cross-checks one analyzed template against its paired struct exactly as liquid vet would: source-level diagnostics short-circuit the reference check, mirroring the Build/Vet pipeline.
func (*Facts) VetSubscriptions ¶
func (f *Facts) VetSubscriptions() []Diagnostic
VetSubscriptions statically flags direct Subscribe calls on a Liquid observable (BehaviorSubject, Derived, or the Observable interface) in the package's own source — the D29 reactivity-leak check. Such a subscription is not tied to the session's unsubscribe-on-GC hook (D20), so it leaks; the managed path is liquid.Observe within Subscriptions() (D25). Best-effort: it flags the detectable call pattern, not a soundness proof, and a broken package (no usable type information) yields nothing.
type ManifestAction ¶
type ManifestAction struct {
// Name is the handler method's name — the allowlist key.
Name string `json:"name"`
// Signature renders the handler's Go signature (func() or
// func(e liquid.Event)).
Signature string `json:"signature"`
// TakesEvent reports the func(liquid.Event) shape (D11); false is the
// bare func() shape.
TakesEvent bool `json:"takesEvent"`
// Events are the binding kinds that wire this handler (click, submit,
// input, change),
// sorted and de-duplicated.
Events []string `json:"events"`
// Guard reports a <Name>Guard boundary predicate (D30): a pure check the
// dispatch seam runs over this action's payload before the handler.
Guard bool `json:"guard"`
// ClosedDomains maps a payload field (as declared) to the enumerated value
// set the seam admits (D30); an empty, non-null map when the action
// constrains no field.
ClosedDomains map[string][]string `json:"closedDomains"`
}
ManifestAction is one allowlisted event handler (D10): a method dispatchable from client events, with its D11 signature.
type ManifestComponent ¶
type ManifestComponent struct {
// Selector is the custom-element tag the component registers under.
Selector string `json:"selector"`
// Struct is the paired Go struct's name.
Struct string `json:"struct"`
// File is the component's .lsx template path.
File string `json:"file"`
// Interactive reports a declared [hydroId] root — the component mints a
// hydro session and dispatches (click)/(submit) actions.
Interactive bool `json:"interactive"`
// Head reports that the struct provides a Head() document head (D6).
Head bool `json:"head"`
// Fields are the exported struct fields, in declaration order.
Fields []ManifestField `json:"fields"`
// Actions are the allowlisted event handlers wired from this template,
// sorted by name.
Actions []ManifestAction `json:"actions"`
}
ManifestComponent is one component's compiled surface: its identity, the struct fields an author can bind, the allowlisted actions an author can wire, and whether it roots an interactive (hydro) region.
type ManifestField ¶
type ManifestField struct {
// Name is the field identifier templates and [input] bindings reference.
Name string `json:"name"`
// Type renders the field's Go type with package names qualified bare
// (liquid.Event, not the import path).
Type string `json:"type"`
// Input reports that some template binds this field via [input] (D4
// nesting) — i.e. it is a composition input, not internal state.
Input bool `json:"input"`
}
ManifestField is one exported struct field an author can read or bind.
type ManifestGraph ¶
type ManifestGraph struct {
// Version is the envelope schema code (ManifestVersion).
Version string `json:"version"`
// Components are the resolvable components, sorted by selector then source
// file. Empty (not null) when the directory declares none.
Components []ManifestComponent `json:"components"`
}
ManifestGraph is the whole compiled component app as data: a versioned envelope over every resolvable component in the scanned directory, sorted by selector. The static graph only — not live sessions or runtime instances (D2).
type Member ¶
type Member struct {
// Name is the identifier templates reference.
Name string
// Method reports a method; false means a struct field.
Method bool
// Type renders the member's type — a field type, or a method signature —
// with package names qualified bare (liquid.Event, not the import path).
Type string
// Doc is the declaration's doc comment, "" when undocumented.
Doc string
// File, Line and Col (1-based line and byte column) locate the
// declaration in Go source.
File string
Line int
Col int
// Handler reports a method dispatchable as an event handler (D11).
Handler bool
}
Member is one exported field or method of a component struct, as the language server presents it.
type SelectorDecl ¶
type SelectorDecl struct {
// Selector is the custom-element tag (app-user-card).
Selector string
// Struct is the component struct declaring the selector.
Struct string
// Decl locates the component struct's type declaration.
Decl
}
SelectorDecl records one component selector a package declares.
type SourceAnalysis ¶
type SourceAnalysis struct {
Interpolations []ExprRef
Directives []DirectiveUse
// Diagnostics are the source-level findings (LSX001, LSX005, LSX006,
// LSX010, LSX013, LSX014); when non-empty the compile pipeline stops
// before the vet cross-check, and Facts.Vet mirrors that.
Diagnostics []Diagnostic
// contains filtered or unexported fields
}
SourceAnalysis is everything a source-only scan of one .lsx buffer yields: the positioned references, and the source-level diagnostics that gate the vet cross-check.
func AnalyzeSource ¶
func AnalyzeSource(lsxPath string, src []byte) *SourceAnalysis
AnalyzeSource runs the compiler's raw-source scans over one .lsx buffer. The buffer need not exist on disk — the language server analyzes unsaved editor contents; lsxPath supplies diagnostic positions and the pairing convention only.