document

package
v0.0.1 Latest Latest
Warning

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

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

Documentation

Overview

Package document analyses template sources, rather than compiling them.

A go template declares nothing about the data it runs on: which data model fits which template is knowledge that lives outside the language. The parse tree, however, records every access, and the data the current dot stands for can be followed through range, with and template statements. What a template expects is therefore recoverable, as the set of paths it reads.

Analyze reads one asset and reports, per template it declares, the comments documenting it and the contract it works to.

What the result means

The result closes over every branch, so it holds everything a template may read, not what one execution of it does read. A path guarded by a condition is reported like any other, and the tree cannot tell which of them a given run will reach.

It is therefore what the data has to be able to answer, and not a list of what it must hold.

Limits

An access the analysis cannot place is counted rather than dropped, so an incomplete contract is visible as such. A dot or a variable that comes out of a function call produces one, since a value cannot be followed through a function.

A template invoking a function held by its data, with the builtin "call", is reported as dynamic: what such a function reads is decided when the template runs. Nothing else invokes a template or reads data by a name computed at run time, and a func map that did would be invisible here.

A variable assigned inside a block keeps, for the rest of the block, the path it is assigned. Past the end of that block the analysis reports the path it was declared with, so an assignment intended to outlive its block is not followed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Closure

func Closure(contracts map[string]Contract) map[string]Transitive

Closure folds every template into the templates it calls.

A template is folded once, against the data it is executed on, and its result is rebased wherever it is called. A template no other one calls is folded all the same, since what it reads is answered by whatever data reaches it.

Templates calling one another in a loop are folded without following the loop. Which templates those are is a property of the call graph, not of the order the folding happens in, so the result is the same however this is called.

Types

type Analysis

type Analysis struct {
	// Docstrings holds the comments documenting a template, by template name.
	Docstrings map[string][]string

	// Contracts holds what a template reads and calls, by template name.
	Contracts map[string]Contract
}

Analysis holds the result of reading an asset, per template it declares.

func Analyze

func Analyze(path, name string, data []byte, funcs template.FuncMap) (Analysis, error)

Analyze reads an asset and reports what the templates it declares document and do.

name is the template name the asset itself is registered under, which the parser also uses for the tree holding whatever lies outside the define statements.

funcs is the map the templates are bound to. It decides which calls are worth reporting: a builtin of text/template is left out, unless the map defines a function of that name, in which case calling it is a dependency on the map like any other.

type Call

type Call struct {
	// Name is the template invoked.
	Name string

	// Data is the path handed to it, rooted like the paths of the calling template.
	//
	// It is "." when the caller hands over its own data, and empty when the analysis could not
	// place it.
	Data string
}

Call is the invocation of one template by another.

type Contract

type Contract struct {
	// Reads lists the data paths the template may read, sorted, across every branch.
	Reads []string

	// RootReads lists the paths read through "$", sorted.
	//
	// Inside a range or a with, "$" still stands for the data the template was executed on, so a
	// path listed here is a reach past the current dot, back to the top of the template.
	RootReads []string

	// Funcs lists the func map functions the template calls, sorted.
	//
	// The builtins of [text/template] are left out: a caller replacing a template supplies the
	// func map, never those.
	Funcs []string

	// Calls lists the templates it invokes, with the data handed to each, sorted by name.
	Calls []Call

	// Unresolved counts the accesses the analysis could not place.
	Unresolved int

	// Empty reports whether the template holds nothing but white space and comments, so that running
	// it renders nothing.
	Empty bool

	// Dynamic reports whether the template invokes a function held by the data, with "call".
	//
	// Such a function is resolved only when the template runs, so a contract reported for a template
	// that uses one is incomplete by construction.
	Dynamic bool
}

Contract holds the data a template reads, and the data it passes to the templates it calls.

Paths are rooted at the data the template itself is executed on, which is the argument of the call that reached it, not the data of whoever called that caller.

A contract closes over every branch, so it holds what the template may read rather than what any one execution of it reads.

type Transitive

type Transitive struct {
	// Reads lists the data paths the template may read, itself or through the templates it calls.
	Reads []string

	// Funcs lists the func map functions reached the same way.
	Funcs []string

	// Reaches lists the templates it calls, directly or not, sorted.
	//
	// Unlike the paths, this follows a loop all the way round: a template in a loop reaches every
	// other template of that loop.
	Reaches []string

	// Unresolved counts what could not be folded in: the accesses the template itself could not
	// place, and the contract of a template called with data that could not be placed.
	Unresolved int

	// Recursive reports whether the template is in a loop, calling itself directly or through
	// others.
	//
	// The paths of a template in the loop are left out of the fold: rebasing them would hang them
	// from themselves without end. Every template of a loop is treated alike, so what the fold
	// holds does not depend on which template it started from.
	Recursive bool
}

Transitive holds the data a template reads once the templates it calls are folded into it.

Paths are rooted at the data the template itself is executed on, like those of a Contract: the paths a called template reads are rebased onto the data handed to it, so a template reading ".GoName" that is called with ".Properties[]" contributes ".Properties[].GoName".

Jump to

Keyboard shortcuts

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