Documentation
¶
Overview ¶
Package renderer executes a resolver.Plan's templates into a target directory. Rendering is transactional: targetDir either ends up fully populated or is never created. See docs/architecture.md for the renderer's role and guarantees. Phase 1 scope: Go output only.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrTargetExists = errors.New("target directory already exists")
ErrTargetExists is returned when targetDir already exists. Render refuses to touch it — no parents created, nothing written inside it.
var ErrTargetMissing = errors.New("target directory does not exist")
ErrTargetMissing is returned by RenderAdd when targetDir does not exist — the mirror image of ErrTargetExists: RenderAdd only ever writes into an already-generated project, never creates one.
Functions ¶
This section is empty.
Types ¶
type AddError ¶
type AddError struct {
FileCollisions []*FileCollisionError
}
AddError aggregates every file collision RenderAdd finds before writing anything, the same aggregate-everything-first posture as RenderError.
type AddOptions ¶
type AddOptions struct {
// Force skips the per-file collision check — a destination a new
// module's template would write to that already exists on disk.
// It never bypasses anything validatePlan finds (DependencyConflictError,
// EnvVarConflictError) or anything resolver.Resolve itself rejects
// (cycles, requires/conflicts, unsupported language): those represent
// a genuine incompatibility between modules, not an incidental file
// clash, and no flag overrides them in v1.
Force bool
// DryRun computes and reports what RenderAdd would do without
// writing anything — no files, no go.mod/.env.example edits, no
// state.json update.
DryRun bool
}
AddOptions configures RenderAdd.
type AddResult ¶
type AddResult struct {
NewModules []AddedModule // modules not previously present, now rendered
Files []string // template destinations written, relative to targetDir
GoModDeps []string // dependency module paths newly added to go.mod
EnvVars []string // env var names newly appended to .env.example
}
AddResult summarizes what RenderAdd did, or — under AddOptions.DryRun — would do. Every slice is sorted for deterministic output.
type AddedModule ¶
AddedModule is one module RenderAdd newly rendered.
type Context ¶
type Context struct {
ProjectName string // e.g. "myapp"
ModulePath string // Go import path, e.g. "github.com/user/myapp"
GoVersion string // e.g. "1.26" — target project's go.mod go line
}
Context is the substitution data available to every template.
func ContextFromExisting ¶
ContextFromExisting derives a Context for RenderAdd from a project that already exists on disk: ModulePath and GoVersion are read back from the real go.mod (keel add never asks the user to re-supply them — they were fixed at keel init time), and ProjectName from targetDir's own directory name.
type DependencyConflictError ¶
type DependencyConflictError struct {
Module string
VersionA string
ViaA string
VersionB string
ViaB string
}
DependencyConflictError reports the same Go module path required at two different versions. Via names the requiring module, same provenance style as the resolver's Conflict.
func (*DependencyConflictError) Error ¶
func (e *DependencyConflictError) Error() string
type DuplicateOutputPathError ¶
DuplicateOutputPathError reports two modules writing to the same destination path.
func (*DuplicateOutputPathError) Error ¶
func (e *DuplicateOutputPathError) Error() string
type EnvVarConflictError ¶
type EnvVarConflictError struct {
Name string
ModuleA string
RequiredA bool
DefaultA string
ModuleB string
RequiredB bool
DefaultB string
}
EnvVarConflictError reports the same environment variable declared by two modules with different Required or Default settings. Identical declarations (same Required and Default; Description may differ, that's just prose) dedupe silently instead of producing this error.
func (*EnvVarConflictError) Error ¶
func (e *EnvVarConflictError) Error() string
type FileCollisionError ¶
FileCollisionError reports a new module's template destination that already exists on disk in the target project. This is the only failure --force is allowed to bypass — a genuine conflict between modules (DependencyConflictError, EnvVarConflictError) or in the resolver's own requires/conflicts graph is never something a file flag should override.
func (*FileCollisionError) Error ¶
func (e *FileCollisionError) Error() string
type ModuleInfo ¶
ModuleInfo is the per-module data exposed to templates under the "Modules" key. It is derived by Render from plan.Modules, sorted alphabetically by name — deliberately NOT plan.Modules' own dependency order (topological, e.g. base last). Templates like base's README want a stable, human-readable listing, not build order.
type PathEscapeError ¶
PathEscapeError reports a template destination that would write outside the target directory. Computed independently of manifest validation — Render never trusts that a Template.To was already checked.
func (*PathEscapeError) Error ¶
func (e *PathEscapeError) Error() string
type RenderError ¶
type RenderError struct {
PathEscapes []*PathEscapeError
DuplicateOutputPaths []*DuplicateOutputPathError
DependencyConflicts []*DependencyConflictError
EnvVarConflicts []*EnvVarConflictError
}
RenderError aggregates every failure found while validating a plan (pass 1), rather than stopping at the first.
func (*RenderError) Error ¶
func (e *RenderError) Error() string
func (*RenderError) Unwrap ¶
func (e *RenderError) Unwrap() []error
Unwrap exposes the individual failures for errors.As and errors.Is.
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer executes module templates sourced from an fs.FS. A manifest Template.From (e.g. "templates/go/x.tmpl") resolves against templates as path.Join("modules", moduleName, from).
func New ¶
New constructs a Renderer over templates, e.g. an embed.FS in the real CLI or a fstest.MapFS in tests.
func (*Renderer) Render ¶
Render executes plan against ctx and writes the result to targetDir. targetDir must not already exist. On any failure, targetDir is never created and no partial output is left behind.
Pass 1 validates the whole plan — destination paths, containment, and merged Go dependencies — without touching disk, aggregating every violation it finds. Only if that passes does pass 2 render templates into a staging directory beside targetDir and atomically rename it into place, so targetDir either appears fully populated or not at all.
func (*Renderer) RenderAdd ¶
func (r *Renderer) RenderAdd(plan *resolver.Plan, ctx Context, targetDir string, existing []state.Module, opts AddOptions) (*AddResult, error)
RenderAdd renders newly-requested modules' templates into an already-existing project, merging go.mod and .env.example instead of overwriting them, and updates .keel/state.json. This is keel add's entry point — the mirror image of Render, which refuses to run against a directory that already exists; RenderAdd refuses to run against one that doesn't (ErrTargetMissing).
plan is the full resolved closure — existing modules (named in existing) union whatever was newly requested — exactly as resolver.Resolve/ResolveProject already produces for keel init. Nothing about conflict, cycle, or requires detection changes for keel add: it is the same Resolve call and the same validatePlan pass, over a larger requested set. A module already listed in existing is never re-rendered; its templates are assumed already on disk from a prior init or add.
Unlike Render, RenderAdd writes directly into targetDir rather than staging into a sibling directory and renaming — there is no atomic swap available for a directory that already has other content in it. If writing fails partway through, already-written new files are left in place; validation (the conflict and collision checks) happens entirely before the first write, so a failure discovered there always leaves targetDir untouched, but an I/O failure mid-write does not roll back. This asymmetry with Render's all-or-nothing guarantee is a property of adding to an existing tree, not an oversight.
type TemplateError ¶
TemplateError wraps a parse or execution failure for one module's template.
func (*TemplateError) Error ¶
func (e *TemplateError) Error() string
func (*TemplateError) Unwrap ¶
func (e *TemplateError) Unwrap() error