Documentation
¶
Overview ¶
Package generator provides the code generation engine for the Nexus CLI.
This file defines the ProjectConfig struct, which holds all user selections from the interactive init wizard. It is the single data structure that flows from the TUI wizard into the template engine — every template receives a pointer to ProjectConfig as its dot context.
The struct includes validation logic, helper methods for querying selections, and constants for all valid option values. These constants are used by both the TUI screens (to populate selection lists) and the template engine (to conditionally render sections).
Design Decision:
- We use plain strings and string slices rather than custom enum types because the values flow directly into Go templates where string comparison is the most natural and readable approach.
- Validation is strict: the Validate() method checks that all values are from the known set of constants, preventing typos and invalid configs.
Package generator provides the code generation engine for the Nexus CLI.
This file implements the Generator — the central engine that takes a ProjectConfig from the init wizard, loads embedded templates, renders them with the config as data, and writes the output files to disk via the Writer.
The engine uses a manifest-driven approach: rather than globbing all .tmpl files and rendering them blindly, it maintains an explicit list of which templates to render based on which features are enabled in the config. This ensures that a project with only REST enabled doesn't get gRPC templates, and vice versa.
Architecture:
Wizard → ProjectConfig → Generator.Generate()
│
├── Load embedded templates (go:embed)
├── Build manifest (which templates for which features)
├── Render each template with ProjectConfig as .
├── Write output via Writer (mkdir, format, write)
├── Run PostProcessor (go mod tidy, go fmt, git init)
└── Return list of generated files
Design Decision:
- Templates are embedded into the nexus binary via go:embed. This means the binary is self-contained — no external template files to distribute.
- We use text/template (not html/template) because we're generating Go source code and Makefiles, not HTML. text/template does not escape output, which is what we want for code generation.
- The manifest is a slice of templateEntry structs, each declaring the template path, output path, and a condition function. This makes it trivial to add new templates for future phases.
Package generator provides the code generation engine for the Nexus CLI.
This file defines the template function map — a collection of helper functions that are available inside every Go template rendered by the generator engine. These functions handle common string transformations, slice operations, and project-specific checks that would be verbose or impossible in raw template syntax.
All functions are registered via TemplateFuncMap() which returns a template.FuncMap suitable for passing to template.Template.Funcs().
Design Decision:
- We implement pluralize/singularize with simple English rules rather than pulling in a dependency like jinzhu/inflection. The rules cover the 95% case for Go backend entity names (Task→Tasks, Category→Categories, etc.) and keep the dependency tree minimal.
- String casing functions (camel, pascal, snake, kebab) handle the common patterns found in Go projects: single words, multi-word with separators, and already-cased identifiers.
Package generator provides the code generation engine for the Nexus CLI.
This file implements the post-processing steps that run after all templates have been rendered and written to disk. These steps transform the raw generated output into a polished, ready-to-use Go project.
Post-processing steps (in order):
- go mod tidy — download dependencies and clean up go.mod/go.sum
- go fmt ./... — format all Go source files to canonical style
- git init — initialize a Git repository (optional, based on config)
- git add + commit — create an initial commit with all generated files
Each step reports its status via a callback function so the TUI progress screen can display real-time feedback to the user.
Design Decision:
- We use os/exec to shell out to the go and git binaries rather than using library equivalents. This ensures we use the exact same toolchain the user has installed, and avoids pulling in large dependencies like go-git. The trade-off is that go and git must be in PATH, which is a reasonable assumption for a Go developer tool.
- Each step is idempotent and best-effort: if go fmt fails (unlikely after go mod tidy succeeds), we log the error but don't abort the entire generation. The user gets a working project either way.
- Context support allows the parent to cancel long-running steps (e.g., if the user presses Ctrl+C during go mod download).
Package generator provides the code generation engine for the Nexus CLI.
This file implements the file writer — the component responsible for taking rendered template content and writing it to the correct location on disk. It handles directory creation, file existence checks (with --force override), and post-write formatting of Go source files via gofmt.
The writer is used by the Generator engine after each template is rendered. It reports back the relative path of each written file so the TUI progress screen can display real-time feedback.
Design Decision:
- We run gofmt on each .go file immediately after writing rather than batching it at the end. This means if generation fails partway through, the files that were written are still properly formatted.
- File permissions are set to 0644 (rw-r--r--) for regular files and 0755 (rwxr-xr-x) for directories, matching standard Go project conventions.
- The writer does NOT use os/exec to call gofmt — instead it uses the go/format package from the standard library for in-process formatting. This avoids a dependency on the gofmt binary being in PATH and is faster.
Index ¶
- Constants
- func AllAuthStrategies() []string
- func AllCIProviders() []string
- func AllCacheStrategies() []string
- func AllDatabases() []string
- func AllMiddleware() []string
- func AllProtocols() []string
- func CommandExists(name string) bool
- func CoreMiddleware() []string
- func EnsureDir(path string) error
- func GoVersion() string
- func OptionalMiddleware() []string
- func Pluralize(word string) string
- func Singularize(word string) string
- func TemplateFS() embed.FS
- func TemplateFuncMap() template.FuncMap
- func ValidateModulePath(path string) error
- func ValidateProjectName(name string) error
- type Generator
- type GeneratorOption
- type PostProcessCallback
- type PostProcessStatus
- type PostProcessStep
- type PostProcessor
- type ProgressCallback
- type ProjectConfig
- func (c *ProjectConfig) AuthDisplayName() string
- func (c *ProjectConfig) CIDisplayName() string
- func (c *ProjectConfig) CacheDisplayName() string
- func (c *ProjectConfig) DatabaseDisplayName() string
- func (c *ProjectConfig) HasAPIKey() bool
- func (c *ProjectConfig) HasAuth() bool
- func (c *ProjectConfig) HasCI() bool
- func (c *ProjectConfig) HasCache() bool
- func (c *ProjectConfig) HasDatabase() bool
- func (c *ProjectConfig) HasDocker() bool
- func (c *ProjectConfig) HasGRPC() bool
- func (c *ProjectConfig) HasGraphQL() bool
- func (c *ProjectConfig) HasInfra() bool
- func (c *ProjectConfig) HasJWT() bool
- func (c *ProjectConfig) HasMiddleware(name string) bool
- func (c *ProjectConfig) HasPostgres() bool
- func (c *ProjectConfig) HasProtocol(protocol string) bool
- func (c *ProjectConfig) HasREST() bool
- func (c *ProjectConfig) HasSQLite() bool
- func (c *ProjectConfig) HasSSE() bool
- func (c *ProjectConfig) HasWebSocket() bool
- func (c *ProjectConfig) MiddlewareDisplayNames() []string
- func (c *ProjectConfig) ProtocolDisplayNames() []string
- func (c *ProjectConfig) Summary() string
- func (c *ProjectConfig) Validate() error
- type WriteResult
- type Writer
- func (w *Writer) BaseDir() string
- func (w *Writer) Reset()
- func (w *Writer) Skipped() []string
- func (w *Writer) SkippedCount() int
- func (w *Writer) WriteExecutable(relPath string, content []byte) (WriteResult, error)
- func (w *Writer) WriteFile(relPath string, content []byte) (WriteResult, error)
- func (w *Writer) Written() []string
- func (w *Writer) WrittenCount() int
- type WriterOption
Constants ¶
const ( ProtocolREST = "rest" ProtocolGRPC = "grpc" ProtocolGraphQL = "graphql" ProtocolWebSocket = "websocket" ProtocolSSE = "sse" )
Protocol constants define the valid protocol adapter names.
const ( DatabaseSQLite = "sqlite" DatabasePostgres = "postgres" DatabaseNone = "none" )
Database constants define the valid database backend names.
const ( AuthJWT = "jwt" AuthAPIKey = "apikey" AuthNone = "none" )
Auth constants define the valid authentication strategy names.
const ( CacheInMemory = "inmemory" CacheNone = "none" )
Cache constants define the valid cache backend names.
const ( MiddlewareRequestID = "requestid" MiddlewareLogger = "logger" MiddlewareRecover = "recover" MiddlewareRateLimiter = "ratelimit" MiddlewareCORS = "cors" MiddlewareCache = "cache" MiddlewareTimeout = "timeout" MiddlewareCircuitBreaker = "circuitbreaker" MiddlewareCoalesce = "coalesce" )
Middleware constants define the valid middleware names. The first three (RequestID, Logger, Recover) are always enabled and cannot be deselected.
const ( CIGitHubActions = "github-actions" CINone = "none" )
CI constants define the valid CI provider names.
Variables ¶
This section is empty.
Functions ¶
func AllAuthStrategies ¶
func AllAuthStrategies() []string
AllAuthStrategies returns a slice of all valid auth strategy names. Used by the TUI to populate the auth selection screen.
func AllCIProviders ¶
func AllCIProviders() []string
AllCIProviders returns a slice of all valid CI provider names.
func AllCacheStrategies ¶
func AllCacheStrategies() []string
AllCacheStrategies returns a slice of all valid cache strategy names.
func AllDatabases ¶
func AllDatabases() []string
AllDatabases returns a slice of all valid database names. Used by the TUI to populate the database selection screen.
func AllMiddleware ¶
func AllMiddleware() []string
AllMiddleware returns a slice of all valid middleware names (core + optional).
func AllProtocols ¶
func AllProtocols() []string
AllProtocols returns a slice of all valid protocol names. Used by the TUI to populate the protocol selection screen.
func CommandExists ¶
CommandExists checks whether a command is available in PATH. This can be used before post-processing to warn the user if required tools are missing.
Example:
if !generator.CommandExists("git") {
log.Warn("git not found in PATH, skipping git init")
}
func CoreMiddleware ¶
func CoreMiddleware() []string
CoreMiddleware returns the middleware names that are always enabled and cannot be deselected by the user.
func EnsureDir ¶
EnsureDir creates a directory (and all parents) if it does not exist. This is a convenience function for use outside the Writer when you need to create a directory without writing a file.
func GoVersion ¶
func GoVersion() string
GoVersion returns the installed Go version string, or "unknown" if the go binary is not found. This is used to verify compatibility before running go mod tidy.
Example:
version := generator.GoVersion() // "go1.24.2" or "unknown"
func OptionalMiddleware ¶
func OptionalMiddleware() []string
OptionalMiddleware returns the middleware names that the user can choose to enable or disable.
func Pluralize ¶
Pluralize returns the plural form of an English noun. It handles common patterns and irregular forms encountered in Go backend entity naming.
Examples:
Pluralize("task") → "tasks"
Pluralize("category") → "categories"
Pluralize("status") → "statuses"
Pluralize("address") → "addresses"
Pluralize("bus") → "buses"
Pluralize("quiz") → "quizzes"
Pluralize("wolf") → "wolves"
Pluralize("child") → "children"
func Singularize ¶
Singularize returns the singular form of an English noun. It reverses the common pluralization rules.
Examples:
Singularize("tasks") → "task"
Singularize("categories") → "category"
Singularize("statuses") → "status"
Singularize("children") → "child"
func TemplateFS ¶
TemplateFS returns the embedded filesystem containing all generator templates. This is exported so that other packages (e.g., scaffold) can access entity templates without duplicating the embed directive.
func TemplateFuncMap ¶
TemplateFuncMap returns the function map used by all generator templates. Every function here is available as {{funcName args...}} in .tmpl files.
func ValidateModulePath ¶
ValidateModulePath checks whether a Go module path is valid. This is used by the TUI text input for inline validation.
func ValidateProjectName ¶
ValidateProjectName checks whether a project name is valid. This is used by the TUI text input for inline validation before the full config exists.
Types ¶
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator is the code generation engine. It renders embedded Go templates with a ProjectConfig and writes the output to a target directory.
func NewGenerator ¶
func NewGenerator(cfg *ProjectConfig, outDir string, opts ...GeneratorOption) *Generator
NewGenerator creates a new Generator for the given config and output directory. The output directory is created if it does not exist.
Options:
- WithProgressCallback(fn) — receive notifications as files are written
- WithOverwrite(true) — overwrite existing files
func (*Generator) Generate ¶
Generate renders all applicable templates and writes the output files. It returns the list of generated file paths (relative to outDir) and any error encountered during generation.
The generation process:
- Validate the project config
- Build the template manifest based on enabled features
- For each manifest entry: a. Load the template from the embedded FS b. Parse it with the shared function map c. Render it with the ProjectConfig as template data d. Write the rendered output via the Writer e. Notify the progress callback
- Run post-processing (go mod tidy, go fmt, git init)
- Return the list of generated files
If any template fails to parse or render, Generate returns an error immediately without writing partial output for that template. Files that were already written before the error are not rolled back.
func (*Generator) GeneratedFiles ¶
GeneratedFiles returns the list of files written so far. This can be called during or after generation to inspect what was produced.
type GeneratorOption ¶
type GeneratorOption func(*Generator)
GeneratorOption is a functional option for configuring the Generator.
func WithOverwrite ¶
func WithOverwrite(force bool) GeneratorOption
WithOverwrite configures the generator to overwrite existing files.
func WithProgressCallback ¶
func WithProgressCallback(cb ProgressCallback) GeneratorOption
WithProgressCallback sets a callback that is invoked after each file is written. Used by the TUI to display real-time generation progress.
type PostProcessCallback ¶
type PostProcessCallback func(status PostProcessStatus)
PostProcessCallback is called after each post-processing step completes. The TUI progress screen uses this to display step-by-step feedback.
The callback receives the status of the just-completed step. It should return quickly — do not block in the callback.
type PostProcessStatus ¶
type PostProcessStatus struct {
// Step identifies which post-processing step this status is for.
Step PostProcessStep
// Success is true if the step completed without errors.
Success bool
// Message is a human-readable description of the outcome.
// On success: "go mod tidy completed"
// On failure: "go mod tidy failed: exit status 1"
Message string
// Duration is how long the step took to execute.
Duration time.Duration
// Output contains the combined stdout+stderr from the command, if any.
// Useful for debugging failures.
Output string
}
PostProcessStatus reports the result of a single post-processing step.
type PostProcessStep ¶
type PostProcessStep string
PostProcessStep identifies a post-processing step for progress reporting.
const ( // StepModTidy represents the `go mod tidy` step. StepModTidy PostProcessStep = "go mod tidy" // StepGoFmt represents the `go fmt ./...` step. StepGoFmt PostProcessStep = "go fmt" // StepGitInit represents the `git init` step. StepGitInit PostProcessStep = "git init" // StepGitCommit represents the initial `git add . && git commit` step. StepGitCommit PostProcessStep = "git commit" )
type PostProcessor ¶
type PostProcessor struct {
// contains filtered or unexported fields
}
PostProcessor runs post-generation steps on a generated project directory. It is created with the project directory and an optional callback for progress reporting.
func NewPostProcessor ¶
func NewPostProcessor(projectDir string, gitInit bool, callback PostProcessCallback) *PostProcessor
NewPostProcessor creates a new PostProcessor for the given project directory.
Parameters:
- projectDir: Path to the generated project directory.
- gitInit: Whether to initialize a git repository.
- callback: Optional callback for progress reporting (may be nil).
func (*PostProcessor) Results ¶
func (pp *PostProcessor) Results() []PostProcessStatus
Results returns the accumulated status of all post-processing steps.
func (*PostProcessor) Run ¶
func (pp *PostProcessor) Run(ctx context.Context) error
Run executes all post-processing steps in order. It respects the given context for cancellation. Each step is attempted regardless of whether previous steps failed (best-effort), except that git commit is skipped if git init failed.
Returns an error only if a critical step (go mod tidy) fails, since that means the generated project won't compile. Non-critical failures (go fmt, git) are reported via the callback but don't cause Run to return an error.
type ProgressCallback ¶
type ProgressCallback func(relativePath string)
ProgressCallback is called after each file is written. The argument is the relative path of the file that was just generated. This is used by the TUI generating screen to show real-time progress.
type ProjectConfig ¶
type ProjectConfig struct {
// ProjectName is the directory name and binary name for the generated
// project (e.g., "my-backend"). Must be lowercase, alphanumeric with
// hyphens, no spaces.
ProjectName string
// ModulePath is the Go module path (e.g., "github.com/user/my-backend").
// Used in go.mod and all import statements.
ModulePath string
// Description is a short human-readable description of the project.
// Used in README and package-level doc comments. Can be empty.
Description string
// Protocols lists the selected protocol adapters (e.g., ["rest", "grpc"]).
// At least one protocol must be selected.
Protocols []string
// Database is the selected database backend ("sqlite", "postgres", "none").
Database string
// Auth is the selected authentication strategy ("jwt", "apikey", "none").
Auth string
// Cache is the selected cache backend ("inmemory", "none").
Cache string
// Middleware lists the selected middleware (e.g., ["requestid", "logger",
// "recover", "ratelimit"]). The core middleware (requestid, logger, recover)
// are always present.
Middleware []string
// Docker indicates whether to generate a Dockerfile.
Docker bool
// DockerCompose indicates whether to generate a docker-compose.yml.
DockerCompose bool
// CI is the selected CI provider ("github-actions", "none").
CI string
// Kubernetes indicates whether to generate Kubernetes manifests.
Kubernetes bool
// GitInit indicates whether to run `git init` after generation.
GitInit bool
}
ProjectConfig holds all user selections from the init wizard. It is the single data structure passed to the template engine as the dot context for every template. Each field maps directly to a wizard screen.
func DefaultConfig ¶
func DefaultConfig() ProjectConfig
DefaultConfig returns a ProjectConfig with sensible defaults pre-filled. The wizard uses this as the starting point, and the user modifies it through the interactive screens.
func (*ProjectConfig) AuthDisplayName ¶
func (c *ProjectConfig) AuthDisplayName() string
AuthDisplayName returns the human-readable name for the selected auth strategy.
func (*ProjectConfig) CIDisplayName ¶
func (c *ProjectConfig) CIDisplayName() string
CIDisplayName returns the human-readable name for the selected CI provider.
func (*ProjectConfig) CacheDisplayName ¶
func (c *ProjectConfig) CacheDisplayName() string
CacheDisplayName returns the human-readable name for the selected cache.
func (*ProjectConfig) DatabaseDisplayName ¶
func (c *ProjectConfig) DatabaseDisplayName() string
DatabaseDisplayName returns the human-readable name for the selected database.
func (*ProjectConfig) HasAPIKey ¶
func (c *ProjectConfig) HasAPIKey() bool
HasAPIKey returns true if API key authentication is selected.
func (*ProjectConfig) HasAuth ¶
func (c *ProjectConfig) HasAuth() bool
HasAuth returns true if a real auth strategy (not "none") is selected.
func (*ProjectConfig) HasCI ¶
func (c *ProjectConfig) HasCI() bool
HasCI returns true if a real CI provider (not "none") is selected.
func (*ProjectConfig) HasCache ¶
func (c *ProjectConfig) HasCache() bool
HasCache returns true if a real cache backend (not "none") is selected.
func (*ProjectConfig) HasDatabase ¶
func (c *ProjectConfig) HasDatabase() bool
HasDatabase returns true if a real database backend (not "none") is selected.
func (*ProjectConfig) HasDocker ¶
func (c *ProjectConfig) HasDocker() bool
HasDocker returns true if either Dockerfile or DockerCompose is enabled.
func (*ProjectConfig) HasGRPC ¶
func (c *ProjectConfig) HasGRPC() bool
HasGRPC returns true if the gRPC protocol is selected.
func (*ProjectConfig) HasGraphQL ¶
func (c *ProjectConfig) HasGraphQL() bool
HasGraphQL returns true if the GraphQL protocol is selected.
func (*ProjectConfig) HasInfra ¶
func (c *ProjectConfig) HasInfra() bool
HasInfra returns true if any infrastructure option is enabled (Docker, DockerCompose, CI, or Kubernetes).
func (*ProjectConfig) HasJWT ¶
func (c *ProjectConfig) HasJWT() bool
HasJWT returns true if JWT authentication is selected.
func (*ProjectConfig) HasMiddleware ¶
func (c *ProjectConfig) HasMiddleware(name string) bool
HasMiddleware returns true if the given middleware is in the selected list.
func (*ProjectConfig) HasPostgres ¶
func (c *ProjectConfig) HasPostgres() bool
HasPostgres returns true if PostgreSQL is selected as the database.
func (*ProjectConfig) HasProtocol ¶
func (c *ProjectConfig) HasProtocol(protocol string) bool
HasProtocol returns true if the given protocol is in the selected list.
func (*ProjectConfig) HasREST ¶
func (c *ProjectConfig) HasREST() bool
HasREST returns true if the REST protocol is selected.
func (*ProjectConfig) HasSQLite ¶
func (c *ProjectConfig) HasSQLite() bool
HasSQLite returns true if SQLite is selected as the database.
func (*ProjectConfig) HasSSE ¶
func (c *ProjectConfig) HasSSE() bool
HasSSE returns true if the SSE protocol is selected.
func (*ProjectConfig) HasWebSocket ¶
func (c *ProjectConfig) HasWebSocket() bool
HasWebSocket returns true if the WebSocket protocol is selected.
func (*ProjectConfig) MiddlewareDisplayNames ¶
func (c *ProjectConfig) MiddlewareDisplayNames() []string
MiddlewareDisplayNames returns human-readable display names for the selected middleware. Used by the summary/review screen.
func (*ProjectConfig) ProtocolDisplayNames ¶
func (c *ProjectConfig) ProtocolDisplayNames() []string
ProtocolDisplayNames returns human-readable display names for the selected protocols. Used by the summary/review screen.
func (*ProjectConfig) Summary ¶
func (c *ProjectConfig) Summary() string
Summary returns a multi-line human-readable string summarizing all selections. Used for logging and debugging.
func (*ProjectConfig) Validate ¶
func (c *ProjectConfig) Validate() error
Validate checks that all fields contain valid values. It returns a descriptive error if any field is invalid, or nil if the config is ready for code generation.
type WriteResult ¶
type WriteResult struct {
// Path is the relative file path from the base directory.
Path string
// FullPath is the absolute file path on disk.
FullPath string
// Written is true if the file was written (or overwritten).
Written bool
// Skipped is true if the file was skipped because it already exists.
Skipped bool
// Formatted is true if the file was run through go/format.
Formatted bool
}
WriteResult describes the outcome of a single file write operation.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer handles writing rendered template content to disk. It creates directories as needed, respects existing files (unless force is set), and formats Go source files after writing.
func NewWriter ¶
func NewWriter(baseDir string, opts ...WriterOption) *Writer
NewWriter creates a new Writer that writes files under the given base directory. The base directory is created if it does not exist.
Options:
- WithForce(true) — overwrite existing files
- WithFormatGo(false) — skip go/format on .go files
func (*Writer) Reset ¶
func (w *Writer) Reset()
Reset clears the written and skipped file lists. This is useful if the writer is reused across multiple generation runs.
func (*Writer) SkippedCount ¶
SkippedCount returns the number of files skipped.
func (*Writer) WriteExecutable ¶
func (w *Writer) WriteExecutable(relPath string, content []byte) (WriteResult, error)
WriteExecutable writes content to a file and sets the executable bit. This is used for shell scripts, Makefiles, and similar files that need to be executable.
func (*Writer) WriteFile ¶
func (w *Writer) WriteFile(relPath string, content []byte) (WriteResult, error)
WriteFile writes content to a file at the given relative path under the base directory. It creates any necessary parent directories.
If the file already exists and force is false, the file is skipped and the result indicates Skipped=true. If force is true, the file is overwritten.
If the file has a .go extension and formatGo is true, the content is run through go/format before writing. If formatting fails (e.g., due to syntax errors in the template output), the unformatted content is written instead and no error is returned — the post-processing step (go fmt ./...) will catch and report formatting issues.
Returns a WriteResult describing what happened, and an error if the write itself failed (disk full, permission denied, etc.).
func (*Writer) WrittenCount ¶
WrittenCount returns the number of files successfully written.
type WriterOption ¶
type WriterOption func(*Writer)
WriterOption is a functional option for configuring the Writer.
func WithForce ¶
func WithForce(force bool) WriterOption
WithForce configures the writer to overwrite existing files.
func WithFormatGo ¶
func WithFormatGo(format bool) WriterOption
WithFormatGo configures whether .go files are formatted after writing.