inputs

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: May 13, 2026 License: MPL-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package inputs provides static analysis of boilerplate templates to compute which output files are affected by each declared input variable.

Unlike the render path in package render/templates, this package never executes a template body. It parses each template into an AST and walks the AST to collect variable references. The result is a JSON-serializable map suitable for live-diff UIs that need to know which files to re-render when a single input changes.

Index

Constants

View Source
const (
	// KindUndeclaredVariable: referenced in a template body but not declared
	// in any boilerplate.yml in scope.
	KindUndeclaredVariable = "undeclared_variable"

	// KindCycle: a dependency cycle was detected.
	KindCycle = "cycle"

	// KindUnresolvableDependency: a remote URL in FS-only mode, or a path
	// that does not exist.
	KindUnresolvableDependency = "unresolvable_dependency"

	// KindFilenameRender: failed to render a template-bearing filename.
	KindFilenameRender = "filename_render"

	// KindParse: failed to parse a template body or value expression.
	KindParse = "parse"

	// KindParseArgs: failed to parse CLI arguments before analysis began.
	KindParseArgs = "parse_args"

	// KindSkipFiles: failed to render or expand a skip_files entry's path,
	// not_path, or if condition.
	KindSkipFiles = "skip_files"

	// KindPartialExpansionLimit: partial-template invocation graph did not
	// reach a fixed point within the analyzer's iteration cap; results may
	// be missing some transitive references.
	KindPartialExpansionLimit = "partial_expansion_limit"
)

Kind values that may appear in AnalysisError.Kind. Listed here so callers can switch on a stable identifier rather than a string literal.

Variables

View Source
var (
	// ErrOutputNotProduced means outputPath is not produced by any file in
	// the resolved template tree.
	ErrOutputNotProduced = errors.New("output path not produced by template tree")

	// ErrDependencyNotInBundle means a local dependency's template-url did
	// not resolve to a directory inside rootFS. Remote dependencies always
	// produce this error (no go-getter is available in WASM).
	ErrDependencyNotInBundle = errors.New("dependency not present in bundle")

	// ErrDynamicFilename means outputPath corresponds to a source file
	// whose filename contains template syntax that did not render to a
	// static path. The consumer should fall back to cold render rather
	// than attempt warm dispatch.
	ErrDynamicFilename = errors.New("output path has a dynamic (templated) filename")

	// ErrSkipFilesExcluded means a skip_files rule excludes outputPath
	// from the dep's output under the current vars. The consumer should
	// treat this as cold-only.
	ErrSkipFilesExcluded = errors.New("output path excluded by skip_files rule")
)

Sentinel errors returned by RenderFileFromFS. Callers (notably the WASM bridge) discriminate on these so they can route specific failure modes to the cold-render fallback rather than surfacing them as generic render errors.

View Source
var ErrRemoteDependencyInBundle = errors.New("remote dependency cannot be bundled")

ErrRemoteDependencyInBundle is non-fatal at the bundle level: the bundle is returned with whatever local deps did resolve, and the caller treats the affected outputs as cold-only.

Functions

func BundleFromOptions

func BundleFromOptions(ctx context.Context, l logging.Logger, opts *options.BoilerplateOptions) (*Bundle, []BundleNote, error)

BundleFromOptions resolves the root template (via go-getter if needed) and walks the dep tree, collecting every boilerplate.yml and text template file. Remote dependencies are not followed. Any go-getter temp directories are cleaned up before returning; Files holds contents as strings, so the bundle remains usable.

func RenderFileFromFS

func RenderFileFromFS(ctx context.Context, rootFS fs.FS, rootPath, outputPath string, userVars map[string]any, depsIndex map[string][]ResolvedDep) (string, error)

RenderFileFromFS walks the dep tree rooted at rootPath in rootFS, locates the dep that produces outputPath, builds the dep-scoped variable map by applying each ancestor's variable defaults (overridable by userVars), and renders the source template that produces outputPath against that scope.

The function is side-effect-free: hooks never execute, no files are written, and no I/O occurs outside rootFS. It is the WASM warm-dispatch counterpart to running `boilerplate template` against a single file.

depsIndex is the bundle's pre-computed dependency layout. It MUST be non-nil — helpers like `{{ templateFolder }}` are meaningless when re- rendering a dep's `template-url` against an in-memory fs. An empty map is fine for templates with no deps; nil yields ErrDependencyNotInBundle.

userVars is layered onto each dep's declared defaults (user wins). A var referenced by a template but absent from both produces a render error via missingkey=error.

Types

type AnalysisError

type AnalysisError struct {
	Kind     string `json:"kind"`
	Template string `json:"template,omitempty"`
	Name     string `json:"name,omitempty"`
	File     string `json:"file,omitempty"`
	Message  string `json:"message,omitempty"`
}

AnalysisError is a soft error encountered during analysis. Soft errors do not abort the run; they accumulate in Result.Errors so the caller can surface them to the user. See the Kind* constants for the canonical set of values that may appear in the Kind field.

type Bundle

type Bundle struct {
	Files map[string]string `json:"files"`

	// Dependencies maps a parent template's bundle directory ("." for
	// the root, or another ResolvedDep.BundlePath for nested deps) to
	// its ordered list of resolved local deps. Remote deps and
	// unresolvable local deps do NOT appear here; consumers must treat
	// their omission as a hint to force cold render.
	//
	// Required for warm rendering of any template with deps.
	// RenderFileFromFS rejects bundles missing this field.
	Dependencies map[string][]ResolvedDep `json:"dependencies,omitempty"`

	RootPath string `json:"rootPath"`
}

Bundle is a snapshot of every text file in the resolved boilerplate template tree, keyed by a forward-slash path relative to RootPath. The shape mirrors the bundle that boilerplateInputsMap and boilerplateRenderFile accept, so callers can pipe the output of `boilerplate inputs map --include-bundle` directly into the WASM functions without rewriting.

Each dep's files live at a deterministic, bundle-relative directory computed by the producer (see ResolvedDep.BundlePath). That directory does not — and cannot — mirror the dep's on-disk template-url, because a template-url that uses {{ templateFolder }} resolves to an absolute disk path that has no defensible meaning inside a virtual filesystem.

type BundleNote

type BundleNote struct {
	Kind    string
	Name    string
	Message string
}

BundleNote is a soft diagnostic surfaced during bundle collection. The CLI maps these into inputs.AnalysisError so the JSON contract stays uniform.

type InputEntry

type InputEntry struct {
	Name        string   `json:"name"`
	DeclaredIn  string   `json:"declared_in"`
	Type        string   `json:"type"`
	Description string   `json:"description,omitempty"`
	Files       []string `json:"files"`
}

InputEntry describes a single declared input.

type PreparedBundle

type PreparedBundle struct {
	RootFS    fs.FS
	DepsIndex map[string][]ResolvedDep
	RootPath  string
}

PreparedBundle is a pre-parsed bundle reusable across many render calls. It carries no per-render state.

func (*PreparedBundle) RenderFile

func (b *PreparedBundle) RenderFile(ctx context.Context, outputPath string, userVars map[string]any) (string, error)

func (*PreparedBundle) RenderFiles

func (b *PreparedBundle) RenderFiles(ctx context.Context, outputPaths []string, userVars map[string]any) []RenderFileResult

type RenderFileResult

type RenderFileResult struct {
	Err     error
	Path    string
	Content string
}

RenderFileResult is one entry returned by RenderFilesFromFS. Exactly one of Content or Err is non-zero. Err may be a sentinel from render_file.go (discriminate with errors.Is) or a wrapped downstream error.

func RenderFilesFromFS

func RenderFilesFromFS(ctx context.Context, rootFS fs.FS, rootPath string, outputPaths []string, userVars map[string]any, depsIndex map[string][]ResolvedDep) []RenderFileResult

RenderFilesFromFS renders each path in outputPaths in input order. Per-path failures are returned inline so one broken template doesn't blank siblings.

type ResolvedDep

type ResolvedDep struct {
	Name string `json:"name"`

	// BundlePath is the dep's directory inside Bundle.Files.
	// Forward-slash, strictly-relative, validateBundlePath-clean.
	BundlePath string `json:"bundlePath"`

	// OutputFolder is pre-rendered against the parent scope at bundle time.
	OutputFolder string `json:"outputFolder"`

	// Each is the for_each iteration value the consumer must seed into the
	// parent scope as `__each__` before evaluating dep-variable defaults.
	// Empty for non-for_each deps.
	Each string `json:"each,omitempty"`
}

ResolvedDep is the bundle-time-resolved location and output-folder for one declared dependency. A for_each dep produces one entry per iteration, sharing Name + BundlePath but with distinct OutputFolder + Each.

type Result

type Result struct {
	// Inputs is keyed by "<template_path>:<input_name>". Each entry describes
	// one declared input variable across the entire dependency tree.
	Inputs map[string]InputEntry `json:"inputs"`

	// Files is the inverse index: keyed by output path (relative to the root
	// template's output root), each entry lists the input keys whose change
	// would re-render that file.
	Files map[string][]string `json:"files"`

	// Sources maps each output path to the absolute path of the source
	// template file that produced it. Consumers that re-render a single file
	// (e.g., the WASM warm-dispatch path in runbooks) use this to locate the
	// template body to feed into boilerplateRenderTemplate.
	//
	// In OS mode (CLI) the values are absolute disk paths; for templates
	// pulled in via go-getter the path lives under the go-getter temp dir.
	// In FS mode (WASM) the values are slash-separated paths within the
	// supplied rootFS — no absolute disk path is available there.
	//
	// Files whose output path is dynamic and whose filename template failed
	// to render (KindFilenameRender) are absent from Sources; the missing
	// entry plus the existing soft error tells consumers to fall back to a
	// full render rather than guess at a path.
	Sources map[string]string `json:"sources"`

	// Errors collects soft errors encountered during analysis. A non-empty
	// list does not imply the run failed; callers should still consume Inputs
	// and Files. Hard errors (parse failures of the boilerplate config tree
	// itself) are returned via the error return of FromOptions / FromFS.
	Errors []AnalysisError `json:"errors"`
}

Result is the top-level analysis output. It is JSON-serializable and matches the shape documented for the `boilerplate inputs map` CLI subcommand.

func FromFS

func FromFS(ctx context.Context, rootFS fs.FS, rootPath string, vars map[string]any) (*Result, error)

FromFS runs analysis with no I/O outside the supplied fs.FS. Used by the WASM bridge.

rootFS must contain a `boilerplate.yml` at rootPath (use "." for the root of the FS). Local dependency template URLs are resolved as relative paths inside rootFS; remote URLs land in Result.Errors as "unresolvable_dependency".

func FromOptions

func FromOptions(ctx context.Context, l logging.Logger, opts *options.BoilerplateOptions) (*Result, error)

FromOptions runs analysis using the same template-resolution rules as `boilerplate template`: it can resolve remote URLs through go-getter, accepts --var and --var-file values via opts, and reads from the local filesystem.

Use this entry point from the CLI. For a side-effect-free pure analysis (as required by the WASM build), use FromFS instead.

Jump to

Keyboard shortcuts

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