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
- Variables
- func BundleFromOptions(ctx context.Context, l logging.Logger, opts *options.BoilerplateOptions) (*Bundle, []BundleNote, error)
- func RenderFileFromFS(ctx context.Context, rootFS fs.FS, rootPath, outputPath string, ...) (string, error)
- type AnalysisError
- type Bundle
- type BundleNote
- type InputEntry
- type PreparedBundle
- type RenderFileResult
- type ResolvedDep
- type Result
Constants ¶
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 ¶
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.
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 ¶
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 (*PreparedBundle) RenderFiles ¶
func (b *PreparedBundle) RenderFiles(ctx context.Context, outputPaths []string, userVars map[string]any) []RenderFileResult
type RenderFileResult ¶
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.