gotmx

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 22 Imported by: 0

README

Gotmx

Go Reference CI Go Report Card

Gotmx is a component-based HTML template engine for Go that keeps your templates as valid HTML.

  • Plain HTML templates: Use standard data-g-* attributes instead of custom syntax.
  • Browser-previewable: Open templates directly in your browser to see structure and styling.
  • Composable components: Build complex UIs from small, reusable pieces using slots.
  • Interoperable: Mix with Go's text/template and html/template where needed.
  • No build step: Works with any HTML editor, no special tooling required.

Gotmx is ideal for server-side rendering, HTMX-enhanced applications, HTML emails, or any scenario where you want to author templates as real HTML.

Quick Start

Install Gotmx:

go get github.com/authentic-devel/gotmx
Minimal Example

Create an HTML template file templates/hello.htm (.htm for eager loading at startup):

<div data-g-define="hello">
  <span data-g-inner-text="[[ .Name ]]">placeholder</span>
</div>

Render it from Go:

package main

import (
    "context"
    "fmt"
    "github.com/authentic-devel/gotmx"
)

func main() {
    // Create engine and load templates from directory
    engine, err := gotmx.New(
        gotmx.WithTemplateDir("./templates"),
    )
    if err != nil {
        panic(err)
    }
    defer engine.Close()

    // Render template with data
    data := map[string]string{"Name": "World"}
    result, err := engine.RenderString(context.Background(), "hello", data)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // Output: <div><span>World</span></div>
}
Inline Templates

You can also load templates directly from strings:

engine, _ := gotmx.New()
engine.LoadHTML(`<div data-g-define="greeting">Hello, <span data-g-inner-text="[[ .Name ]]"></span>!</div>`)

result, _ := engine.RenderString(context.Background(), "greeting", map[string]string{"Name": "Gotmx"})
Dev Mode with Auto-Reload

For development, enable file watching to automatically reload templates on changes:

engine, _ := gotmx.New(
    gotmx.WithTemplateDir("./templates"),
    gotmx.WithDevMode(true),
)
defer engine.Close() // Important: stops file watchers

Philosophy and Goals

Gotmx was created to make server-side rendering easy and ergonomic, especially for HTMX-enhanced applications. While Go's built-in templates are powerful, they use non-HTML syntax that breaks editor tooling and prevents browser preview.

Design Principles

Zero additional tooling: Templates are plain HTML with data attributes. Any HTML editor works. No plugins, no build step.

Browser-previewable: Open your templates directly in a browser to see layout and styling. The data-g-* attributes are ignored by browsers, so the HTML renders normally with placeholder content.

Progressive enhancement: Start with static HTML for design and styling, then add template directives incrementally where you need dynamic behavior.

Composition over inheritance: Build complex UIs by composing small components. Use slots to inject content rather than complex inheritance chains.

Separation of concerns: Templates define structure and presentation. Business logic stays in Go code. Keep templates declarative and simple.

No lock-in: Gotmx coexists with Go's native templates. Use Gotmx where it fits and Go templates where they make more sense. You can even mix them in the same project.

Minimal dependencies: Gotmx avoids framework assumptions. Use it with Gin, Echo, Chi, net/http, or no framework at all.

Browser-Previewable Templates

Because gotmx uses HTML attributes, templates remain valid HTML that you can open directly in a browser. Sample content stays visible in preview but gets replaced at runtime:

<div data-g-define="user-list">
    <h2 data-g-inner-text="[[ .Title ]]">Team Members</h2>
    <ul>
        <!-- First item is the template — repeated for each user at runtime -->
        <li data-g-outer-repeat="[[ .Users ]]">
            <strong data-g-inner-text="[[ .Name ]]">Alice Johnson</strong> —
            <span data-g-inner-text="[[ .Role ]]">Engineer</span>
        </li>
        <!-- Extra items are preview-only, stripped at runtime -->
        <li data-g-ignore="outer">
            <strong>Bob Smith</strong> — <span>Designer</span>
        </li>
        <li data-g-ignore="outer">
            <strong>Carol Lee</strong> — <span>Product Manager</span>
        </li>
    </ul>
</div>

Open this file in a browser and you see a team list with three members. At runtime, the ignored items disappear and the first <li> repeats for each actual user.

Other preview techniques include:

  • Placeholder text in data-g-inner-text elements (visible in preview, replaced at runtime)
  • Static src/href with data-g-src/data-g-href overrides (relative paths for preview, absolute for server)
  • Slot default content (shows layout structure in preview, replaced by caller at runtime)
  • Full HTML scaffolding around data-g-define elements (the <html>, <head>, <body> wrapper is ignored at runtime)

See Browser Preview Guide for all techniques with examples.

Accessing Data from Your Model

Gotmx provides a fast path syntax for accessing model properties: [[ .Path ]]. This uses square brackets instead of Go template's curly braces and is optimized for simple property access.

<!-- Simple property access -->
<span data-g-inner-text="[[ .User.Name ]]"></span>

<!-- Nested properties -->
<span data-g-inner-text="[[ .Order.Customer.Email ]]"></span>

<!-- Array indexing -->
<span data-g-inner-text="[[ .Items[0].Title ]]"></span>

<!-- String concatenation -->
<span data-g-inner-text='[[ "Hello, " .Name "!" ]]'></span>

<!-- Current data context -->
<span data-g-inner-text="[[ . ]]"></span>

The [[ ]] syntax supports:

  • Property access on structs and maps
  • Array/slice indexing
  • String literals (in single or double quotes)
  • Concatenation of multiple values
  • Simple comparisons and negation

For complex expressions, use Go templates with data-g-as-template. See Working with Go Templates.

The path resolution is powered by the empaths library.

Slots: Component Composition

Slots let you define named injection points in your templates. This enables true component composition where the parent decides what content to inject.

Defining Slots

Use data-g-define-slot to mark where content can be injected:

<div data-g-define="card">
    <div class="card-header" data-g-define-slot="header">
        Default Header (preview only)
    </div>
    <div class="card-body" data-g-define-slot="">
        Default content (preview only)
    </div>
    <div class="card-footer" data-g-define-slot="footer">
        Default Footer (preview only)
    </div>
</div>

An empty slot name (data-g-define-slot="") creates the default slot for content without an explicit slot assignment.

Note: The text inside slot elements is for browser preview only. At render time, slots display the injected content or nothing — the preview text is never rendered.

Filling Slots

Use data-g-use-slot when calling a component to direct content to specific slots:

<div data-g-use="card">
    <h2 data-g-use-slot="header">My Custom Title</h2>
    <p>This paragraph goes to the default slot.</p>
    <button data-g-use-slot="footer">Action Button</button>
</div>

Children without data-g-use-slot are placed in the default slot.

Why Slots Matter

Slots invert the dependency direction. Instead of a layout template deciding which content template to include, the calling code decides what to inject. This makes components truly independent and reusable.

For HTMX applications, this is particularly useful: you can render a full page (layout + content) for initial requests, or just the content component for HTMX partial updates.

Building Blocks Overview

Gotmx has a layered architecture:

Engine: The single entry point for using gotmx. Handles template loading, dev mode, and provides convenient render methods.

engine, _ := gotmx.New(gotmx.WithTemplateDir("templates"))
engine.Render(ctx, w, "my-template", data)

TemplateRegistry: Stores templates by name and namespace. Supports both eager loading (at startup) and lazy loading (on first use). Pluggable via WithCustomRegistry().

Template/Renderable: Core interfaces. A Template is a factory that creates Renderable components bound to data.

ModelPathResolver: Resolves [[ .Path ]] expressions. Uses empaths by default but can be replaced via WithCustomResolver().

For detailed architecture information, see Architecture.

Go Template Integration

Gotmx is not a replacement for Go templates. It complements them. You can use Go templates anywhere Gotmx templates alone are insufficient.

Inline Go Templates

Add data-g-as-template to treat an element's content as a Go HTML template:

<ul data-g-as-template>
    {{ range .Items }}
        <li>{{ .Name }}</li>
    {{ end }}
</ul>

Use data-g-as-unsafe-template for text templates (no automatic HTML escaping).

Calling Gotmx from Go Templates

Within Go templates, use GTemplate to render Gotmx templates:

<div data-g-as-template>
    {{ GTemplate "user-card" .User }}
</div>

See Working with Go Templates for details.

Template Directives

Gotmx uses HTML attributes to control template behavior. All attributes work in both short (g-*) and long (data-g-*) forms.

Template Definition:

  • data-g-define="name" - Define a reusable template

Control Flow:

  • data-g-if="[[ .Condition ]]" - Conditional rendering
  • data-g-with="[[ .Object ]]" - Switch data context
  • data-g-ignore - Skip element or children

Iteration:

  • data-g-outer-repeat="[[ .Items ]]" - Repeat entire element
  • data-g-inner-repeat="[[ .Items ]]" - Repeat children only

Content:

  • data-g-inner-text="[[ .Text ]]" - Set text content (escaped)
  • data-g-inner-html="[[ .Html ]]" - Set HTML content (unescaped)
  • data-g-outer-text="[[ .Text ]]" - Replace element with text

Composition:

  • data-g-use="template" - Render a different template
  • data-g-inner-use="template" - Render a template's inner content only
  • data-g-define-slot="name" - Define a slot location
  • data-g-use-slot="name" - Fill a named slot
  • data-g-override-att="class,id" - Pass attributes to component

Attributes:

  • data-g-class="[[ .Class ]]" - Set class attribute
  • data-g-href="[[ .Url ]]" - Set href attribute
  • data-g-src="[[ .Url ]]" - Set src attribute
  • data-g-att-*="[[ .Value ]]" - Set any attribute dynamically
  • data-g-attif-*="[[ .Condition ]]" - Conditionally add attribute
  • data-g-trans="tagname" - Transform element tag name

See Attribute Reference for complete documentation.

Layout Composition

Use WithLayout to wrap any template inside a layout in a single render call:

engine.Render(ctx, w, "dashboard-page", pageData,
    gotmx.WithLayout("main-layout", layoutData),
)

The rendered page is placed into the layout's default slot. Use WithLayoutSlot to target a named slot:

engine.Render(ctx, w, "dashboard-page", pageData,
    gotmx.WithLayout("main-layout", layoutData),
    gotmx.WithLayoutSlot("content"),
)

This is especially useful for HTMX applications where you render just the component for HTMX requests and the full page with layout for initial loads:

if isHxRequest(r) {
    engine.Render(r.Context(), w, "page", data)
} else {
    engine.Render(r.Context(), w, "page", data,
        gotmx.WithLayout("layout", layoutData),
    )
}

Rendering Behavior

Attribute order: By default, attributes render in map iteration order for performance. Enable WithDeterministicOutput(true) for sorted, reproducible output (useful for testing).

Boolean attributes: HTML boolean attributes like disabled, checked, hidden, required, readonly, and selected render without a value when present (e.g., <button disabled> instead of <button disabled="true">).

HTML escaping: All text content and attribute values are HTML-escaped by default to prevent XSS. Use data-g-inner-html only for trusted content.

Whitespace: Some whitespace may not be preserved exactly due to HTML parsing normalization.

Customization

Gotmx components can be replaced with custom implementations:

engine, _ := gotmx.New(
    gotmx.WithCustomRegistry(myRegistry),    // Custom template storage
    gotmx.WithCustomResolver(myResolver),    // Custom path resolution
    gotmx.WithLogger(slog.Default()),        // Custom logging
    gotmx.WithMaxNestingDepth(128),          // Max template nesting depth
)
Max Nesting Depth (Circular Reference Protection)

Gotmx protects against circular template references that could cause stack overflow. When template A uses template B, and B uses A, this creates an infinite loop. Gotmx detects this by limiting the nesting depth of g-use calls.

The default limit is 64 levels, which is sufficient for complex component hierarchies. You can configure this:

// Allow deeper nesting for very complex hierarchies
engine, _ := gotmx.New(gotmx.WithMaxNestingDepth(128))

// Use a stricter limit
engine, _ := gotmx.New(gotmx.WithMaxNestingDepth(32))

// Disable the limit (not recommended)
engine, _ := gotmx.New(gotmx.WithMaxNestingDepth(0))

If the limit is exceeded, rendering fails with a MaxNestingDepthExceededError that includes the template name and current depth, making it easy to diagnose circular references.

See Customization Guide.

Performance

Gotmx prioritizes developer experience over raw performance. That said, it uses:

  • Streaming output to io.Writer (no intermediate string allocation for large outputs)
  • Buffered writing via bufio.Writer to batch many small write calls efficiently
  • Buffer pooling (sync.Pool) for RenderString to reduce GC pressure
  • Fast path resolution via empaths
  • Type-switch fast paths for iteration (avoids reflection for common types)
  • Lazy template loading to reduce startup time
  • Context cancellation support to stop rendering when clients disconnect

For most server-side rendering use cases, performance is more than adequate.

Security

Gotmx HTML-escapes all text content and attribute values by default to prevent XSS attacks. Characters like <, >, &, and " are converted to HTML entities.

Unconditionally safe (always escaped, even with Unescaped()):

  • data-g-inner-text - Always escaped for XSS safety
  • Attribute values - Always escaped

Safe by default (follows global escaping setting):

  • data-g-outer-text - Escaped by default, respects Unescaped() option
  • Text nodes - Escaped by default, respects Unescaped() option

Unsafe (never escaped):

  • data-g-inner-html - Use only with trusted content
  • data-g-as-unsafe-template - Use only with trusted templates

Always validate and sanitize user input in your Go code before passing it to templates.

Further Reading

License

MIT License - see LICENSE file for details.

Documentation

Overview

Package gotmx is a component-based HTML template engine for Go.

It enables server-side rendering with templates that remain valid HTML. Templates use standard HTML attributes (g-* or data-g-*) instead of custom syntax, making them viewable and editable in any HTML editor and previewable in browsers.

Getting Started

Use the Engine API to create and render templates:

engine, err := gotmx.New(gotmx.WithTemplateDir("templates"))
if err != nil {
    log.Fatal(err)
}
defer engine.Close()

// Render a template
engine.Render(ctx, w, "my-template", data)

Advanced Customization

For advanced use cases, Engine supports custom registries and resolvers:

engine, err := gotmx.New(
    gotmx.WithCustomRegistry(myRegistry),
    gotmx.WithCustomResolver(myResolver),
)

Template Attributes

Templates use special attributes to control rendering:

  • g-if: Conditional rendering
  • g-outer-repeat, g-inner-repeat: Iteration over collections
  • g-inner-text, g-inner-html: Content replacement
  • g-use: Component composition
  • g-define-slot, g-use-slot: Slot-based content injection

For more information on available attributes, see the attribute reference in the docs/ directory.

Index

Examples

Constants

View Source
const DefaultMaxNestingDepth = 64

DefaultMaxNestingDepth is the default maximum template nesting depth. This prevents stack overflow from circular template references.

Variables

This section is empty.

Functions

This section is empty.

Types

type AmbiguousTemplateError

type AmbiguousTemplateError struct {
	Name       TemplateName
	Namespaces []Namespace
}

AmbiguousTemplateError is returned when a template name matches multiple templates across different namespaces and no namespace was specified to disambiguate.

func (*AmbiguousTemplateError) Error

func (e *AmbiguousTemplateError) Error() string

type AttributeMap

type AttributeMap map[string]string

type ComponentCreationError

type ComponentCreationError struct {
	TemplateName TemplateRef
	Cause        error
}

ComponentCreationError is returned when a component fails to be created from a template. It wraps the underlying error and provides context about which template failed.

func (*ComponentCreationError) Error

func (e *ComponentCreationError) Error() string

func (*ComponentCreationError) Unwrap

func (e *ComponentCreationError) Unwrap() error

type ComponentNotFoundError

type ComponentNotFoundError struct {
	ComponentRef string
	TemplateName TemplateName
	Cause        error
}

ComponentNotFoundError is returned when a component cannot be created, typically because the referenced template does not exist.

func (*ComponentNotFoundError) Error

func (e *ComponentNotFoundError) Error() string

func (*ComponentNotFoundError) Unwrap

func (e *ComponentNotFoundError) Unwrap() error

type DuplicateTemplateError

type DuplicateTemplateError struct {
	Name      TemplateName
	Namespace Namespace
}

DuplicateTemplateError is returned when attempting to register a template that already exists with the same name and namespace.

func (*DuplicateTemplateError) Error

func (e *DuplicateTemplateError) Error() string

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the main entry point for the gotmx template engine. It provides template loading, rendering, and lifecycle management. Create one with New() and pass it around your application.

func New

func New(opts ...Option) (*Engine, error)

New creates a new template engine with the given options. Returns an error if the configuration is invalid.

func (*Engine) Close

func (e *Engine) Close() error

Close releases any resources held by the engine (file watchers, etc.) Should be called via defer after New().

func (*Engine) Component

func (e *Engine) Component(templateName string, data any) (Renderable, error)

Component returns a Renderable for the given template. Useful when you need to pass a component as slot content.

func (*Engine) ComponentWithSlots

func (e *Engine) ComponentWithSlots(templateName string, data any, slots map[string][]Renderable) (Renderable, error)

ComponentWithSlots creates a Renderable and populates its slots. This is a convenience method combining Component() and AddChildren().

func (*Engine) HasTemplate

func (e *Engine) HasTemplate(templateName string) bool

HasTemplate checks if a template exists without triggering lazy loading. Useful for conditional rendering logic.

func (*Engine) LoadFS

func (e *Engine) LoadFS(fsys fs.FS, prefix string) error

LoadFS loads templates from a filesystem with an optional prefix. Useful for the registration pattern where packages register their own templates. The prefix is prepended to template namespaces for disambiguation.

engine.LoadFS(users.TemplateFS, "users")  // templates namespaced as "users/..."
engine.LoadFS(shared.TemplateFS, "")      // no prefix

func (*Engine) LoadFile

func (e *Engine) LoadFile(path string) error

LoadFile parses a single HTML file and registers any template definitions. The file path becomes the namespace for the templates.

func (*Engine) LoadHTML

func (e *Engine) LoadHTML(html string) error

LoadHTML parses HTML string(s) and registers any template definitions found. Templates are available immediately after this call. Returns an error if parsing fails.

func (*Engine) Logger

func (e *Engine) Logger() Logger

Logger returns the logger configured for this engine.

func (*Engine) MustComponent

func (e *Engine) MustComponent(templateName string, data any) Renderable

MustComponent is like Component but panics on error. Use only for templates known to exist at development time.

func (*Engine) NewRenderContext

func (e *Engine) NewRenderContext(ctx context.Context) *RenderContext

NewRenderContext creates a new RenderContext that wraps this Engine's capabilities. This context is created once per render call and passed through the entire rendering tree, avoiding per-node allocations while decoupling renderables from the Engine type.

The ctx parameter is Go's standard context.Context for request cancellation, timeouts, and passing request-scoped values. HTTP handlers should pass r.Context() to enable proper request lifecycle handling.

Use this method when you need to call Renderable.Render() directly on a component obtained via Component(). For normal template rendering, use Render() or RenderString() which create the context automatically.

Example:

component, _ := engine.Component("my-template", data)
renderCtx := engine.NewRenderContext(r.Context())
component.Render(renderCtx, writer, gotmx.RenderOuter)

func (*Engine) Preload

func (e *Engine) Preload(patterns ...string) error

Preload forces immediate loading of templates matching the given patterns. Useful for preloading critical templates while keeping others lazy. Patterns support glob syntax: "components/*.htm", "layouts/**/*.html"

func (*Engine) RegisterFunc

func (e *Engine) RegisterFunc(name string, fn any)

RegisterFunc registers a function for use in Go templates. Must be called before templates using the function are parsed.

func (*Engine) Render

func (e *Engine) Render(ctx context.Context, w io.Writer, templateName string, data any, opts ...RenderOption) error

Render writes the rendered template to the given writer. Uses HTML escaping by default. Returns an error if template not found. The ctx parameter is Go's standard context.Context for request cancellation, timeouts, and passing request-scoped values. HTTP handlers should pass r.Context().

Example
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<div data-g-define="greeting" data-g-inner-text="[[ .Name ]]">placeholder</div>`)

	engine.Render(context.Background(), os.Stdout, "greeting", map[string]any{"Name": "World"})
}
Output:

<div>World</div>
Example (Composition)
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`
		<button data-g-define="btn" class="btn"><span data-g-define-slot="">Click</span></button>
		<div data-g-define="page"><div data-g-use="btn">Submit</div></div>
	`)

	engine.Render(context.Background(), os.Stdout, "page", nil)
}
Output:

<div><button class="btn"><span>Submit</span></button></div>
Example (Conditional)
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<div data-g-define="status"><span data-g-if="[[ .Active ]]">Active</span><span data-g-if="[[ .Inactive ]]">Inactive</span></div>`)

	engine.Render(context.Background(), os.Stdout, "status", map[string]any{"Active": true, "Inactive": false})
}
Output:

<div><span>Active</span></div>
Example (Escaping)
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<div data-g-define="safe" data-g-inner-text="[[ .Input ]]">placeholder</div>`)

	// g-inner-text always escapes HTML, protecting against XSS
	engine.Render(context.Background(), os.Stdout, "safe", map[string]any{
		"Input": `<script>alert("xss")</script>`,
	})
}
Output:

<div>&lt;script&gt;alert(&#34;xss&#34;)&lt;/script&gt;</div>
Example (Iteration)
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<ul data-g-define="list"><li data-g-outer-repeat="[[ .Items ]]" data-g-inner-text="[[ . ]]">placeholder</li></ul>`)

	data := map[string]any{
		"Items": []string{"Alice", "Bob", "Carol"},
	}
	engine.Render(context.Background(), os.Stdout, "list", data)
}
Output:

<ul><li>Alice</li><li>Bob</li><li>Carol</li></ul>
Example (WithLayout)
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`
		<div data-g-define="layout"><nav>Menu</nav><main data-g-define-slot="">default</main></div>
		<p data-g-define="page">Hello from page</p>
	`)

	engine.Render(context.Background(), os.Stdout, "page", nil,
		gotmx.WithLayout("layout", nil),
	)
}
Output:

<div><nav>Menu</nav><main><p>Hello from page</p></main></div>

func (*Engine) RenderString

func (e *Engine) RenderString(ctx context.Context, templateName string, data any, opts ...RenderOption) (string, error)

RenderString renders a template to a string. Convenience wrapper around Render() using a pooled bytes.Buffer. The ctx parameter is Go's standard context.Context for request cancellation, timeouts, and passing request-scoped values.

Example
package main

import (
	"context"
	"fmt"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<span data-g-define="badge" data-g-inner-text="[[ .Label ]]">x</span>`)

	result, _ := engine.RenderString(context.Background(), "badge", map[string]any{"Label": "New"})
	fmt.Println(result)
}
Output:

<span>New</span>
Example (WithSlots)
package main

import (
	"context"
	"fmt"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<div data-g-define="card"><header data-g-define-slot="header">Default</header><main data-g-define-slot="">Body</main></div>`)

	result, _ := engine.RenderString(context.Background(), "card", nil,
		gotmx.Slot("header", "<h1>Title</h1>"),
		gotmx.Slot("", "<p>Content</p>"),
	)
	fmt.Println(result)
}
Output:

<div><header><h1>Title</h1></header><main><p>Content</p></main></div>

type FileError

type FileError struct {
	Path      string // The file path that caused the error
	Operation string // The operation that failed (e.g., "stat", "open", "read")
	Cause     error  // The underlying error
}

FileError is returned when a file system operation fails. It wraps the underlying error and provides context about which file and operation failed.

func (*FileError) Error

func (e *FileError) Error() string

func (*FileError) Unwrap

func (e *FileError) Unwrap() error

type GoTemplateRegistry

type GoTemplateRegistry interface {
	// RegisterGoTemplate adds a native Go template to the registry.
	// Returns an error if the template cannot be registered.
	RegisterGoTemplate(templateName TemplateName, template string, sourceFile string) error

	// RegisterFunc registers a function that can be used within Go templates.
	// The function must be registered before any templates that use it are parsed.
	RegisterFunc(name string, fun interface{})
}

GoTemplateRegistry is an additional interface that template registries can implement to handle native Go templates. Go templates may reference each other, and in such cases they need to be in the same textTemplate.Template or htmlTemplate.Template instance. Therefore, they need to be treated separately from regular gotmx templates.

Note: Golang templates require you to register any functions BEFORE the templates are actually parsed.

type HasAttributes

type HasAttributes interface {
	SetAttributes(attributes AttributeMap)
}

HasAttributes is an interface for a Renderable that can have attributes.

type HasChildren

type HasChildren interface {
	AddChild(slot string, child Renderable)
	AddChildren(slottedChildren SlottedRenderables)
}

HasChildren is an interface for a Renderable that can have children (in slots).

type InvalidPathError

type InvalidPathError struct {
	Path         string // The problematic path
	ExpectedType string // What was expected: "file" or "directory"
	ActualType   string // What was found: "file" or "directory"
}

InvalidPathError is returned when a file system path is invalid for the expected operation. For example, when a file path is provided where a directory is expected, or vice versa.

func (*InvalidPathError) Error

func (e *InvalidPathError) Error() string

type LazyTemplateLoader

type LazyTemplateLoader interface {
	Init()

	// Load is called by the template registry to lazily load a template.
	// It is intentional that this method does not return those templates. Instead, the template loader must
	// register them with the registry. We do this to have a more flexible approach what the template loader needs to
	// do when a template is requested.
	Load(namespace Namespace, name TemplateName) error
}

type Logger

type Logger interface {
	// Debug logs a message at debug level with optional key-value pairs.
	// This method is used for detailed troubleshooting information.
	Debug(msg string, keysAndValues ...any)

	// Info logs a message at info level with optional key-value pairs.
	// This method is used for general operational information.
	Info(msg string, keysAndValues ...any)

	// Error logs a message at error level with optional key-value pairs.
	// This method is used for error conditions that should be investigated.
	Error(msg string, keysAndValues ...any)
}

Logger is the interface used by gotmx for all logging operations.

The Logger interface provides a consistent logging API within the gotmx library while allowing users to integrate their own logging implementations. This design enables gotmx to work with any logging library or framework that can be adapted to this interface.

Users can provide their own Logger implementation via WithLogger(). If no logger is provided, a default no-operation logger is used, which silently discards all log messages. The slog.Logger from the golang standard library can be used as-is.

Example usage with a custom logger:

type MyCustomLogger struct {
    // Your logger fields
}

func (l *MyCustomLogger) Debug(msg string, keysAndValues ...any) {
    // Your implementation
}

func (l *MyCustomLogger) Info(msg string, keysAndValues ...any) {
    // Your implementation
}

func (l *MyCustomLogger) Error(msg string, keysAndValues ...any) {
    // Your implementation
}

// Then in your application:
engine, _ := gotmx.New(gotmx.WithLogger(&MyCustomLogger{}))

type MaxNestingDepthExceededError

type MaxNestingDepthExceededError struct {
	TemplateName string // The template that was being rendered when the limit was exceeded
	CurrentDepth int    // The current nesting depth when the error occurred
	MaxDepth     int    // The configured maximum allowed depth
}

MaxNestingDepthExceededError is returned when template nesting depth exceeds the configured limit. This typically indicates circular template references (e.g., template A uses template B, and B uses A) or excessively deep template hierarchies.

func (*MaxNestingDepthExceededError) Error

type ModelPathResolver

type ModelPathResolver interface {

	// TryResolve is a convenience function that attempts to extract a model value from an expression.
	// If the expression is not recognized as a path, it returns false as the second argument.
	// If it is a valid path, it returns the resolved model value as the first return value and true as the second return value.
	// For example, the implementation in ModelPathResolverDefault returns false as the second return value if
	// the expression does not start with "[[" and does not end with "]]".
	TryResolve(expression string, data any) (any, bool)

	// Resolve processes the given path and returns the corresponding value from the model.
	// The path parameter should be the actual plain path, without any markers like
	// "[[" prefix or "]]" suffix that are used in the ModelPathResolverDefault implementation.
	Resolve(path string, data any) any
}

ModelPathResolver is an interface that handles the resolution of model paths within templates. It provides methods to extract and resolve values from data models using path expressions. The default implementation is ModelPathResolverDefault, which handles paths enclosed in [[ ]] delimiters.

func NewModelPathResolverDefault

func NewModelPathResolverDefault(
	referenceResolver empaths.ReferenceResolver,
) ModelPathResolver

NewModelPathResolverDefault creates a new ModelPathResolverDefault

type ModelPathResolverDefault

type ModelPathResolverDefault struct {
	// contains filtered or unexported fields
}

ModelPathResolverDefault is a default implementation of ModelPathResolver. it uses the empaths library for resolving model paths: https://github.com/authentic-devel/empaths

func (*ModelPathResolverDefault) Resolve

func (r *ModelPathResolverDefault) Resolve(path string, data any) any

Resolve processes the given path string and returns the corresponding value from the model. The path should be the actual plain path, without "[[" prefix or "]]" suffix.

func (*ModelPathResolverDefault) TryResolve

func (r *ModelPathResolverDefault) TryResolve(path string, data any) (any, bool)

TryResolve attempts to extract a model value from an expression. It returns the resolved value and true if the expression is a valid path (starts with "[[" and ends with "]]"). Otherwise, it returns nil and false.

type Namespace

type Namespace string

Namespace identifies the source of a template, typically the file path it was loaded from. Example: "components/button.html"

func (Namespace) String

func (n Namespace) String() string

type NilComponentError

type NilComponentError struct {
	TemplateName TemplateRef
}

NilComponentError is returned when a template's NewRenderable method returns nil without an error. This indicates a bug in the template implementation.

func (*NilComponentError) Error

func (e *NilComponentError) Error() string

type Option

type Option func(*engineConfig) error

Option configures the Engine during construction.

func WithCustomRegistry

func WithCustomRegistry(registry TemplateRegistry) Option

WithCustomRegistry allows using a custom TemplateRegistry implementation. Most users should not need this.

func WithCustomResolver

func WithCustomResolver(resolver ModelPathResolver) Option

WithCustomResolver allows using a custom ModelPathResolver implementation. Most users should not need this.

func WithDeterministicOutput

func WithDeterministicOutput(enabled bool) Option

WithDeterministicOutput enables sorted attribute output for deterministic HTML. When enabled, HTML attributes are sorted alphabetically on each element. This is useful for testing where you need predictable output for assertions.

By default, attributes are rendered in map iteration order (faster, non-deterministic). Production code should leave this disabled for better performance.

Example:

// For tests:
engine, _ := gotmx.New(gotmx.WithDeterministicOutput(true))

func WithDevDebounce

func WithDevDebounce(d time.Duration) Option

WithDevDebounce sets the debounce duration for dev mode file watching. When template files change rapidly (e.g., during a save-all operation), the engine waits this long after the last change before reloading. This prevents redundant reloads. Default: 1 second. Only effective when WithDevMode(true) is set.

Example:

gotmx.WithDevDebounce(500 * time.Millisecond) // Faster feedback
gotmx.WithDevDebounce(2 * time.Second)        // Less churn

func WithDevMode

func WithDevMode(enabled bool) Option

WithDevMode enables or disables development mode with automatic template reloading. When enabled (true), the engine watches template directories for changes. Should be disabled (false) in production due to overhead. Accepts a boolean for easy integration with config flags:

gotmx.WithDevMode(config.IsDev)
gotmx.WithDevMode(os.Getenv("ENV") == "development")

func WithEagerExtensions

func WithEagerExtensions(exts ...string) Option

WithEagerExtensions specifies which file extensions are parsed at startup. Templates in these files are loaded when New() is called and can be referenced by simple name (e.g., "button" instead of "components/button.htm#button"). Default: []string{".htm"}

func WithFS

func WithFS(fsys fs.FS) Option

WithFS adds an embedded or virtual filesystem as a template source. IMPORTANT: The engine automatically walks the ENTIRE filesystem and discovers all template files (*.htm, *.html) at any depth. You do NOT need to specify where templates are located within the FS - they are found automatically.

This enables the "single embed.FS" pattern where one embed directive with glob patterns captures templates scattered across the codebase:

//go:embed */*.htm */*/*.htm */*/*.html
var templateFs embed.FS

Can be called multiple times to add multiple filesystems if needed. Templates are loaded lazily on first access by default.

func WithIgnore

func WithIgnore(patterns ...string) Option

WithIgnore specifies directory patterns to skip when loading templates. Commonly used to ignore "node_modules", "vendor", ".git", etc.

func WithLazyExtensions

func WithLazyExtensions(exts ...string) Option

WithLazyExtensions specifies which file extensions are loaded on demand. Templates in these files are only parsed when first requested and must be referenced by fully qualified name (e.g., "pages/home.html#home-page"). This enables faster startup for large template sets. Default: []string{".html"}

func WithLogger

func WithLogger(logger Logger) Option

WithLogger sets the logger for all engine components. The logger is automatically propagated to all internal components. Compatible with slog.Logger from the standard library.

func WithMaxNestingDepth

func WithMaxNestingDepth(depth int) Option

WithMaxNestingDepth sets the maximum allowed template nesting depth. This prevents stack overflow from circular template references (e.g., template A uses B, and B uses A).

When a template uses another template via g-use or g-inner-use, the nesting depth increases. If the depth exceeds this limit, rendering will fail with MaxNestingDepthExceededError.

The default is 64, which is sufficient for most use cases while providing protection against accidental circular references. Set to 0 to disable the limit (not recommended).

Example:

// Allow deeper nesting for complex component hierarchies
engine, _ := gotmx.New(gotmx.WithMaxNestingDepth(128))

// Use a lower limit for stricter protection
engine, _ := gotmx.New(gotmx.WithMaxNestingDepth(32))

func WithReloadCallback

func WithReloadCallback(fn func(err error)) Option

WithReloadCallback sets a callback that is invoked after every dev mode template reload. If the reload succeeded, err is nil. If it failed, err describes what went wrong. The previous templates remain intact on failure (see atomic reload). Only effective when WithDevMode(true) is set.

Example:

gotmx.WithReloadCallback(func(err error) {
    if err != nil {
        log.Printf("Template reload failed: %v", err)
    } else {
        log.Println("Templates reloaded successfully")
    }
})

func WithTemplateDir

func WithTemplateDir(dir string) Option

WithTemplateDir adds a directory as a template source. Can be called multiple times to add multiple directories - templates can be scattered across the codebase (e.g., next to their corresponding Go code). In dev mode: all directories are watched for changes. In prod mode: templates are loaded lazily on first access.

type RenderContext

type RenderContext struct {
	// Context is Go's standard context.Context for request cancellation, timeouts,
	// and passing request-scoped values through the rendering pipeline.
	// HTTP handlers should pass r.Context() to enable proper request lifecycle handling.
	Context context.Context

	// ResolveText resolves a value (which may contain model path expressions like "[[ .Name ]]")
	// to a string. The value is evaluated against the provided data context.
	// If escaped is true, HTML special characters in the result are escaped for XSS protection.
	//
	// Example: ResolveText("[[ .User.Name ]]", userData, true) might return "John &amp; Jane"
	ResolveText func(value string, data any, escaped bool) (string, error)

	// ResolveValue resolves a value as a model path expression and returns the raw result.
	// Returns (resolved value, true) if the value was a model path expression that was resolved,
	// or (original value, false) if the value was not a model path expression.
	//
	// This is useful when you need the actual typed value rather than a string representation,
	// for example when resolving iteration targets or conditional expressions.
	//
	// Example: ResolveValue("[[ .Items ]]", data) might return ([]Item{...}, true)
	ResolveValue func(value string, data any) (any, bool)

	// CreateRenderable creates a new Renderable for the given template reference.
	// The template reference can be either a simple name (e.g., "myTemplate") or a
	// fully qualified name with namespace (e.g., "templates/page.html#myTemplate").
	//
	// Returns an error if the template is not found or if component creation fails.
	CreateRenderable func(name TemplateRef, data any) (Renderable, error)

	// Logger provides logging capabilities during rendering for debug, info, and error messages.
	// This is the same logger configured on the Engine instance.
	Logger Logger

	// CurrentNestingDepth tracks the current depth of nested template rendering.
	// This is incremented when entering a nested template (via g-use) and decremented when exiting.
	// Used to prevent stack overflow from circular template references.
	CurrentNestingDepth int

	// MaxNestingDepth is the maximum allowed nesting depth for template rendering.
	// If CurrentNestingDepth reaches this limit, rendering will fail with MaxNestingDepthExceededError.
	// A value of 0 means no limit (not recommended). Default is 64.
	MaxNestingDepth int

	// DeterministicOutput controls whether HTML attributes are sorted alphabetically.
	// When true, attributes are sorted for predictable output (useful for testing).
	// When false (default), attributes render in map iteration order (faster).
	DeterministicOutput bool

	// Escaped controls whether text content is HTML-escaped by default.
	// This is set once at the start of rendering based on the render options.
	// Individual attributes may override this — for example, g-inner-text always
	// escapes regardless of this flag, while g-inner-html never escapes.
	Escaped bool

	// CurrentTemplate tracks the template being rendered, for error diagnostics.
	// Updated when entering a new template via g-use or g-inner-use.
	CurrentTemplate string
}

RenderContext provides the rendering capabilities needed during template rendering. A single context is created when rendering begins (via Engine.Render or Engine.RenderString) and passed by pointer through the entire rendering tree. This design:

  • Avoids per-node allocations (only one context object is created per render call)
  • Decouples Renderable implementations from Engine, breaking bi-directional coupling
  • Makes testing easier by allowing mock implementations of individual functions

Implementations of Renderable should NOT create new RenderContext instances; they should pass the same context pointer to any child renderables they create or invoke.

type RenderError

type RenderError struct {
	Template  string // The template being rendered (may be empty if unknown)
	Element   string // The HTML element tag name (e.g., "div", "span")
	Attribute string // The attribute being processed when the error occurred (may be empty)
	Cause     error  // The underlying error
}

RenderError provides context about where an error occurred during rendering. It includes the template name, element tag, and optionally the attribute that caused the error. This makes debugging template issues much easier by showing the exact location in the template hierarchy where the error occurred.

func (*RenderError) Error

func (e *RenderError) Error() string

func (*RenderError) Unwrap

func (e *RenderError) Unwrap() error

type RenderOption

type RenderOption func(*renderConfig)

RenderOption configures individual render calls.

func Slot

func Slot(name string, content any) RenderOption

Slot is a convenience function for creating a single-slot Slots map.

func Unescaped

func Unescaped() RenderOption

Unescaped disables HTML escaping for the render call. Use only when rendering trusted content. By default, all text is HTML-escaped.

Example:

engine.Render(ctx, w, "template", trustedData, gotmx.Unescaped())
Example
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`<div data-g-define="raw" data-g-inner-html="[[ .Html ]]">placeholder</div>`)

	// Use g-inner-html for trusted HTML content
	engine.Render(context.Background(), os.Stdout, "raw", map[string]any{
		"Html": "<strong>Bold</strong>",
	})
}
Output:

<div><strong>Bold</strong></div>

func WithLayout

func WithLayout(layoutTemplate string, layoutData any) RenderOption

WithLayout wraps the rendered template inside a layout template. The rendered content is placed into the layout's default slot (empty name). Use WithLayoutSlot to target a named slot instead.

Example:

engine.Render(ctx, w, "dashboard-page", pageData,
    gotmx.WithLayout("main-layout", layoutData),
)
Example
package main

import (
	"context"
	"os"

	"github.com/authentic-devel/gotmx"
)

func main() {
	engine, _ := gotmx.New()
	engine.LoadHTML(`
		<html data-g-define="base"><head><title data-g-inner-text="[[ .Title ]]">T</title></head><body data-g-define-slot="">default</body></html>
		<article data-g-define="page">Welcome!</article>
	`)

	engine.Render(context.Background(), os.Stdout, "page", nil,
		gotmx.WithLayout("base", map[string]any{"Title": "Home"}),
	)
}
Output:

<!DOCTYPE html>
<html><head><title>Home</title></head><body><article>Welcome!</article></body></html>

func WithLayoutSlot

func WithLayoutSlot(slotName string) RenderOption

WithLayoutSlot sets which slot in the layout receives the rendered content. By default, content goes to the default slot (empty name). Must be used together with WithLayout.

Example:

engine.Render(ctx, w, "dashboard-page", pageData,
    gotmx.WithLayout("main-layout", layoutData),
    gotmx.WithLayoutSlot("content"),
)

func WithSlots

func WithSlots(slots Slots) RenderOption

WithSlots provides slot content for the render. Slot values can be:

  • string: rendered as HTML content
  • Renderable: rendered using its Render method
  • []Renderable: all items rendered in sequence

type RenderType

type RenderType int

RenderType controls whether a Renderable outputs its outer HTML tags or only its content.

const (
	// RenderInner renders only the inner content of the element, without the opening/closing tags.
	// Used by g-inner-use to embed a template's content without its root element.
	RenderInner RenderType = iota

	// RenderOuter renders the complete element including its opening and closing tags.
	// This is the default render type used for most rendering operations.
	RenderOuter
)

type Renderable

type Renderable interface {
	// Render outputs the HTML representation to the provided writer.
	//
	// Parameters:
	//   - ctx: The render context providing resolution and component creation functions.
	//          This is created once per render call and should be passed unchanged to children.
	//          Escaping is controlled by ctx.Escaped.
	//   - writer: Destination for the rendered HTML output.
	//   - renderType: Controls whether to render outer tags (RenderOuter) or content only (RenderInner).
	//
	// Returns an error if rendering fails at any point.
	Render(ctx *RenderContext, writer io.Writer, renderType RenderType) error
}

Renderable represents something that can be rendered to HTML output. Built-in implementations handle HTML nodes, text content, Go templates, and literal strings.

The RenderContext parameter is created once at the start of rendering and passed through the entire rendering tree. Implementations should pass this same context to any child renderables they invoke, without creating new context instances.

Escaping behavior is controlled by ctx.Escaped rather than a method parameter. Specific attributes enforce their own escaping rules: g-inner-text always escapes, g-inner-html never escapes, and attribute values always escape.

type Slots

type Slots map[string]any

Slots is a map of slot names to content. Content can be:

  • string: rendered as HTML content
  • Renderable: rendered using its Render method
  • []Renderable: all items rendered in sequence

Slots supports a fluent API for building slot content:

slots := gotmx.Slots{}.
    Set("header", headerComponent).
    Set("content", bodyComponent).
    Add("sidebar", widget1).
    Add("sidebar", widget2)

func (Slots) Add

func (s Slots) Add(name string, r Renderable) Slots

Add appends a Renderable to a named slot. Unlike Set, this preserves existing content in the slot. Returns the Slots map for chaining.

func (Slots) Set

func (s Slots) Set(name string, content any) Slots

Set replaces the content of a named slot. Returns the Slots map for chaining.

type SlottedRenderables

type SlottedRenderables map[string][]Renderable

SlottedRenderables maps slot names to their renderable content.

type Template

type Template interface {

	// Name returns the unique name of the template within its namespace.
	// This must always return a constant value. Unpredictable behavior will occur if it returns
	// a different value after the template has been registered.
	Name() TemplateName

	// Namespace returns the namespace of the template. Within a namespace, the name must be unique.
	// Typically, the namespace is the file path the template came from, e.g., an HTML file.
	// The fully qualified name of a template is the namespace and the name separated by a hash,
	// for example "frontend/index.html#myTemplate".
	// This must always return a constant value. Unpredictable behavior will occur if it returns
	// a different value after the template has been registered.
	Namespace() Namespace

	// NewRenderable creates a new Renderable instance from this template with the given data.
	// The data parameter provides the model context for rendering.
	NewRenderable(data any) (Renderable, error)
}

Template is a blueprint for creating Renderable instances. Each Template has a unique name within its namespace and can create Renderable instances with the provided data.

func NewStringLiteralTemplate

func NewStringLiteralTemplate(name TemplateName, literal string, namespace Namespace) Template

NewStringLiteralTemplate creates a template that is defined by a string literal. When rendered, it does not substitute any placeholders. The string literal is rendered as is.

type TemplateLoadError

type TemplateLoadError struct {
	TemplateName TemplateName
	Namespace    Namespace
	Cause        error
}

TemplateLoadError is returned when a template fails to load. It wraps the underlying error and provides context about which template failed.

func (*TemplateLoadError) Error

func (e *TemplateLoadError) Error() string

func (*TemplateLoadError) Unwrap

func (e *TemplateLoadError) Unwrap() error

type TemplateName

type TemplateName string

TemplateName is the simple name of a template within its namespace. Examples: "my-button", "dashboard-page"

func (TemplateName) String

func (n TemplateName) String() string

type TemplateNotFoundError

type TemplateNotFoundError struct {
	Name       TemplateName
	Namespace  Namespace
	Available  []TemplateName
	DidYouMean TemplateName
}

TemplateNotFoundError is returned when a template cannot be found in the registry. It includes helpful context for debugging: the list of available templates and a "did you mean" suggestion based on Levenshtein distance.

func (*TemplateNotFoundError) Error

func (e *TemplateNotFoundError) Error() string

type TemplateRef

type TemplateRef string

TemplateRef is a reference to a template, either simple or fully qualified with namespace. Simple: "my-button" Qualified: "components/button.html#my-button"

func (TemplateRef) String

func (n TemplateRef) String() string

type TemplateRegistry

type TemplateRegistry interface {
	// GetTemplate returns the template with the given name or an error if no
	// matching template exists or if the template name is ambiguous.
	GetTemplate(ref TemplateRef) (Template, error)

	// RegisterTemplate adds a new template to the registry.
	// Returns an error if the template cannot be registered (e.g., duplicate template name in the same namespace).
	RegisterTemplate(template Template) error

	// ClearTemplates removes all templates from the registry.
	ClearTemplates()

	SetLazyTemplateLoader(ltl LazyTemplateLoader)
}

TemplateRegistry is an interface for managing templates within the gotmx engine. It provides methods for registering, retrieving, and managing templates.

type TemplateRegistryDefault

type TemplateRegistryDefault struct {
	// contains filtered or unexported fields
}

TemplateRegistryDefault is a default implementation of TemplateRegistry and also implements GoTemplateRegistry It supports

  • registering any type of template that implements the Template interface
  • parsing HTML files from the filesystem or other sources using the golang builtin HTML parser and automatically registering any found template definitions as NodeTemplate
  • registering golang templates

Thread Safety: This registry is safe for concurrent use. All template access and registration operations are protected by an internal mutex.

func NewTemplateRegistryDefault

func NewTemplateRegistryDefault() *TemplateRegistryDefault

NewTemplateRegistryDefault creates a new ModelPathResolverDefault

func (*TemplateRegistryDefault) ClearTemplates

func (tr *TemplateRegistryDefault) ClearTemplates()

func (*TemplateRegistryDefault) GetTemplate

func (tr *TemplateRegistryDefault) GetTemplate(templateRef TemplateRef) (Template, error)

GetTemplate returns the template with the given name or an error if no matching template exists or if the template name is ambiguous. The name can either be an unqualified template name without namespace like "myTemplate" or a fully qualified name with namespace like "frontend/templates/index.html#myTemplate". If an unqualified name is given, then that name must be globally unique across all namespaces. Otherwise, an error will be returned.

func (*TemplateRegistryDefault) GetTemplateExt

func (tr *TemplateRegistryDefault) GetTemplateExt(namespace Namespace, templateName TemplateName) (Template, error)

GetTemplateExt returns a template by namespace and name, with extended lookup logic. Returns an error if the template is not found, is ambiguous, or if loading fails.

func (*TemplateRegistryDefault) LogTemplates

func (tr *TemplateRegistryDefault) LogTemplates()

func (*TemplateRegistryDefault) RegisterFunc

func (tr *TemplateRegistryDefault) RegisterFunc(name string, f interface{})

func (*TemplateRegistryDefault) RegisterGoTemplate

func (tr *TemplateRegistryDefault) RegisterGoTemplate(name TemplateName, template string, sourceFile string) error

func (*TemplateRegistryDefault) RegisterTemplate

func (tr *TemplateRegistryDefault) RegisterTemplate(template Template) error

func (*TemplateRegistryDefault) ReplaceFrom

func (tr *TemplateRegistryDefault) ReplaceFrom(source *TemplateRegistryDefault)

ReplaceFrom atomically replaces all templates in this registry with the templates from source. This is used by dev mode to perform zero-downtime reloads: a new registry is built in isolation, then swapped in with a single write-lock acquisition. Concurrent readers never see an empty registry.

func (*TemplateRegistryDefault) SetLazyTemplateLoader

func (tr *TemplateRegistryDefault) SetLazyTemplateLoader(ltl LazyTemplateLoader)

func (*TemplateRegistryDefault) SetLogger

func (tr *TemplateRegistryDefault) SetLogger(logger Logger)

type TemplateRetrievalError

type TemplateRetrievalError struct {
	TemplateName TemplateRef
	Cause        error
}

TemplateRetrievalError is returned when there's a failure retrieving a template from the registry. It wraps the underlying error (which could be TemplateNotFoundError, AmbiguousTemplateError, etc.).

func (*TemplateRetrievalError) Error

func (e *TemplateRetrievalError) Error() string

func (*TemplateRetrievalError) Unwrap

func (e *TemplateRetrievalError) Unwrap() error

type TemplateSourceNotFoundError

type TemplateSourceNotFoundError struct {
	Name      TemplateName
	Namespace Namespace
	Sources   []string // Description of sources that were searched
}

TemplateSourceNotFoundError is returned when a template cannot be found in any of the configured template sources (file systems or directories).

func (*TemplateSourceNotFoundError) Error

type VoidElementChildError

type VoidElementChildError struct {
	Element string // The void element tag name (e.g., "br", "img")
}

VoidElementChildError is returned when a void HTML element (like <br>, <img>, etc.) unexpectedly has child nodes. Void elements cannot have children according to the HTML spec.

func (*VoidElementChildError) Error

func (e *VoidElementChildError) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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