gotmpl

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

gotmpl

The gotmpl package provides a service-oriented wrapper around Go's standard text/template package with enhanced features including custom delimiters, string replacements, template function management, and template data reference extraction.

Features

  • Service Pattern: Reusable service instances with default configurations
  • Custom Delimiters: Support for any delimiter pair (e.g., [[, ]] or {%, %})
  • String Replacements: Protect literal strings from template parsing with UUID placeholders
  • Custom Functions: Add custom template functions with flexible override capabilities
  • Missing Key Handling: Configurable behavior for missing map keys (default, zero, error)
  • Context Integration: Full context support for logging and cancellation
  • Structured Logging: Detailed logging at multiple verbosity levels
  • Reference Extraction: Extract data field references from templates for dependency analysis

Installation

import "github.com/oakwood-commons/scafctl/pkg/gotmpl"

Quick Start

Basic Template Execution
ctx := context.Background()

result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "greeting",
    Content: "Hello, {{.Name}}!",
    Data:    map[string]string{"Name": "World"},
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(result.Output) // Output: Hello, World!
Using the Service Pattern

For repeated template execution with shared default functions:

// Create a service with default functions
svc := gotmpl.NewService(template.FuncMap{
    "upper": strings.ToUpper,
    "lower": strings.ToLower,
})

// Execute multiple templates
result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "formatted",
    Content: "{{upper .Text}}",
    Data:    map[string]string{"Text": "hello"},
})

API Reference

Types
TemplateOptions

Configuration for template execution:

type TemplateOptions struct {
    // Content is the template content as a string (required)
    Content string

    // Name is the reference name/identifier for the template
    // Used in logging and error messages (optional, defaults to "unnamed-template")
    Name string

    // Data is the data source passed to the template during execution
    Data any

    // LeftDelim sets the left action delimiter (default: "{{")
    LeftDelim string

    // RightDelim sets the right action delimiter (default: "}}")
    RightDelim string

    // Replacements is a slice of strings to replace before template execution
    // The key is replaced with a UUID placeholder, then restored after execution
    Replacements []Replacement

    // Funcs is a map of custom template functions
    Funcs template.FuncMap

    // MissingKey controls the behavior when a map key is missing
    // Options: MissingKeyDefault, MissingKeyZero, MissingKeyError
    MissingKey MissingKeyOption

    // DisableBuiltinFuncs disables the built-in template functions
    DisableBuiltinFuncs bool
}
MissingKeyOption

Defines behavior for missing map keys:

type MissingKeyOption string

const (
    // MissingKeyDefault prints "<no value>" for missing keys (default)
    MissingKeyDefault MissingKeyOption = "default"

    // MissingKeyZero returns the zero value for the type
    MissingKeyZero MissingKeyOption = "zero"

    // MissingKeyError stops execution with an error
    MissingKeyError MissingKeyOption = "error"
)
ExecuteResult

Result of template execution:

type ExecuteResult struct {
    // Output is the rendered template content
    Output string

    // TemplateName is the name/identifier of the template
    TemplateName string

    // ReplacementsMade is the number of replacements that were applied
    ReplacementsMade int
}
TemplateReference

Represents a data field reference found in a template:

type TemplateReference struct {
    // Path is the dot-notation path to the field (e.g., ".User.Name")
    Path string

    // Position is the location in the template (e.g., "line:col")
    Position string
}
Functions
Execute

Convenience function for one-off template execution:

func Execute(ctx context.Context, opts TemplateOptions) (*ExecuteResult, error)
NewService

Creates a new template service with optional default functions:

func NewService(defaultFuncs template.FuncMap) *Service
Service.Execute

Renders a template with the provided options:

func (s *Service) Execute(ctx context.Context, opts TemplateOptions) (*ExecuteResult, error)
Service.GetReferences

Extracts data field references from a template:

func (s *Service) GetReferences(ctx context.Context, opts TemplateOptions) ([]TemplateReference, error)
GetGoTemplateReferences

Convenience function for extracting references without creating a service:

func GetGoTemplateReferences(content, leftDelim, rightDelim string) ([]string, error)

Usage Examples

Custom Delimiters

Use Jinja2-style delimiters:

svc := gotmpl.NewService(nil)

result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
    Name:      "jinja-style",
    Content:   "{% for item in .Items %}{{ item }}{% endfor %}",
    LeftDelim: "{%",
    RightDelim: "%}",
    Data: map[string][]string{
        "Items": {"apple", "banana", "cherry"},
    },
})
Custom Functions

Add and override template functions:

svc := gotmpl.NewService(template.FuncMap{
    "default": func(def, val string) string {
        if val == "" {
            return def
        }
        return val
    },
})

result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "with-functions",
    Content: "{{default \"N/A\" .Value}}",
    Data:    map[string]string{"Value": ""},
    Funcs: template.FuncMap{
        "upper": strings.ToUpper, // Per-execution function
    },
})
String Replacements

Protect literal template syntax from parsing:

result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "with-replacements",
    Content: "The syntax {{.Name}} uses REPLACE_ME delimiters",
    Data:    map[string]string{"Name": "Go"},
    Replacements: []gotmpl.Replacement{
        {Find: "REPLACE_ME", Replace: "{{...}}"},
    },
})
// Output: The syntax Go uses {{...}} delimiters
Missing Key Handling

Configure behavior for undefined map keys:

// Error on missing keys
result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:       "strict",
    Content:    "{{.Missing}}",
    Data:       map[string]string{},
    MissingKey: gotmpl.MissingKeyError,
})
// Returns error: "template: strict:1:2: executing \"strict\" at <.Missing>: map has no entry for key \"Missing\""

// Use zero value
result, err = gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:       "zero-value",
    Content:    "{{.Missing}}",
    Data:       map[string]string{},
    MissingKey: gotmpl.MissingKeyZero,
})
// Output: "" (empty string, the zero value for string type)

// Default behavior (print "<no value>")
result, err = gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:       "default",
    Content:    "{{.Missing}}",
    Data:       map[string]string{},
    MissingKey: gotmpl.MissingKeyDefault,
})
// Output: "<no value>"
Extracting Template References

Analyze template dependencies by extracting data field references:

svc := gotmpl.NewService(nil)

refs, err := svc.GetReferences(ctx, gotmpl.TemplateOptions{
    Content: `
        {{.User.Name}}
        {{range .Items}}
            {{.Price}}
        {{end}}
    `,
})

for _, ref := range refs {
    fmt.Printf("%s at %s\n", ref.Path, ref.Position)
}
// Output:
// .User.Name at 1:8
// .Items at 2:15
// .Price at 3:14
Complex Example

Combine multiple features:

svc := gotmpl.NewService(template.FuncMap{
    "upper": strings.ToUpper,
})

result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
    Name:      "complex",
    Content:   "[[ upper .Title ]]\n[[range .Items]]\n- [[.Name]]: $[[.Price]]\n[[end]]\nLiteral: TEMPLATE_SYNTAX",
    LeftDelim: "[[",
    RightDelim: "]]",
    Data: map[string]any{
        "Title": "products",
        "Items": []map[string]any{
            {"Name": "Apple", "Price": "1.50"},
            {"Name": "Banana", "Price": "0.75"},
        },
    },
    Replacements: []gotmpl.Replacement{
        {Find: "TEMPLATE_SYNTAX", Replace: "[[...]]"},
    },
    MissingKey: gotmpl.MissingKeyError,
})

// Output:
// PRODUCTS
// - Apple: $1.50
// - Banana: $0.75
// Literal: [[...]]

Logging

The package integrates with the github.com/oakwood-commons/scafctl/pkg/logger package for structured logging:

  • V(1): High-level operations (template execution start/complete)
  • V(2): Detailed steps (parsing, replacements, function registration)

Enable verbose logging to debug template issues:

import "github.com/oakwood-commons/scafctl/pkg/logger"

// Create a logger with verbosity level 2
lgr := logger.Get(2)
ctx := logger.WithLogger(context.Background(), lgr)

result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "debug",
    Content: "{{.Value}}",
    Data:    map[string]string{"Value": "test"},
})

Error Handling

The package provides detailed error messages with context:

result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
    Name:    "invalid",
    Content: "{{.Value",  // Missing closing delimiter
    Data:    map[string]string{},
})
if err != nil {
    // Error: failed to create template 'invalid': parse error: ...
    fmt.Println(err)
}

Common error scenarios:

  • Empty content: Returns "template content cannot be empty"
  • Parse errors: Invalid template syntax
  • Execution errors: Type mismatches, undefined functions, missing keys (with MissingKeyError)

Template Reference Extraction

The package can analyze templates to extract data field references, useful for:

  • Validating that required data is provided
  • Building dependency graphs
  • Documentation generation
  • Static analysis

The extraction:

  • ✅ Includes field accesses (.User.Name, .Items)
  • ✅ Works with custom delimiters
  • ✅ Handles nested structures and control flow
  • ❌ Excludes function calls ({{upper .Text}} only extracts .Text)
  • ❌ Excludes template variables ({{$var := .Value}} extracts .Value but not $var)
// Using the service method
svc := gotmpl.NewService(nil)
refs, err := svc.GetReferences(ctx, gotmpl.TemplateOptions{
    Content:   "{{.User.Name}} {{range .Orders}}{{.ID}}{{end}}",
    LeftDelim: "[[",
    RightDelim: "]]",
})

// Using the convenience function
refs, err := gotmpl.GetGoTemplateReferences(
    "{{.User.Name}}",
    "{{",
    "}}",
)

Constants

const (
    // DefaultLeftDelim is the default left delimiter for templates
    DefaultLeftDelim = "{{"

    // DefaultRightDelim is the default right delimiter for templates
    DefaultRightDelim = "}}"
)

Testing

The package includes comprehensive test coverage:

# Run all tests
go test ./pkg/gotmpl

# Run with verbose output
go test -v ./pkg/gotmpl

# Run examples
go test -v ./pkg/gotmpl -run Example

# Run with coverage
go test -cover ./pkg/gotmpl

See gotmpl_test.go, refs_test.go, and example_test.go for detailed usage examples.

Design Rationale

Service Pattern

The service pattern allows for:

  • Reusability: Create once, execute many times
  • Default configurations: Share common functions across executions
  • Testability: Easy to mock and test
  • Consistency: Same pattern as other packages in the project
Context Support

Context integration enables:

  • Cancellation: Stop long-running template execution
  • Logging: Structured logging with verbosity levels
  • Tracing: Future support for distributed tracing
  • Deadline propagation: Respect timeouts from upstream callers
Replacement System

The replacement system solves the problem of embedding literal template syntax:

  1. Before parsing, specified strings are replaced with UUID placeholders
  2. Template is parsed and executed (placeholders pass through unmodified)
  3. After execution, placeholders are restored to original strings

This is useful when:

  • Documenting template syntax (showing {{...}} examples)
  • Embedding other template formats in output
  • Protecting special characters from template parsing
Typed MissingKey Options

Using a custom type instead of strings provides:

  • Compile-time safety: Invalid options caught by the compiler
  • IDE autocomplete: Better developer experience
  • Self-documentation: Clear available options
  • Future extensibility: Easy to add new options

Performance Considerations

  • Template parsing: Parse once per execution (not cached across executions)
  • Replacements: Linear scan of content (O(n) per replacement)
  • Reference extraction: Parse tree traversal (O(nodes) in template)
  • Logging overhead: Minimal when verbosity is disabled

For high-performance scenarios:

  • Reuse Service instances to avoid function map duplication
  • Minimize replacements (only protect necessary strings)
  • Disable verbose logging in production
  • Consider caching parsed templates externally if needed
  • text/template: Standard Go template engine (underlying implementation)
  • text/template/parse: Template AST parsing (used for reference extraction)
  • github.com/oakwood-commons/scafctl/pkg/logger: Structured logging integration
  • github.com/oakwood-commons/scafctl/pkg/celexp: CEL expression evaluation (complementary templating)

License

See the main project LICENSE file.

Documentation

Index

Examples

Constants

View Source
const (
	// DefaultLeftDelim is the default left delimiter for templates
	DefaultLeftDelim = "{{"

	// DefaultRightDelim is the default right delimiter for templates
	DefaultRightDelim = "}}"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ExecuteResult

type ExecuteResult struct {
	// Output is the rendered template content
	Output string

	// TemplateName is the name/identifier of the template
	TemplateName string

	// ReplacementsMade is the number of replacements that were applied
	ReplacementsMade int
}

ExecuteResult contains the result of template execution

func Execute

func Execute(ctx context.Context, opts TemplateOptions) (*ExecuteResult, error)

Execute is a convenience function that creates a service and executes a template For one-off template execution without custom default functions

Example

ExampleExecute demonstrates basic template execution

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))

	result, err := gotmpl.Execute(ctx, gotmpl.TemplateOptions{
		Name:    "greeting.tmpl",
		Content: "Hello, {{.Name}}! You are {{.Age}} years old.",
		Data: map[string]any{
			"Name": "Alice",
			"Age":  30,
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
Hello, Alice! You are 30 years old.

type GoTemplatingContent

type GoTemplatingContent string

type MissingKeyOption

type MissingKeyOption string

MissingKeyOption defines the behavior when a map key is missing during template execution

const (
	// MissingKeyDefault continues execution and prints "<no value>" for missing keys
	// This is the default behavior
	MissingKeyDefault MissingKeyOption = "default"

	// MissingKeyZero returns the zero value for the map type's element
	MissingKeyZero MissingKeyOption = "zero"

	// MissingKeyError stops execution immediately with an error
	MissingKeyError MissingKeyOption = "error"
)

type Replacement

type Replacement struct {
	// Find is the string to search for in the template content
	Find string

	// Replace is the temporary replacement value
	// If empty, a UUID will be generated automatically
	Replace string
}

Replacement defines a string replacement to perform before/after templating

type Service

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

Service provides template execution capabilities

func NewService

func NewService(defaultFuncs template.FuncMap) *Service

NewService creates a new template service with optional default functions

func (*Service) Execute

func (s *Service) Execute(ctx context.Context, opts TemplateOptions) (*ExecuteResult, error)

Execute renders a template with the provided options

Example (Complex)

ExampleService_Execute_complex demonstrates combining multiple features

package main

import (
	"context"
	"fmt"
	"log"
	"text/template"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))

	// Create service with helper functions
	svc := gotmpl.NewService(template.FuncMap{
		"default": func(defaultVal, val string) string {
			if val == "" {
				return defaultVal
			}
			return val
		},
	})

	result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:       "report.tmpl",
		Content:    "User: [[.Name]], Status: [[default \"inactive\" .Status]], Code: LITERAL_CODE",
		LeftDelim:  "[[",
		RightDelim: "]]",
		Data: map[string]any{
			"Name":   "David",
			"Status": "",
		},
		Replacements: []gotmpl.Replacement{
			{Find: "LITERAL_CODE", Replace: "TEMP_12345"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
User: David, Status: inactive, Code: LITERAL_CODE
Example (CustomDelimiters)

ExampleService_Execute_customDelimiters shows using custom delimiters

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))
	svc := gotmpl.NewService(nil)

	result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:       "jinja-style.tmpl",
		Content:    "Welcome, [[.User]]!",
		LeftDelim:  "[[",
		RightDelim: "]]",
		Data: map[string]any{
			"User": "Bob",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
Welcome, Bob!
Example (CustomFunctions)

ExampleService_Execute_customFunctions demonstrates using custom template functions

package main

import (
	"context"
	"fmt"
	"log"
	"strings"
	"text/template"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))

	// Create service with default functions
	svc := gotmpl.NewService(template.FuncMap{
		"upper": strings.ToUpper,
		"lower": strings.ToLower,
	})

	result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:    "formatted.tmpl",
		Content: "{{upper .FirstName}} {{lower .LastName}}",
		Data: map[string]any{
			"FirstName": "john",
			"LastName":  "DOE",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
JOHN doe
Example (LoopAndConditionals)

ExampleService_Execute_loopAndConditionals shows template control structures

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))
	svc := gotmpl.NewService(nil)

	content := `{{if .Title}}Title: {{.Title}}
{{end}}Items:{{range .Items}}
- {{.}}{{end}}`

	result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:    "list.tmpl",
		Content: content,
		Data: map[string]any{
			"Title": "Shopping List",
			"Items": []string{"Apples", "Bananas", "Oranges"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
Title: Shopping List
Items:
- Apples
- Bananas
- Oranges
Example (MissingKeyHandling)

ExampleService_Execute_missingKeyHandling demonstrates handling missing keys

package main

import (
	"context"
	"fmt"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))
	svc := gotmpl.NewService(nil)

	// Default behavior: prints "<no value>"
	result1, _ := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:       "default.tmpl",
		Content:    "Status: {{.Status}}",
		Data:       map[string]any{}, // Status key missing
		MissingKey: gotmpl.MissingKeyDefault,
	})
	fmt.Println(result1.Output)

	// Zero behavior: returns zero value (empty string for string type)
	result2, _ := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:       "zero.tmpl",
		Content:    "Count: {{.Count}}",
		Data:       map[string]any{}, // Count key missing
		MissingKey: gotmpl.MissingKeyZero,
	})
	fmt.Println(result2.Output)

}
Output:
Status: <no value>
Count: <no value>
Example (Replacements)

ExampleService_Execute_replacements shows protecting literal strings from template processing

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))
	svc := gotmpl.NewService(nil)

	// Template contains both template syntax and literal template syntax
	content := `Name: {{.Name}}
Literal template syntax: {{KEEP_THIS_LITERAL}}`

	result, err := svc.Execute(ctx, gotmpl.TemplateOptions{
		Name:    "mixed.tmpl",
		Content: content,
		Data: map[string]any{
			"Name": "Charlie",
		},
		Replacements: []gotmpl.Replacement{
			{Find: "{{KEEP_THIS_LITERAL}}"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
Name: Charlie
Literal template syntax: {{KEEP_THIS_LITERAL}}

func (*Service) GetReferences

func (s *Service) GetReferences(ctx context.Context, opts TemplateOptions) ([]TemplateReference, error)

GetReferences extracts all references to Data from a Go template This method parses the template and extracts variable references (e.g., .User, .Items) It excludes function calls and Go template control variables (like $var)

Example

ExampleService_GetReferences demonstrates extracting references using the Service

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
	"github.com/oakwood-commons/scafctl/pkg/logger"
)

func main() {
	ctx := logger.WithLogger(context.Background(), logger.Get(0))
	svc := gotmpl.NewService(nil)

	refs, err := svc.GetReferences(ctx, gotmpl.TemplateOptions{
		Name:       "config.tmpl",
		Content:    "[[.App.Name]] version [[.App.Version]]",
		LeftDelim:  "[[",
		RightDelim: "]]",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Template references:")
	for _, ref := range refs {
		fmt.Printf("  %s\n", ref.Path)
	}
}
Output:
Template references:
  .App.Name
  .App.Version

type TemplateOptions

type TemplateOptions struct {
	// Content is the template content as a string
	Content string

	// Name is the reference name/identifier for the template (e.g., file path)
	// Used in logging and error messages
	Name string

	// Data is the data source passed to the template during execution
	Data any

	// LeftDelim sets the left action delimiter (default: "{{")
	LeftDelim string

	// RightDelim sets the right action delimiter (default: "}}")
	RightDelim string

	// Replacements is a map of strings to replace before template execution
	// The key is replaced with a UUID placeholder, then restored after execution
	// This helps avoid template parsing errors for content that should be literal
	Replacements []Replacement

	// Funcs is a map of custom template functions to make available
	// These are added to the template's function map
	Funcs template.FuncMap

	// MissingKey controls the behavior when a map key is missing
	// Default: MissingKeyDefault (prints "<no value>")
	// Options: MissingKeyDefault, MissingKeyZero, MissingKeyError
	MissingKey MissingKeyOption

	// DisableBuiltinFuncs disables the built-in template functions
	// By default, basic functions like "html", "js", etc. are available
	DisableBuiltinFuncs bool
}

TemplateOptions contains configuration for template execution

type TemplateReference

type TemplateReference struct {
	// Path is the dot-notation path to the data (e.g., ".User.Name", ".Items")
	Path string

	// Position is the line:column position in the template (if available)
	Position string
}

TemplateReference represents a reference to data in a template

func GetGoTemplateReferences

func GetGoTemplateReferences(templateContent, leftDelim, rightDelim string) ([]TemplateReference, error)

GetGoTemplateReferences is a convenience function that creates a service and extracts references For one-off reference extraction without needing to create a service

Example

ExampleGetGoTemplateReferences demonstrates extracting data references from templates using the convenience function

package main

import (
	"fmt"
	"log"

	"github.com/oakwood-commons/scafctl/pkg/gotmpl"
)

func main() {
	template := `
{{if .User.IsAdmin}}
	Welcome, {{.User.Name}}!
	{{range .User.Permissions}}
		- {{.}}
	{{end}}
{{end}}
`

	refs, err := gotmpl.GetGoTemplateReferences(template, "", "")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Found references:")
	for _, ref := range refs {
		fmt.Printf("  %s\n", ref.Path)
	}
}
Output:
Found references:
  .User.IsAdmin
  .User.Name
  .User.Permissions

Jump to

Keyboard shortcuts

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