import (
"github.com/titpetric/vuego"
}// Component is a renderable .vuego template component.
type Component interface {
// Render renders the component template to the given writer.
Render(ctx context.Context, w io.Writer, filename string, data any) error
}// ExprEvaluator wraps expr for evaluating boolean and interpolated expressions.
// It caches compiled programs to avoid recompilation.
type ExprEvaluator struct {
mu sync.RWMutex
programs map[string]*vm.Program
}// FuncMap is a map of function names to functions, similar to text/template's FuncMap.
// Functions can have any number of parameters and must return 1 or 2 values.
// If 2 values are returned, the second must be an error.
//
// Functions can optionally take *VueContext or context.Context as the first parameter:
//
// - func myFunc(ctx context.Context) bool { ... }
// - func myFunc(ctx *VueContext, arg1 string) (string, error) { ... }
// - func myFunc(arg1 string) string { ... }
//
// This allows access to the execution context.
type FuncMap map[string]any// LessProcessor implements vuego.NodeProcessor.
// It implements the functionality to compile Less CSS to standard css.
// It processes `script` tags with a `type="text/css+less"` attribute.
type LessProcessor struct {
// fs is optional filesystem for loading @import statements in LESS files
fs fs.FS
}// LessProcessorError wraps processing errors with context.
type LessProcessorError struct {
// Err is the underlying error from the LESS processor.
Err error
// Reason is a descriptive message about the LESS processing failure.
Reason string
}// LoadOption is a functional option for configuring Load().
type LoadOption func(*Vue)// Loader loads and parses .vuego files from an fs.FS.
type Loader struct {
// FS is the file system used to load templates.
FS fs.FS
}// NodeProcessor is an interface for post-processing []*html.Node after template evaluation.
// Implementations can inspect and modify HTML nodes to implement custom tags and attributes.
// NodeProcessor receives the complete rendered DOM and can transform it in place.
//
// Processors are called after all Vue directives and interpolations have been evaluated.
// Multiple processors can be registered and are applied in order of registration.
//
// See docs/nodeprocessor.md for examples and best practices.
type NodeProcessor interface {
// New ensures that we can have render-scoped allocations.
New() NodeProcessor
// PreProcess receives the nodes as they are decoded.
PreProcess(nodes []*html.Node) error
// PostProcess receives the rendered nodes and may modify them in place.
// It should return an error if processing fails.
PostProcess(nodes []*html.Node) error
}// OverlayFS overlays two filesystems.
// This allows extension of the lower filesystem with modified files,
// new files and encourages composition of the contents of a `fs.FS`.
type OverlayFS struct {
chainFS []fs.FS
}// Renderer renders parsed HTML nodes to output.
type Renderer interface {
// Render renders HTML nodes to the given writer.
Render(ctx context.Context, w io.Writer, nodes []*html.Node) error
}// SlotContent holds the processed content for a slot along with any scoped props.
type SlotContent struct {
// Nodes contains the rendered content for this slot.
Nodes []*html.Node
// Props contains any scoped props passed to the slot.
Props map[string]any
// TemplateNode holds the original template node for processing scoped slots.
TemplateNode *html.Node
}// SlotScope holds all slot contents indexed by name for a component instance.
type SlotScope struct {
// Slots maps slot names to their content.
// Empty string key is the default slot.
Slots map[string]*SlotContent
}// Stack provides stack-based variable lookup and convenient typed accessors.
type Stack struct {
stack []map[string]any // bottom..top, top is last element
rootData any // original data passed to Render (for struct field fallback)
// envCache caches the flattened map from EnvMap().
// Invalidated on Push/Pop/Set. Stack is request-scoped so no mutex needed.
envCache map[string]any
}// Template represents a prepared vuego template.
// It allows variable assignment and rendering with internal buffering.
type Template interface {
TemplateConstructors
TemplateState
TemplateRendering
TemplateRenderingDetail
}// TemplateConstructors creates new template rendering contexts.
// The results provide request scoped data allocations and should be discarded after use.
type TemplateConstructors interface {
New() Template
Load(filename string) Template
}// TemplateRendering bundles the interface for the render functions.
type TemplateRendering interface {
Render(ctx context.Context, w io.Writer) error
}// TemplateRenderingDetail the interface for stateless render functions.
type TemplateRenderingDetail interface {
RenderFile(ctx context.Context, w io.Writer, filename string) error
RenderString(ctx context.Context, w io.Writer, templateStr string) error
RenderByte(ctx context.Context, w io.Writer, templateData []byte) error
RenderReader(ctx context.Context, w io.Writer, r io.Reader) error
}// TemplateState bundles the interface for template state management.
type TemplateState interface {
Fill(vars any) Template
Assign(key string, value any) Template
Get(key string) string
}// Vue is the main template renderer for .vuego templates.
// After initialization, Vue is safe for concurrent use by multiple goroutines.
type Vue struct {
templateFS fs.FS
loader *Loader
renderer Renderer
funcMap FuncMap
exprEval *ExprEvaluator
// Template cache to avoid re-parsing the same template
templateCache map[string]*templateCacheEntry
templateMu sync.RWMutex
// Custom node processors for post-processing rendered DOM
nodeProcessors []NodeProcessor
// Component shorthand mapping: maps kebab-case tag names to component filenames
// e.g., "button-primary" -> "components/ButtonPrimary.vuego"
componentMap map[string]string
// initialData is pre-loaded data from WithData() that seeds the template stack.
// This is equivalent to calling Fill() on the base template before any New()/Load().
initialData map[string]any
}// VueContext carries template inclusion context and request-scoped state used during evaluation.
// Each render operation gets its own VueContext, making concurrent rendering safe.
type VueContext struct {
// Variable scope and data resolution
ctx context.Context
stack *Stack
// BaseDir is the root directory for template inclusion chains.
BaseDir string
// CurrentDir is the current working directory during template processing.
CurrentDir string
// FromFilename is the name of the file currently being processed.
FromFilename string
// TemplateStack is the stack of included template files.
TemplateStack []string
// TagStack is the stack of HTML tags being rendered.
TagStack []string
// Processors are the registered template processors.
Processors []NodeProcessor
// v-once element tracking for deep clones
seen map[string]bool
// SlotScope contains slot content for the current component.
SlotScope *SlotScope
}// VueContextOptions holds configurable options for a new VueContext.
type VueContextOptions struct {
// Stack is the resolver stack for variable lookups.
Stack *Stack
// Processors are the registered template processors.
Processors []NodeProcessor
}func GetSlotName (attr string) stringfunc New (opts ...LoadOption) Templatefunc NewExprEvaluator () *ExprEvaluatorfunc NewFS (templateFS fs.FS, opts ...LoadOption) Templatefunc NewLessProcessor (fsys ...fs.FS) *LessProcessorfunc NewLoader (fs fs.FS) *Loaderfunc NewOverlayFS (upper fs.FS, lower ...fs.FS) *OverlayFSfunc NewRenderer () Rendererfunc NewSlotScope () *SlotScopefunc NewStack (root map[string]any) *Stackfunc NewStackWithData (root map[string]any, originalData any) *Stackfunc NewVue (templateFS fs.FS) *Vuefunc NewVueContext (ctx context.Context, fromFilename string, options *VueContextOptions) VueContextfunc View (renderer Template, filename string, data V) Templatefunc WithComponents () LoadOptionfunc WithFS (templateFS fs.FS) LoadOptionfunc WithFuncs (funcMap FuncMap) LoadOptionfunc WithLessProcessor () LoadOptionfunc WithProcessor (processor NodeProcessor) LoadOptionfunc (*ExprEvaluator) ClearCache ()func (*ExprEvaluator) Eval (expression string, env map[string]any) (any, error)func (*LessProcessor) New () NodeProcessorfunc (*LessProcessor) PostProcess (nodes []*html.Node) errorfunc (*LessProcessor) PreProcess (nodes []*html.Node) errorfunc (*LessProcessorError) Error () stringfunc (*Loader) Load (filename string) ([]*html.Node, error)func (*Loader) LoadFragment (filename string) ([]*html.Node, error)func (*Loader) Stat (filename string) errorfunc (*OverlayFS) Glob (pattern string) ([]string, error)func (*OverlayFS) Open (name string) (fs.File, error)func (*OverlayFS) ReadDir (name string) ([]fs.DirEntry, error)func (*SlotScope) GetSlot (name string) *SlotContentfunc (*SlotScope) SetSlot (name string, content *SlotContent)func (*Stack) Copy () *Stackfunc (*Stack) EnvMap () map[string]anyfunc (*Stack) ForEach (expr string, fn func(index int, value any) error) errorfunc (*Stack) GetInt (expr string) (int, bool)func (*Stack) GetMap (expr string) (map[string]any, bool)func (*Stack) GetSlice (expr string) ([]any, bool)func (*Stack) GetString (expr string) (string, bool)func (*Stack) Lookup (name string) (any, bool)func (*Stack) Pop ()func (*Stack) Push (m map[string]any)func (*Stack) Resolve (expr string) (any, bool)func (*Stack) Set (key string, val any)func (*Vue) DefaultFuncMap () FuncMapfunc (*Vue) Funcs (funcMap FuncMap) *Vuefunc (*Vue) GetComponentFile (tagName string) (string, bool)func (*Vue) RegisterComponent (tagName,filename string) *Vuefunc (*Vue) RegisterNodeProcessor (processor NodeProcessor) *Vuefunc (*Vue) Render (ctx context.Context, w io.Writer, filename string, data any) errorfunc (*Vue) RenderFragment (ctx context.Context, w io.Writer, filename string, data any) errorfunc (*VueContext) PopTag ()func (*VueContext) PushTag (tag string)func (VueContext) Context () context.Contextfunc (VueContext) CurrentTag () stringfunc (VueContext) ExprEnv () map[string]anyfunc (VueContext) FormatTemplateChain () stringfunc (VueContext) Stack () *Stackfunc (VueContext) WithTemplate (filename string) VueContext
GetSlotName extracts the slot name from a v-slot directive. Returns "default" if no name is specified.
func GetSlotName(attr string) stringNew creates a new Template for rendering strings, bytes, or readers without a filesystem. Use this when templates are provided as strings/bytes rather than loaded from files. To render from files, use NewFS(fs) or New(WithFS(fs)) instead. The returned Template can be used for variable assignment and rendering.
func New(opts ...LoadOption) TemplateNewExprEvaluator creates a new ExprEvaluator with an empty cache.
func NewExprEvaluator() *ExprEvaluatorNewFS creates a new Template with access to the given filesystem and optional configurations. The returned Template can be used to render files, strings, or bytes with variable assignment.
func NewFS(templateFS fs.FS, opts ...LoadOption) TemplateNewLessProcessor creates a new LESS processor.
func NewLessProcessor(fsys ...fs.FS) *LessProcessorNewLoader creates a Loader backed by fs.
func NewLoader(fs fs.FS) *LoaderNewOverlayFS will create a new *OverlayFS.
func NewOverlayFS(upper fs.FS, lower ...fs.FS) *OverlayFSNewRenderer creates a new Renderer.
func NewRenderer() RendererNewSlotScope creates a new SlotScope for a component.
func NewSlotScope() *SlotScopeNewStack constructs a Stack with an optional initial root map (nil allowed). The originalData parameter is the original value passed to Render (for struct field fallback).
func NewStack(root map[string]any) *StackNewStackWithData constructs a Stack with both map data and original root data for struct field fallback.
func NewStackWithData(root map[string]any, originalData any) *StackNewVue creates a new Vue backed by the given filesystem. The returned Vue is safe for concurrent use by multiple goroutines.
func NewVue(templateFS fs.FS) *VueNewVueContext returns a VueContext initialized for the given template filename with initial data.
func NewVueContext(ctx context.Context, fromFilename string, options *VueContextOptions) VueContextView is a type safety shim to bind a template file to a data model type. The returned template should be rendered and discarded.
func View(renderer Template, filename string, data V) TemplateWithComponents returns a LoadOption that registers all component shorthands. It recursively loads all .vuego files from the components folder and subfolders. File paths are mapped to kebab-case tag names using directory path and filename.
func WithComponents() LoadOptionWithFS returns a LoadOption that sets the filesystem for template loading.
func WithFS(templateFS fs.FS) LoadOptionWithFuncs returns a LoadOption that merges custom template functions into the existing funcmap.
func WithFuncs(funcMap FuncMap) LoadOptionWithLessProcessor returns a LoadOption that registers a LESS processor.
func WithLessProcessor() LoadOptionWithProcessor returns a LoadOption that registers a custom node processor.
func WithProcessor(processor NodeProcessor) LoadOptionClearCache clears the program cache (useful for testing or memory management).
func (*ExprEvaluator) ClearCache()Eval evaluates an expression against the given environment (stack). It returns the result value and any error. The expression can contain:
- Variable references: item, item.title, items[0]
- Comparison: ==, !=, <, >, <=, >=, === (same as ==, for convenience)
- Boolean operations: &&, ||, !
- Function calls: len(items), isActive(v)
- Literals: 42, "text", true, false.
func (*ExprEvaluator) Eval(expression string, env map[string]any) (any, error)New creates a new allocation of *LessProcessor.
func (*LessProcessor) New() NodeProcessorPostProcess walks the DOM tree and performs compilation.
func (*LessProcessor) PostProcess(nodes []*html.Node) errorPreProcess currently does nothing.
func (*LessProcessor) PreProcess(nodes []*html.Node) errorError returns the combined reason and underlying error message.
func (*LessProcessorError) Error() stringLoad parses a full HTML document from the given filename.
func (*Loader) Load(filename string) ([]*html.Node, error)LoadFragment parses a template fragment; if the file is a full document, it falls back to Load. Front-matter is extracted and discarded; use loadFragment to access it.
func (*Loader) LoadFragment(filename string) ([]*html.Node, error)Stat checks that filename exists in the loader filesystem.
func (*Loader) Stat(filename string) errorGlob implements combined FS reading.
func (*OverlayFS) Glob(pattern string) ([]string, error)Open opens a file in the overlaid filesystem.
func (*OverlayFS) Open(name string) (fs.File, error)ReadDir implements combined FS reading.
func (*OverlayFS) ReadDir(name string) ([]fs.DirEntry, error)GetSlot retrieves slot content by name. Returns nil if the slot doesn't exist.
func (*SlotScope) GetSlot(name string) *SlotContentSetSlot sets the content for a named slot.
func (*SlotScope) SetSlot(name string, content *SlotContent)Copy returns a copy of the stack that can be discarded. The root data is retained as is, the envmap is a copy.
func (*Stack) Copy() *StackEnvMap converts the Stack to a map[string]any for expr evaluation. Includes all accessible values from stack and struct fields. The result is cached and reused until the stack is mutated via Push/Pop/Set.
func (*Stack) EnvMap() map[string]anyForEach iterates over a collection at the given expr and calls fn(index,value). Supports slices/arrays and map[string]any (iteration order for maps is unspecified). If fn returns an error iteration is stopped and the error passed through.
func (*Stack) ForEach(expr string, fn func(index int, value any) error) errorGetInt resolves and tries to return an int (best-effort).
func (*Stack) GetInt(expr string) (int, bool)GetMap returns map[string]any or converts map[string]string to map[string]any. Avoids reflection for other map types.
func (*Stack) GetMap(expr string) (map[string]any, bool)GetSlice returns a []any for slice types.
func (*Stack) GetSlice(expr string) ([]any, bool)GetString resolves and tries to return a string.
func (*Stack) GetString(expr string) (string, bool)Lookup searches stack from top to bottom for a plain identifier (no dots). If not found in the stack maps, it checks the root data struct (if any). Returns (value, true) if found.
func (*Stack) Lookup(name string) (any, bool)Pop the top-most Stack. If only root remains it still pops to empty slice safely. Returns pooled maps to reduce GC pressure.
func (*Stack) Pop()Push a new map as a top-most Stack. If m is nil, an empty map is obtained from the pool.
func (*Stack) Push(m map[string]any)Resolve resolves dotted/bracketed expression paths like:
"user.name", "items[0].title", "mapKey.sub"
It returns (value, true) if resolution succeeded.
func (*Stack) Resolve(expr string) (any, bool)Set sets a key in the top-most Stack.
func (*Stack) Set(key string, val any)DefaultFuncMap returns a FuncMap with built-in utility functions.
func (*Vue) DefaultFuncMap() FuncMapFuncs merges custom template functions into the existing funcmap, overwriting any existing keys. Returns the Vue instance for chaining.
func (*Vue) Funcs(funcMap FuncMap) *VueGetComponentFile looks up a component file by its kebab-case tag name. Returns the filename and true if found, otherwise returns empty string and false.
func (*Vue) GetComponentFile(tagName string) (string, bool)RegisterComponent registers a component shorthand mapping. The tagName (e.g., "button-primary") will be resolved to the given filename (e.g., "components/ButtonPrimary.vuego").
func (*Vue) RegisterComponent(tagName, filename string) *VueRegisterNodeProcessor adds a custom node processor to the Vue instance. Processors are applied in order after template evaluation completes.
func (*Vue) RegisterNodeProcessor(processor NodeProcessor) *VueRender processes a full-page template file and writes the output to w. Front-matter data in the template is authoritative and overrides passed data. Render is safe to call concurrently from multiple goroutines.
func (*Vue) Render(ctx context.Context, w io.Writer, filename string, data any) errorRenderFragment processes a template fragment file and writes the output to w. Front-matter data in the template is authoritative and overrides passed data. RenderFragment is safe to call concurrently from multiple goroutines.
func (*Vue) RenderFragment(ctx context.Context, w io.Writer, filename string, data any) errorPopTag removes the current tag from the tag stack.
func (*VueContext) PopTag()PushTag adds a tag to the tag stack.
func (*VueContext) PushTag(tag string)Context returns the context.Context associated with this VueContext.
func (VueContext) Context() context.ContextCurrentTag returns the current parent tag, or empty string if no tag is on the stack.
func (VueContext) CurrentTag() stringExprEnv returns the env map for expr evaluation, wrapping any functions whose first parameter is context.Context so the context is injected automatically.
func (VueContext) ExprEnv() map[string]anyFormatTemplateChain returns the template inclusion chain formatted for error messages.
func (VueContext) FormatTemplateChain() stringStack returns the variable resolution stack for this context. This allows functions with *VueContext parameters to resolve variables from the execution scope.
func (VueContext) Stack() *StackWithTemplate returns a copy of the context extended with filename in the inclusion chain.
func (VueContext) WithTemplate(filename string) VueContext