templar

package module
v0.0.27 Latest Latest
Warning

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

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 19 Imported by: 5

README

Templar: Go Template Loader

Go Reference

Templar is a powerful extension to Go's standard templating libraries that adds dependency management, simplifies template composition, and solves common pain points in template organization.

Why Templar?

Templar is designed to integrate smoothly with Go's standard templating libraries while solving common issues:

  1. Minimal Learning Curve: If you know Go templates, you already know 99% of Templar.
  2. Zero New Runtime Syntax: The include directives are processed before rendering (variable based inclusion in the works).
  3. Flexible and Extensible: Create custom loaders for any template source (file loader for now, more in the works).
  4. Production Ready: Handles complex dependencies, prevents cycles, and provides clear error messages (and aiming to get better at this).

Background

Go's built-in templating libraries (text/template and html/template) are powerful but have limitations when working with complex template structures:

  1. No Native Dependency Management: When templates reference other templates, you must manually ensure they're loaded in the correct order.

  2. Global Template Namespace: All template definitions share a global namespace, making it challenging to use different versions of the same template in different contexts.

  3. Brittle Template Resolution: In large applications, templates often load differently in development vs. production environments.

  4. Verbose Template Loading: Loading templates with their dependencies typically requires repetitive boilerplate code:

// Standard approach - verbose and error-prone
func renderIndexPage(w http.ResponseWriter, r *http.Request) {
  t := template.ParseFiles("Base1.tmpl", "a2.tmpl", "IndexPage.tmpl")
  t.Execute(w, data)
}

func renderProductListPage(w http.ResponseWriter, r *http.Request) {
  t := template.ParseFiles("AnotherBase.tmpl", "a2.tmpl", "ProductListPage.tmpl")
  t.Execute(w, data)
}

Proposal

Templar solves these problems by providing:

  1. Dependency Declaration: Templates can declare their own dependencies using a simple include syntax:
{{# include "base.tmpl" #}}
{{# include "components/header.tmpl" #}}

<div class="content">
  {{ template "content" . }}
</div>
  1. Automatic Template Loading: Templar automatically loads and processes all dependencies:
// With Templar - clean and maintainable
func renderIndexPage(w http.ResponseWriter, r *http.Request) {
  tmpl := loadTemplate("IndexPage.tmpl")  // Dependencies automatically handled
  tmpl.Execute(w, data)
}
  1. Flexible Template Resolution: Multiple loaders can be configured to find templates in different locations.

  2. Template Reuse: The same template name can be reused in different contexts without conflict.

Getting Started

Basic Example
package main

import (
    "os"
    "github.com/panyam/templar"
)

func main() {
  // Create a template group
  group := templar.NewTemplateGroup()
  
  // Create a filesystem loader that searches multiple directories
  group.Loader = templar.NewFileSystemLoader(
      "templates/",
      "templates/shared/",
  )
  
  // Load a root template (dependencies handled automatically)
  rootTemplate := group.MustLoad("pages/homepage.tmpl", "")

  // Prepare data for the template
  data := map[string]any{
    "Title": "Home Page",
    "User": User{
      ID:   1,
      Name: "John Doe",
    },
    "Updates": []Update{
      {Title: "New Feature Released", Date: "2023-06-15"},
      {Title: "System Maintenance", Date: "2023-06-10"},
      {Title: "Welcome to our New Site", Date: "2023-06-01"},
    },
    "Featured": FeaturedContent{
      Title:       "Summer Sale",
      Description: "Get 20% off on all products until July 31st!",
      URL:         "/summer-sale",
    },
  }

  // Render the template to stdout (for this example)
  if err = group.RenderHtmlTemplate(os.Stdout, rootTemplate[0], "", data, nil); err != nil {
    fmt.Printf("Error rendering template: %v\n", err)
  }
}

Key Features

1. Template Dependencies

In your templates, use the {{# include "path/to/template" #}} directive to include dependencies:

{{# include "layouts/base.tmpl" #}}
{{# include "components/navbar.tmpl" #}}

{{ define "content" }}
  <h1>Welcome to our site</h1>
  <p>This is the homepage content.</p>
{{ end }}

You can also selectively include only specific templates (tree-shaking):

{{# include "forms.tmpl" "button" "input" #}}

{{ define "page" }}
  {{ template "button" . }}  {{/* Only button and input are included */}}
{{ end }}
2. Template Namespacing

Avoid template name collisions by importing templates into namespaces:

{{# namespace "UI" "widgets/buttons.tmpl" #}}
{{# namespace "Theme" "themes/bootstrap.tmpl" #}}

{{ define "page" }}
  {{ template "UI:button" dict "Text" "Click Me" }}
  {{ template "Theme:header" . }}
{{ end }}

Namespace resolution rules:

  • Plain names like button are prefixed with the current namespace → NS:button
  • Names with : like Other:button are kept as-is (cross-namespace reference)
  • Names starting with :: like ::global become global (no namespace)

Tree-shaking is also supported with namespaces:

{{# namespace "UI" "widgets.tmpl" "button" "icon" #}}
{{/* Only button, icon, and their dependencies are included */}}

See namespace.md for detailed examples, the diamond problem, and common gotchas.

3. Template Extension (Inheritance)

Extend base templates while overriding specific blocks:

{{# namespace "Base" "layouts/base.tmpl" #}}
{{# extend "Base:layout" "MyLayout" "Base:title" "myTitle" "Base:content" "myContent" #}}

{{ define "myTitle" }}Custom Page Title{{ end }}
{{ define "myContent" }}
  <h1>My Custom Content</h1>
{{ end }}

{{ template "MyLayout" . }}

This creates a new template MyLayout by copying Base:layout, but rewiring:

  • {{ template "Base:title" . }}{{ template "myTitle" . }}
  • {{ template "Base:content" . }}{{ template "myContent" . }}

Non-overridden blocks retain their original references to the base templates.

Important: The extend directive only rewrites template calls within the copied template itself, not in templates it calls. For nested overrides, you need to extend each level of the hierarchy. See extend.md for detailed examples, visual diagrams, and common gotchas.

4. Multiple Template Loaders

Templar allows you to configure multiple template loaders with fallback behavior:

// Create a list of loaders to search in order
loaderList := &templar.LoaderList{}

// Add loaders in priority order
loaderList.AddLoader(templar.NewFileSystemLoader("app/templates/"))
loaderList.AddLoader(templar.NewFileSystemLoader("shared/templates/"))

// Set a default loader as final fallback
loaderList.DefaultLoader = templar.NewFileSystemLoader("default/templates/")
5. Template Groups

Template groups manage collections of templates and their dependencies:

group := templar.NewTemplateGroup()
group.Loader = loaderList
group.AddFuncs(map[string]any{
    "formatDate": func(t time.Time) string {
        return t.Format("2006-01-02")
    },
})
6. External Template Sources (Vendoring)

Load templates from external sources like GitHub repositories:

# templar.yaml
sources:
  goapplib:
    url: github.com/panyam/goapplib
    path: templates
    ref: v1.2.0

vendor_dir: ./templar_modules
search_paths:
  - ./templates
  - ./templar_modules

Reference external templates with the @sourcename prefix:

{{# namespace "EL" "@goapplib/components/EntityListing.html" #}}

{{ define "MyPage" }}
    {{ template "EL:EntityListing" .Items }}
{{ end }}

Fetch dependencies with:

templar get              # Fetch all sources
templar get --update     # Update to latest versions
templar get --verify     # Verify local matches lock file

See vendoring.md for deployment strategies, configuration reference, and examples.

Advanced Usage

Conditional Template Loading

You can implement conditional template loading based on application state:

folder := "desktop"
if isMobile {
  folder = "mobile"
}
tmpl, err := loader.Load(fmt.Sprintf("%s/homepage.tmpl", folder))
Dynamic Templates

Generate templates dynamically and use them immediately:

dynamicTemplate := &templar.Template{
    Name:      "dynamic-template",
    RawSource: []byte(`Hello, {{.Name}}!`),
}

group.RenderTextTemplate(w, dynamicTemplate, "", map[string]any{"Name": "World"}, nil)

Command Line Interface

Templar provides a CLI tool for serving templates, debugging dependencies, and managing external sources:

# Install
go install github.com/panyam/templar/cmd/templar@latest

# Initialize a new project
templar init

# Fetch external template sources
templar get

# List configured sources
templar sources

# Start development server
templar serve -t ./templates -s /static:./public

# Debug template dependencies
templar debug -p templates homepage.html

Key commands:

  • templar init - Create a templar.yaml configuration file
  • templar get - Fetch external template sources for vendoring
  • templar sources - List configured sources and their status
  • templar serve - Start HTTP server to serve and test templates
  • templar debug - Analyze dependencies, detect cycles, visualize with GraphViz
  • templar version - Print version information

Configuration via .templar.yaml or environment variables (TEMPLAR_ prefix).

See cli.md for complete command reference, flags, configuration options, and examples.

Comparison with Other Solutions

Feature Standard Go Templates Templar
Dependency Management
Self-describing Templates (*)
Template Namespacing
Template Extension/Inheritance
Tree-shaking
Standard Go Template Syntax ��
Supports Cycles Prevention (**)
HTML Escaping
Template Grouping (***) ⚠️ Partial

*: Self-describing here refers to a template specifying all the dependencies it needs so a template author can be clear about what is required and include them instead of hoping they exist somehow. **: Cycles are caught by the preprocessor and is clearer. ***: Grouping in standard templates is done in code by the template user instead of the author.

Other alternatives
  • Pongo2 is amazing for its reverence for Django syntax.
  • Templ is amazing as a typed template library and being able to perform compile time validations of templates.

My primary goal here was to have as much alignment with Go's template stdlib. Beyond this library for managing dependencies, the goal itself was to have strict adherence to Go's templating syntax. Using the same Go template syntax also allows extra features during preprocessing of templates. (eg using same set of variables for both pre-processing as well as for final rendering).

Documentation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var TemplateNotFound = errors.New("template not found")

TemplateNotFound is returned when a template could not be found by a loader.

Functions

func ApplyNamespaceToTree added in v0.0.23

func ApplyNamespaceToTree(tree *parse.Tree, namespace string)

ApplyNamespaceToTree applies a namespace transformation to all template references within a parse tree. It modifies the tree in place.

This transforms:

  • {{ template "foo" }} → {{ template "NS:foo" }}
  • {{ template "Other:bar" }} → {{ template "Other:bar" }} (unchanged)
  • {{ template "::global" }} → {{ template "global" }}

func CollectLocalReferences added in v0.0.23

func CollectLocalReferences(tree *parse.Tree) []string

CollectLocalReferences collects all local (non-namespaced, non-global) template references from a parse tree. These are the references that would be transformed when applying a namespace.

func CollectTemplateNames added in v0.0.23

func CollectTemplateNames(tree *parse.Tree) []string

CollectTemplateNames walks a parse tree and collects all template names that are referenced via {{ template "name" }} calls.

func ComputeReachableTemplates added in v0.0.23

func ComputeReachableTemplates(templates map[string]*parse.Tree, entryPoints []string) map[string]bool

ComputeReachableTemplates computes the transitive closure of templates reachable from the given entry points. This is used for tree-shaking: only namespace templates that are actually used.

Parameters:

  • templates: map of template name to parse tree (all available templates)
  • entryPoints: starting template names to trace from

Returns: set of template names that are reachable (including entry points)

func CopyTreeWithNamespace added in v0.0.23

func CopyTreeWithNamespace(tree *parse.Tree, namespace string) *parse.Tree

CopyTreeWithNamespace creates a deep copy of a parse tree and applies namespace transformation to both the tree name and all template references.

func CopyTreeWithRewrites added in v0.0.23

func CopyTreeWithRewrites(tree *parse.Tree, rewrites map[string]string) *parse.Tree

CopyTreeWithRewrites creates a deep copy of a parse tree and rewrites template references according to the provided mapping.

Parameters:

  • tree: the source parse tree to copy
  • rewrites: map of old name -> new name for template references

Returns: a new tree with references rewritten

func CreateDelegationTree added in v0.0.23

func CreateDelegationTree(treeName string, delegateTo string) *parse.Tree

CreateDelegationTree creates a parse tree that simply delegates to another template. The resulting tree, when executed, will call {{ template "delegateTo" . }}

This is used for template extension: when a child template overrides a block, we replace the base template's block with a delegation to the child's definition.

func FetchAllSources added in v0.0.27

func FetchAllSources(config *VendorConfig) (map[string]*FetchResult, error)

FetchAllSources fetches all sources defined in the config

func FindVendorConfig added in v0.0.27

func FindVendorConfig(startDir string) (string, error)

FindVendorConfig searches for templar.yaml starting from the given directory and walking up to parent directories until found or root is reached.

func IsLocalReference added in v0.0.23

func IsLocalReference(name string) bool

IsLocalReference returns true if the name is a local reference (not namespaced, not global). Local references are plain names like "header" that should be namespaced. Non-local references include:

  • "NS:header" (already namespaced)
  • "::header" (explicitly global)

func TransformName added in v0.0.23

func TransformName(name, namespace string) string

TransformName applies namespace resolution rules to a template reference name.

Resolution rules:

  • If name starts with "::" → strip "::", return as global (no namespace)
  • If name contains ":" → return unchanged (explicit cross-namespace reference)
  • Otherwise → prepend namespace (e.g., "icon" → "NS:icon")

func WalkParseTree added in v0.0.23

func WalkParseTree(node parse.Node, visitor func(*parse.TemplateNode))

WalkParseTree walks a parse tree and calls the visitor function for each TemplateNode. The visitor can modify the node's Name field to apply namespace transformations.

func WriteLockFile added in v0.0.27

func WriteLockFile(path string, lock *VendorLock) error

WriteLockFile writes a VendorLock to the specified path

Types

type EmbedFSLoader added in v0.0.21

type EmbedFSLoader struct {
	// Embeds is a list of directories to search for templates.
	Embeds []embed.FS

	// Extensions is a list of file extensions to consider as templates.
	Extensions []string
}

EmbedFSLoader loads templates from the file system based on a set of directories and file extensions.

func NewEmbedFSLoader added in v0.0.21

func NewEmbedFSLoader(fss ...embed.FS) *EmbedFSLoader

NewEmbedFSLoader creates a new file system loader that will search in the provided folders for template files. By default, it recognizes files with .tmpl, .tmplus, and .html extensions.

func (*EmbedFSLoader) Load added in v0.0.21

func (g *EmbedFSLoader) Load(name string, _ string) (template []*Template, err error)

Load attempts to find and load a template with the given name. If the name includes an extension, only files with that extension are considered. Otherwise, files with any of the loader's recognized extensions are searched. The cwd parameter is ignored as we can only provided templates from embedded FS Returns the loaded templates or TemplateNotFound if no matching templates were found.

type Extension added in v0.0.23

type Extension struct {
	// SourceTemplate is the template to copy from (e.g., "Base:layout")
	SourceTemplate string

	// DestTemplate is the name for the new template (e.g., "Page:layout")
	DestTemplate string

	// Rewrites maps block names to their replacements.
	// Key is the original reference, value is the replacement.
	Rewrites map[string]string
}

Extension represents an extend directive that creates a new template by copying a source template and rewiring specific template references.

Syntax: {{# extend "SourceTemplate" "DestTemplate" "block1" "override1" "block2" "override2" ... #}}

This creates DestTemplate as a copy of SourceTemplate, but with:

  • {{ template "block1" . }} replaced with {{ template "override1" . }}
  • {{ template "block2" . }} replaced with {{ template "override2" . }}

type FetchResult added in v0.0.27

type FetchResult struct {
	SourceName     string
	URL            string
	Ref            string
	ResolvedCommit string
	DestDir        string
	FetchedAt      time.Time
}

FetchResult contains the result of fetching a source

func FetchSource added in v0.0.27

func FetchSource(config *VendorConfig, sourceName string) (*FetchResult, error)

FetchSource fetches a single source from the config

type FileSystemLoader

type FileSystemLoader struct {
	// Folders is a list of directories to search for templates.
	Folders []string

	// Extensions is a list of file extensions to consider as templates.
	Extensions []string
}

FileSystemLoader loads templates from the file system based on a set of directories and file extensions.

func NewFileSystemLoader

func NewFileSystemLoader(folders ...string) *FileSystemLoader

NewFileSystemLoader creates a new file system loader that will search in the provided folders for template files. By default, it recognizes files with .tmpl, .tmplus, and .html extensions.

func (*FileSystemLoader) Load

func (g *FileSystemLoader) Load(name string, cwd string) (template []*Template, err error)

Load attempts to find and load a template with the given name. If the name includes an extension, only files with that extension are considered. Otherwise, files with any of the loader's recognized extensions are searched. If cwd is provided, it's used for resolving relative paths. Returns the loaded templates or TemplateNotFound if no matching templates were found.

type LoaderList

type LoaderList struct {
	// DefaultLoader is used as a fallback if no other loaders succeed.
	DefaultLoader TemplateLoader
	// contains filtered or unexported fields
}

LoaderList is a composite loader that tries multiple loaders in sequence and returns the first successful match.

func (*LoaderList) AddLoader

func (t *LoaderList) AddLoader(loader TemplateLoader) *LoaderList

AddLoader adds a new loader to the list of loaders to try. Returns the updated LoaderList for method chaining.

func (*LoaderList) Load

func (t *LoaderList) Load(name string, cwd string) (matched []*Template, err error)

Load attempts to load a template with the given name by trying each loader in sequence. It returns the first successful match, or falls back to the DefaultLoader if all others fail. If cwd is provided, it's used for resolving relative paths. Returns TemplateNotFound if no loader can find the template.

type LockedSource added in v0.0.27

type LockedSource struct {
	URL            string `yaml:"url"`
	Ref            string `yaml:"ref"`
	ResolvedCommit string `yaml:"resolved_commit"`
	FetchedAt      string `yaml:"fetched_at"`
}

LockedSource represents a locked source in the lock file

type MemDelta added in v0.0.24

type MemDelta struct {
	FromName         string
	ToName           string
	Duration         time.Duration
	AllocDelta       int64
	TotalAllocDelta  int64
	HeapObjectsDelta int64
	HeapInuseDelta   int64
	NumGCDelta       int32
}

MemDelta represents the difference between two memory snapshots.

func NewMemDelta added in v0.0.24

func NewMemDelta(from, to *MemSnapshot) *MemDelta

NewMemDelta calculates the delta between two snapshots.

func (*MemDelta) String added in v0.0.24

func (d *MemDelta) String() string

String returns a human-readable summary of the delta.

type MemSnapshot added in v0.0.24

type MemSnapshot struct {
	// Name identifies this snapshot (e.g., "before-load", "after-render")
	Name string

	// Timestamp when the snapshot was taken
	Timestamp time.Time

	// Alloc is bytes of allocated heap objects.
	// This is the most useful metric for tracking "live" memory.
	Alloc uint64

	// TotalAlloc is cumulative bytes allocated (never decreases).
	// Useful for tracking allocation pressure.
	TotalAlloc uint64

	// HeapObjects is the number of allocated heap objects.
	HeapObjects uint64

	// HeapInuse is bytes in in-use spans.
	HeapInuse uint64

	// NumGC is the number of completed GC cycles.
	NumGC uint32

	// PauseTotalNs is cumulative nanoseconds in GC stop-the-world pauses.
	PauseTotalNs uint64
}

MemSnapshot captures memory statistics at a point in time.

type MemStats added in v0.0.24

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

MemStats collects memory snapshots for analysis.

func NewMemStats added in v0.0.24

func NewMemStats() *MemStats

NewMemStats creates a new memory statistics collector.

func (*MemStats) Delta added in v0.0.24

func (m *MemStats) Delta(fromName, toName string) *MemDelta

Delta calculates the difference between two named snapshots. Returns nil if either snapshot is not found.

func (*MemStats) Report added in v0.0.24

func (m *MemStats) Report(w io.Writer)

Report writes a formatted report of all snapshots to the writer.

func (*MemStats) Reset added in v0.0.24

func (m *MemStats) Reset()

Reset clears all captured snapshots.

func (*MemStats) Snapshot added in v0.0.24

func (m *MemStats) Snapshot(name string) *MemSnapshot

Snapshot captures current memory statistics with the given name. Call this before and after operations you want to measure.

func (*MemStats) SnapshotWithGC added in v0.0.24

func (m *MemStats) SnapshotWithGC(name string) *MemSnapshot

SnapshotWithGC forces a garbage collection before taking the snapshot. This gives a more accurate picture of "live" memory but is slower.

func (*MemStats) Snapshots added in v0.0.24

func (m *MemStats) Snapshots() []*MemSnapshot

Snapshots returns all captured snapshots.

type SourceConfig added in v0.0.27

type SourceConfig struct {
	URL  string `yaml:"url"`
	Path string `yaml:"path"`
	Ref  string `yaml:"ref"`
}

SourceConfig represents a single external template source configuration

type SourceLoader added in v0.0.27

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

SourceLoader is a template loader that resolves @source prefixed paths to vendored template locations, while falling back to a FileSystemLoader for regular paths.

func NewSourceLoader added in v0.0.27

func NewSourceLoader(config *VendorConfig) *SourceLoader

NewSourceLoader creates a new SourceLoader with the given configuration.

func NewSourceLoaderFromConfig added in v0.0.27

func NewSourceLoaderFromConfig(configPath string) (*SourceLoader, error)

NewSourceLoaderFromConfig creates a SourceLoader from a config file path. It loads the config, resolves all paths relative to the config file location, and creates the appropriate loader.

func NewSourceLoaderFromDir added in v0.0.27

func NewSourceLoaderFromDir(dir string) (*SourceLoader, error)

NewSourceLoaderFromDir finds templar.yaml starting from the given directory and creates a SourceLoader from it.

func (*SourceLoader) Load added in v0.0.27

func (s *SourceLoader) Load(pattern string, cwd string) ([]*Template, error)

Load attempts to load templates matching the given pattern. If the pattern starts with @sourcename/, it resolves to the vendored location. Otherwise, it delegates to the underlying FileSystemLoader.

type Template

type Template struct {
	// Name is an identifier for this template.
	Name string

	// RawSource contains the original, unprocessed template content.
	RawSource []byte

	// ParsedSource contains the template content after preprocessing.
	ParsedSource string

	// Path is the file path for this template if it was loaded from a file.
	Path string

	// Status indicates whether the template has been loaded and parsed.
	Status int

	// AsHtml determines whether the content should be treated as HTML (with escaping)
	// or as plain text.
	AsHtml bool

	// Error contains any error encountered during template processing.
	Error error

	// Metadata stores extracted information from the template (e.g., FrontMatter).
	Metadata map[string]any

	// Namespace is set when this template was included via namespace directive.
	// When set, all template definitions and references will be prefixed with this namespace.
	Namespace string

	// NamespaceEntryPoints specifies which templates to include when namespacing.
	// If empty, all templates are included. If set, only these templates and their
	// transitive dependencies are included (tree-shaking).
	NamespaceEntryPoints []string

	// Extensions records extend directives to be processed after all templates are parsed.
	// Each extension creates a new template by copying a source and rewiring references.
	Extensions []Extension
	// contains filtered or unexported fields
}

Template is the basic unit of rendering that manages content and dependencies.

func (*Template) AddDependency

func (t *Template) AddDependency(another *Template) bool

AddDependency adds another template as a dependency of this template. It returns false if the dependency would create a cycle, true otherwise.

func (*Template) CleanedSource added in v0.0.18

func (t *Template) CleanedSource() (string, error)

Returns the cleaned source of this template wihtout all the includes removed (but before they are preprocessed)

func (*Template) Dependencies

func (t *Template) Dependencies() []*Template

Dependencies returns all templates that this template directly depends on.

func (*Template) WalkTemplate

func (root *Template) WalkTemplate(loader TemplateLoader, handler func(template *Template) error) (err error)

type TemplateGroup

type TemplateGroup struct {

	// Funcs contains template functions available to all templates in this group.
	Funcs map[string]any

	// Loader is used to resolve and load template dependencies.
	Loader TemplateLoader
	// contains filtered or unexported fields
}

TemplateGroup manages a collection of templates and their dependencies, providing methods to process and render them.

func NewTemplateGroup

func NewTemplateGroup() *TemplateGroup

NewTemplateGroup creates a new empty template group with initialized internals.

func (*TemplateGroup) AddFuncs

func (t *TemplateGroup) AddFuncs(funcs map[string]any) *TemplateGroup

AddFuncs adds template functions to this group, making them available to all templates. Returns the template group for method chaining.

func (*TemplateGroup) MustLoad added in v0.0.12

func (t *TemplateGroup) MustLoad(pattern string, cwd string) []*Template

Calls the underlying Loader to load templates matching a pattern and optional using a cwd for relative paths. Panics if an error is encountered. Returns matching templates or an error if no templates were found.

func (*TemplateGroup) NewHtmlTemplate

func (t *TemplateGroup) NewHtmlTemplate(name string, funcs map[string]any) (out *htmpl.Template)

NewHtmlTemplate creates a new HTML template with the given name. The template will have access to the group's functions and any additional functions provided.

func (*TemplateGroup) NewTextTemplate

func (t *TemplateGroup) NewTextTemplate(name string, funcs map[string]any) (out *ttmpl.Template)

NewTextTemplate creates a new TEXT template with the given name. The template will have access to the group's functions and any additional functions provided.

func (*TemplateGroup) PreProcessHtmlTemplate

func (t *TemplateGroup) PreProcessHtmlTemplate(root *Template, funcs htmpl.FuncMap) (out *htmpl.Template, err error)

PreProcessHtmlTemplate processes a HTML template and its dependencies, creating an html/template that can be used for rendering. It handles template dependencies recursively. Returns the processed template and any error encountered.

func (*TemplateGroup) PreProcessTextTemplate

func (t *TemplateGroup) PreProcessTextTemplate(root *Template, funcs ttmpl.FuncMap) (out *ttmpl.Template, err error)

PreProcessTextTemplate processes a template and its dependencies, creating a text/template that can be used for rendering. It handles template dependencies recursively. Returns the processed template and any error encountered.

func (*TemplateGroup) RenderHtmlTemplate

func (t *TemplateGroup) RenderHtmlTemplate(w io.Writer, root *Template, entry string, data any, funcs map[string]any) (err error)

RenderHtmlTemplate renders a template as HTML to the provided writer.

It processes the template with its dependencies, executes it with the given data, and applies any additional template functions provided.

If entry is specified, it executes that specific template within the processed template.

func (*TemplateGroup) RenderTextTemplate

func (t *TemplateGroup) RenderTextTemplate(w io.Writer, root *Template, entry string, data any, funcs map[string]any) (err error)

RenderTextTemplate renders a template as plain text to the provided writer.

It processes the template with its dependencies, executes it with the given data, and applies any additional template functions provided.

If entry is specified, it executes that specific template within the processed template.

type TemplateLoader

type TemplateLoader interface {
	// Load attempts to load templates matching the given pattern.
	// If cwd is not empty, it's used as the base directory for relative paths.
	// Returns matching templates or an error if no templates were found.
	Load(pattern string, cwd string) (template []*Template, err error)
}

TemplateLoader defines an interface for loading template content by name or pattern.

type VendorConfig added in v0.0.27

type VendorConfig struct {
	Sources     map[string]SourceConfig `yaml:"sources"`
	VendorDir   string                  `yaml:"vendor_dir"`
	SearchPaths []string                `yaml:"search_paths"`
	RequireLock bool                    `yaml:"require_lock"`
	// contains filtered or unexported fields
}

VendorConfig represents the templar.yaml configuration

func LoadVendorConfig added in v0.0.27

func LoadVendorConfig(path string) (*VendorConfig, error)

LoadVendorConfig loads a VendorConfig from a templar.yaml file

func (*VendorConfig) ResolveSearchPaths added in v0.0.27

func (c *VendorConfig) ResolveSearchPaths() []string

ResolveSearchPaths returns absolute paths for all search paths

func (*VendorConfig) ResolveVendorDir added in v0.0.27

func (c *VendorConfig) ResolveVendorDir() string

ResolveVendorDir returns the absolute path to the vendor directory

type VendorLock added in v0.0.27

type VendorLock struct {
	Version int                     `yaml:"version"`
	Sources map[string]LockedSource `yaml:"sources"`
}

VendorLock represents the templar.lock file

func LoadLockFile added in v0.0.27

func LoadLockFile(path string) (*VendorLock, error)

LoadLockFile loads a VendorLock from the specified path

type Walker added in v0.0.18

type Walker struct {
	// Buffer stores the processed template content
	Buffer *bytes.Buffer

	// Loader is used to resolve and load template dependencies
	Loader TemplateLoader

	// FoundInclude is called when an include directive is encountered.
	// If it returns true, the include is skipped and not processed.
	FoundInclude func(included string) bool

	// Called before a template is preprocessed.  This is an opportunity
	// for the handler to control entering/preprocessing etc.  For example
	// This could be a place for the handler to skip processing a template
	EnteringTemplate func(template *Template) (skip bool, err error)

	// ProcessedTemplate is called after a template and all its children
	// have been processed. This allows for custom post-processing.
	ProcessedTemplate func(template *Template) error
	// contains filtered or unexported fields
}

Walker provides a mechanism for walking through templates and their dependencies in a customizable way, applying visitor patterns as templates are processed. Unlike the WalkTemplate method which uses post-order traversal, Walker implements in-order traversal, processing includes immediately when encountered.

func (*Walker) Walk added in v0.0.18

func (w *Walker) Walk(root *Template) (err error)

Walk processes a template and its dependencies using in-order traversal. This means includes are processed as soon as they are encountered in the template. After processing, the template's ParsedSource will contain the processed content. If ProcessedTemplate is defined, it will be called on each processed template.

Directories

Path Synopsis
cmd
templar command