compiler

package
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

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

View Source
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.

View Source
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

func PairedStructName(lsxPath string) string

PairedStructName returns the struct name the .lsx filename convention pairs a template with (user_card.lsx → UserCard).

func ParseGoFor

func ParseGoFor(expr string) (loopVar, list string, ok bool)

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.

func Vet

func Vet(ctx context.Context, dir string) ([]Diagnostic, error)

Vet runs the same diagnostic checks as Build on every .lsx file under dir without writing any generated files (D13).

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

type ExprRef struct {
	Expr string
	Line int
	Col  int
}

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

func LoadFacts(ctx context.Context, dir string) (*Facts, error)

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

func (f *Facts) Broken() bool

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

func (f *Facts) Component(structName string) []Member

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

func (f *Facts) StructDecl(structName string) (Decl, bool)

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 Severity

type Severity string

Severity classifies how serious a Diagnostic is.

const (
	SeverityError   Severity = "error"
	SeverityWarning Severity = "warning"
)

The two diagnostic severities defined by D13.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL