Documentation
¶
Overview ¶
Package scaffold implements entity scaffolding for the `nexus add entity` command.
This file contains the core scaffolding engine: it loads the project manifest, renders entity templates with the EntityConfig context, writes generated files to the correct directories, updates the manifest with the new entity, and optionally patches main.go to wire the new entity's operations.
Architecture:
CLI (add_entity.go) → EntityScaffolder.Scaffold()
│
├── Load manifest (nexus.yml)
├── Build EntityConfig from manifest + entity definition
├── Render entity templates (embedded via go:embed)
├── Write output files via generator.Writer
├── Update nexus.yml with new entity
├── Patch main.go to wire entity operations
└── Run go fmt / go mod tidy
Design Decisions:
- Entity templates are embedded separately from the main generator templates. They live in templates/entity/ and are loaded via the same embed.FS.
- The scaffolder does NOT re-run the full generator. It only creates the entity-specific files and patches main.go. This means it's fast and safe to run on an existing project without risking overwrites.
- main.go patching uses simple string insertion rather than AST manipulation. This is pragmatic: the generated main.go has well-known marker comments that make insertion points predictable. Full AST rewriting would be more robust but dramatically more complex for marginal benefit.
Package scaffold implements entity scaffolding for the `nexus add entity` command.
This file defines the EntityConfig type — the template rendering context used by all entity templates. Unlike the main generator which uses ProjectConfig as its template data, entity templates receive an EntityConfig that combines entity-specific metadata (name, fields, options) with project-level context (module path, protocols, database) needed for import paths and conditional code generation.
Design Decisions:
- EntityConfig is a flat struct rather than embedding ProjectConfig because entity templates need a curated subset of project info, not the full config.
- String casing variants (PascalCase, camelCase, snake_case, etc.) are precomputed and stored as fields rather than computed in templates. This makes templates cleaner and avoids repeated function calls.
- Field metadata includes precomputed Go types, SQL types, proto types, and GraphQL types so templates can use them directly without calling methods.
Index ¶
- type EntityConfig
- func (c *EntityConfig) DatabaseImportPath() string
- func (c *EntityConfig) EntityImportPath() string
- func (c *EntityConfig) HasFilterableFields() bool
- func (c *EntityConfig) HasStringFields() bool
- func (c *EntityConfig) MeshImportPath() string
- func (c *EntityConfig) ProtoGoPackage() string
- func (c *EntityConfig) ProtoPackageName() string
- func (c *EntityConfig) RepositoryImportPath() string
- func (c *EntityConfig) ServiceImportPath() string
- func (c *EntityConfig) SoftDeleteProtoField() int
- func (c *EntityConfig) TableName() string
- func (c *EntityConfig) TimestampProtoFields() (createdAt, updatedAt int)
- type EntityScaffolder
- type EnumFieldConfig
- type EnumValueConfig
- type FieldConfig
- type ProgressCallback
- type ScaffolderOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EntityConfig ¶
type EntityConfig struct {
// EntityName is the PascalCase entity name (e.g., "User", "BlogPost").
EntityName string
// EntityNameLower is the lowercase entity name (e.g., "user", "blogpost").
EntityNameLower string
// EntityNameCamel is the camelCase entity name (e.g., "user", "blogPost").
EntityNameCamel string
// EntityNameSnake is the snake_case entity name (e.g., "user", "blog_post").
EntityNameSnake string
// EntityNameKebab is the kebab-case entity name (e.g., "user", "blog-post").
EntityNameKebab string
// EntityNamePlural is the pluralized PascalCase name (e.g., "Users", "BlogPosts").
EntityNamePlural string
// EntityNamePluralLower is the pluralized lowercase name (e.g., "users", "blogposts").
EntityNamePluralLower string
// EntityNamePluralSnake is the pluralized snake_case name (e.g., "users", "blog_posts").
EntityNamePluralSnake string
// EntityNamePluralKebab is the pluralized kebab-case name (e.g., "users", "blog-posts").
EntityNamePluralKebab string
// EntityNamePluralCamel is the pluralized camelCase name (e.g., "users", "blogPosts").
EntityNamePluralCamel string
// ReceiverName is the short receiver variable (first letter lowercase, e.g., "u" for User).
ReceiverName string
// Entity is the full entity definition from the manifest.
Entity manifest.Entity
// Fields is the enriched field list with precomputed type information.
Fields []FieldConfig
// RequiredFields contains only the fields marked as required.
RequiredFields []FieldConfig
// OptionalFields contains only the fields NOT marked as required.
OptionalFields []FieldConfig
// UniqueFields contains only the fields marked as unique.
UniqueFields []FieldConfig
// EnumFields contains only the fields with enum type.
EnumFields []EnumFieldConfig
// HasTimestamps is true if CreatedAt/UpdatedAt should be generated.
HasTimestamps bool
// HasSoftDelete is true if soft deletion support should be generated.
HasSoftDelete bool
// TimestampCreatedAtFieldNum is the protobuf field number for created_at.
TimestampCreatedAtFieldNum int
// TimestampUpdatedAtFieldNum is the protobuf field number for updated_at.
TimestampUpdatedAtFieldNum int
// SoftDeleteFieldNum is the protobuf field number for deleted_at.
SoftDeleteFieldNum int
// ProjectName is the project name (e.g., "my-backend").
ProjectName string
// ModulePath is the Go module path (e.g., "github.com/user/my-backend").
ModulePath string
// Protocols is the list of enabled protocol names.
Protocols []string
// HasREST is true if the REST protocol is enabled.
HasREST bool
// HasGRPC is true if the gRPC protocol is enabled.
HasGRPC bool
// HasGraphQL is true if the GraphQL protocol is enabled.
HasGraphQL bool
// HasWebSocket is true if the WebSocket protocol is enabled.
HasWebSocket bool
// HasSSE is true if the SSE protocol is enabled.
HasSSE bool
// Database is the selected database backend ("sqlite", "postgres", "none").
Database string
// HasDatabase is true if any database (not "none") is selected.
HasDatabase bool
// HasSQLite is true if SQLite is the selected database.
HasSQLite bool
// HasPostgres is true if PostgreSQL is the selected database.
HasPostgres bool
// Auth is the selected auth strategy ("jwt", "apikey", "none").
Auth string
// NeedsTimeImport is true if the "time" package is needed.
NeedsTimeImport bool
// NeedsJSONImport is true if the "encoding/json" package is needed.
NeedsJSONImport bool
// NeedsStringsImport is true if the "strings" package is needed (nearly always).
NeedsStringsImport bool
// NeedsSyncImport is true if the "sync" package is needed (in-memory repo).
NeedsSyncImport bool
}
EntityConfig is the data passed to every entity template during rendering. It contains everything the template needs to generate code for a single domain entity across all layers.
func NewEntityConfig ¶
func NewEntityConfig(m *manifest.Manifest, entity manifest.Entity) *EntityConfig
NewEntityConfig builds an EntityConfig from a manifest and entity definition. It precomputes all casing variants, type mappings, and flags so that templates can use them directly without complex logic.
func (*EntityConfig) DatabaseImportPath ¶
func (c *EntityConfig) DatabaseImportPath() string
DatabaseImportPath returns the full Go import path for the database package.
func (*EntityConfig) EntityImportPath ¶
func (c *EntityConfig) EntityImportPath() string
EntityImportPath returns the full Go import path for the entity package.
func (*EntityConfig) HasFilterableFields ¶
func (c *EntityConfig) HasFilterableFields() bool
HasFilterableFields reports whether any field is filterable in queries.
func (*EntityConfig) HasStringFields ¶
func (c *EntityConfig) HasStringFields() bool
HasStringFields reports whether any field is a string or text type. Used by templates to determine if search filtering is relevant.
func (*EntityConfig) MeshImportPath ¶
func (c *EntityConfig) MeshImportPath() string
MeshImportPath returns the full Go import path for the mesh package.
func (*EntityConfig) ProtoGoPackage ¶
func (c *EntityConfig) ProtoGoPackage() string
ProtoGoPackage returns the Go package option for the proto file (e.g., "github.com/user/my-backend/proto/user/v1;userv1").
func (*EntityConfig) ProtoPackageName ¶
func (c *EntityConfig) ProtoPackageName() string
ProtoPackageName returns the protobuf package name for this entity (e.g., "user.v1").
func (*EntityConfig) RepositoryImportPath ¶
func (c *EntityConfig) RepositoryImportPath() string
RepositoryImportPath returns the full Go import path for the repository package.
func (*EntityConfig) ServiceImportPath ¶
func (c *EntityConfig) ServiceImportPath() string
ServiceImportPath returns the full Go import path for the service package.
func (*EntityConfig) SoftDeleteProtoField ¶
func (c *EntityConfig) SoftDeleteProtoField() int
SoftDeleteProtoField returns the proto field number for deleted_at.
func (*EntityConfig) TableName ¶
func (c *EntityConfig) TableName() string
TableName returns the database table name for this entity (snake_case, pluralized).
func (*EntityConfig) TimestampProtoFields ¶
func (c *EntityConfig) TimestampProtoFields() (createdAt, updatedAt int)
TimestampProtoFields returns the proto field numbers for created_at and updated_at. These come after all entity fields.
type EntityScaffolder ¶
type EntityScaffolder struct {
// contains filtered or unexported fields
}
EntityScaffolder generates entity files for a Nexus project.
func NewEntityScaffolder ¶
func NewEntityScaffolder(projectDir string, opts ...ScaffolderOption) (*EntityScaffolder, error)
NewEntityScaffolder creates a new EntityScaffolder for the given project directory.
It loads the nexus.yml manifest from the project directory. If the manifest is missing or invalid, an error is returned.
func (*EntityScaffolder) GeneratedFiles ¶
func (s *EntityScaffolder) GeneratedFiles() []string
GeneratedFiles returns the list of files that were written during scaffolding.
func (*EntityScaffolder) Manifest ¶
func (s *EntityScaffolder) Manifest() *manifest.Manifest
Manifest returns the (possibly updated) project manifest.
func (*EntityScaffolder) Scaffold ¶
Scaffold generates all entity files for the given entity definition.
It performs the following steps:
- Validate the entity definition
- Check the entity doesn't already exist in the manifest
- Build the EntityConfig template context
- Render all applicable entity templates
- Write output files
- Update nexus.yml with the new entity
- Patch main.go to wire the new entity
- Run go fmt and go mod tidy
Returns the list of generated file paths and any error.
type EnumFieldConfig ¶
type EnumFieldConfig struct {
// FieldName is the original snake_case name (e.g., "status").
FieldName string
// FieldNamePascal is the PascalCase name (e.g., "Status").
FieldNamePascal string
// TypeName is the full Go type name (e.g., "UserStatus").
TypeName string
// Values is the list of enum values with their Go constant names.
Values []EnumValueConfig
}
EnumFieldConfig provides all the information needed to generate enum types for a single enum field. It precomputes type names and value identifiers so templates don't need complex logic.
type EnumValueConfig ¶
type EnumValueConfig struct {
// Value is the raw string value (e.g., "pending", "in_progress").
Value string
// ConstName is the Go constant name (e.g., "UserStatusPending").
ConstName string
// Display is a human-readable version (e.g., "Pending", "In Progress").
Display string
}
EnumValueConfig describes a single enum value with its various representations.
type FieldConfig ¶
type FieldConfig struct {
// Name is the original snake_case field name (e.g., "first_name").
Name string
// NamePascal is the PascalCase field name (e.g., "FirstName").
NamePascal string
// NameCamel is the camelCase field name (e.g., "firstName").
NameCamel string
// Type is the abstract field type from the manifest.
Type manifest.FieldType
// GoType is the Go type string (e.g., "string", "int", "time.Time").
GoType string
// SQLType is the SQL column type (e.g., "TEXT", "INTEGER", "TIMESTAMP").
SQLType string
// ProtoType is the protobuf scalar type (e.g., "string", "int64", "bool").
ProtoType string
// GraphQLType is the GraphQL scalar type (e.g., "String", "Int", "Boolean").
GraphQLType string
// Required is true if the field must be provided on create.
Required bool
// Unique is true if the field must be unique across all entities.
Unique bool
// IsEnum is true if this field is an enum type.
IsEnum bool
// IsSlice is true if this field is a slice type ([]string).
IsSlice bool
// IsTime is true if this field is a time.Time type.
IsTime bool
// IsJSON is true if this field is a json.RawMessage type.
IsJSON bool
// ZeroValue is the Go zero value for this type (e.g., `""`, `0`, `false`).
ZeroValue string
// EnumValues holds the allowed values for enum fields.
EnumValues []string
// ProtoFieldNumber is the field number for protobuf messages (1-based,
// after the ID field which is always 1).
ProtoFieldNumber int
}
FieldConfig extends the manifest.Field with precomputed type mappings and casing variants. Templates use these directly rather than calling methods.
type ProgressCallback ¶
type ProgressCallback func(message string)
ProgressCallback is called after each file is written or action is performed. The argument is a human-readable description of what happened.
type ScaffolderOption ¶
type ScaffolderOption func(*EntityScaffolder)
ScaffolderOption is a functional option for configuring the EntityScaffolder.
func WithForce ¶
func WithForce(force bool) ScaffolderOption
WithForce configures the scaffolder to overwrite existing files.
func WithProgress ¶
func WithProgress(cb ProgressCallback) ScaffolderOption
WithProgress sets a callback for progress notifications.