README
¶
Code Generator
A template-based code generator that supports generating code from configuration files.
Features
- Support for multiple templates and configuration files
- Variable substitution
- Error reporting (including file paths and line numbers)
- Support for variables in output paths
- Quick start example generation
- Support for multiple variable files
- Sub-templates: Allow templates to include other templates, enabling template reuse and modularization.
- Template path variables: Support for variable references in output paths, e.g.,
__variable__, for more flexible file organization. - Skip child template generation: Automatically skip files with
__child__in the template file path to avoid generating unnecessary child template files. - Skip templates by suffix: Skip template files with specific suffixes, e.g.,
.go.tpl.tpl, to selectively generate certain types of files. - Skip templates by prefix: Skip template files with specific path prefixes, e.g.,
web/, to selectively generate server-side or client-side code.
Getting Started
- Ensure you have Go and the Go toolchain installed.
- Clone the repository to your local machine.
- Navigate to the
generatordirectory. - Run
go build -o generator cmd/v1/main.goto compile the generator. - Run
./generator -quickstartto generate a quick start example.
Usage
generator [options]
Options:
-dir string
Working directory path (default ".")
-output string
Output directory path (default ".gen_output")
-quickstart
Generate quick start example
-template string
Template directory path (default ".gen_templates")
-variables string
Variables directory path (default ".gen_variables")
-varfiles string
Variable files path, multiple files separated by commas
-skip-suffixes string
Skip template files with specific suffixes, multiple suffixes separated by commas
Full path (path) is used for matching
Example: -skip-suffixes=.go.tpl.tpl,.vue.tpl
-skip-prefixes string
Skip template files with specific path prefixes, multiple prefixes separated by commas
Relative to the template directory, do not include leading / character
Example: -skip-prefixes=web,server/config
Examples
-
Generate a quick start example:
./generator -quickstart -
Generate code with default configuration:
./generator -
Specify a working directory:
./generator -dir /path/to/workdir -
Customize template, variables, and output directories:
./generator -template /path/to/templates -variables /path/to/variables -output /path/to/output -
Use multiple variable files:
./generator -varfiles file1.yaml,file2.yaml -
Skip template files with specific suffixes:
./generator -skip-suffixes=.go.tpl.tpl,.vue.tpl -
Generate only server-side code (skip web templates):
./generator -skip-prefixes=web -
Generate only client-side code (skip server templates):
./generator -skip-prefixes=server
Configuration Files
The generator uses YAML format configuration files to define templates and their dependencies.
Using as a Library
The generator can be used as a library in Go projects. Import the github.com/clh021/generator/pkg/generator package and use the provided interfaces and functions.
Basic Example
package main
import (
"log"
"os"
"path/filepath"
"github.com/clh021/generator/pkg/config"
"github.com/clh021/generator/pkg/generator"
)
func main() {
// Configure the generator
cfg := &config.Config{
TemplateDir: "./templates", // Template directory
VariablesDir: "./variables", // Variables directory
OutputDir: "./output", // Output directory
VariableFiles: []string{ // Optional: specify additional variable files
"./custom_variables.yaml",
},
SkipTemplateSuffixes: ".go.tpl.tpl,.vue.tpl", // Optional: skip files with these suffixes
SkipTemplatePrefixes: "web", // Optional: skip files with these path prefixes
}
// Create a generator instance with default components
scanner := generator.NewDefaultTemplateScanner()
filter := generator.NewDefaultTemplateFilter(true, cfg.SkipTemplateSuffixes, cfg.SkipTemplatePrefixes, cfg.TemplateDir)
pathProcessor := generator.NewDefaultPathProcessor()
contentGenerator := generator.NewDefaultContentGenerator()
// Scan templates
templateFiles, err := scanner.ScanTemplates(cfg.TemplateDir, filter)
if err != nil {
log.Fatalf("Failed to scan templates: %v", err)
}
// Create template engine
engine := template.New(cfg.TemplateDir, cfg.VariablesDir, cfg.OutputDir)
// Load variables
variableLoader := generator.NewDefaultVariableLoader(cfg.TemplateDir, cfg.VariablesDir, cfg.OutputDir)
variableFiles, err := variableLoader.FindVariableFiles(cfg.VariablesDir, cfg.VariableFiles)
if err != nil {
log.Fatalf("Failed to find variable files: %v", err)
}
if err := engine.LoadVariables(variableFiles); err != nil {
log.Fatalf("Failed to load variables: %v", err)
}
variables := engine.GetVariables()
// Process each template
var generatedFiles []generator.GeneratedFile
for _, templateFile := range templateFiles {
// Process output path
outputPath, err := pathProcessor.ProcessOutputPath(templateFile, cfg.OutputDir, variables)
if err != nil {
log.Printf("Warning: Failed to process output path: %v, using default path", err)
}
// Generate content
content, err := contentGenerator.GenerateContent(templateFile, outputPath, engine)
if err != nil {
log.Fatalf("Failed to generate content: %v", err)
}
// Add to generated files list
generatedFiles = append(generatedFiles, generator.GeneratedFile{
TemplatePath: templateFile.Path,
OutputPath: outputPath,
Content: content,
})
}
// Write generated files
for _, file := range generatedFiles {
// Create output directory
outputDir := filepath.Dir(file.OutputPath)
if err := os.MkdirAll(outputDir, 0755); err != nil {
log.Fatalf("Failed to create output directory: %v", err)
}
// Create output file
if err := os.WriteFile(file.OutputPath, []byte(file.Content), 0644); err != nil {
log.Fatalf("Failed to write file: %v", err)
}
log.Printf("Generated file: %s", file.OutputPath)
}
log.Println("Generation completed")
}
Modular Components
The generator provides several interfaces that can be implemented to customize the generation process:
1. Template Scanner
The TemplateScanner interface is responsible for scanning the template directory and finding template files:
// TemplateScanner defines the template scanner interface
type TemplateScanner interface {
// ScanTemplates scans the template directory and returns a list of template files
ScanTemplates(templateDir string, filter TemplateFilter) ([]TemplateFile, error)
}
Example of a custom template scanner:
// CustomScanner is a custom template scanner
type CustomScanner struct {
IncludePatterns []string
}
// ScanTemplates scans the template directory and returns a list of template files
func (s *CustomScanner) ScanTemplates(templateDir string, filter TemplateFilter) ([]generator.TemplateFile, error) {
var templateFiles []generator.TemplateFile
// Custom scanning logic...
return templateFiles, nil
}
2. Template Filter
The TemplateFilter interface is responsible for filtering template files:
// TemplateFilter defines the template filter interface
type TemplateFilter interface {
// ShouldInclude checks if a template file should be included
// Returns: (should include, reason for exclusion)
ShouldInclude(path, relativePath string) (bool, string)
}
Example of a custom template filter:
// CustomFilter is a custom template filter
type CustomFilter struct {
*generator.DefaultTemplateFilter
AllowedExtensions []string
}
// ShouldInclude checks if a template file should be included
func (f *CustomFilter) ShouldInclude(path, relativePath string) (bool, string) {
// First use the default filter
include, reason := f.DefaultTemplateFilter.ShouldInclude(path, relativePath)
if !include {
return false, reason
}
// Then apply custom filtering logic
// ...
return true, ""
}
3. Path Processor
The PathProcessor interface is responsible for processing output paths:
// PathProcessor defines the path processor interface
type PathProcessor interface {
// ProcessOutputPath processes the output path for a template file
ProcessOutputPath(templateFile TemplateFile, outputDir string, variables map[string]interface{}) (string, error)
}
Example of a custom path processor:
// CustomPathProcessor is a custom path processor
type CustomPathProcessor struct {
*generator.DefaultPathProcessor
PathPrefix string
}
// ProcessOutputPath processes the output path for a template file
func (p *CustomPathProcessor) ProcessOutputPath(templateFile generator.TemplateFile, outputDir string, variables map[string]interface{}) (string, error) {
// Use the default processor
path, err := p.DefaultPathProcessor.ProcessOutputPath(templateFile, outputDir, variables)
if err != nil {
return "", err
}
// Add custom prefix
if p.PathPrefix != "" {
path = filepath.Join(p.PathPrefix, path)
}
return path, nil
}
4. Content Generator
The ContentGenerator interface is responsible for generating content from templates:
// ContentGenerator defines the content generator interface
type ContentGenerator interface {
// GenerateContent generates content for a template file
GenerateContent(templateFile TemplateFile, outputPath string, engine *template.Engine) (string, error)
}
Example of a custom content generator:
// CustomContentGenerator is a custom content generator
type CustomContentGenerator struct {
AddGeneratedComment bool
CommentPrefix string
}
// GenerateContent generates content for a template file
func (g *CustomContentGenerator) GenerateContent(templateFile generator.TemplateFile, outputPath string, engine *template.Engine) (string, error) {
// Generate content
content, err := engine.GenerateContent(templateFile.Path, outputPath)
if err != nil {
return "", err
}
// Add generated comment
if g.AddGeneratedComment {
// Add comment based on file type
// ...
}
return content, nil
}
5. Variable Loader
The VariableLoader interface is responsible for loading variables:
// VariableLoader defines the variable loader interface
type VariableLoader interface {
// LoadVariables loads variables from the variables directory and additional files
LoadVariables(variablesDir string, additionalFiles []string) (map[string]interface{}, error)
// FindVariableFiles finds variable files in the variables directory and additional files
FindVariableFiles(variablesDir string, additionalFiles []string) ([]string, error)
}
Example of a custom variable loader:
// CustomVariableLoader is a custom variable loader
type CustomVariableLoader struct {
*generator.DefaultVariableLoader
ExtraVariables map[string]interface{}
}
// LoadVariables loads variables from the variables directory and additional files
func (l *CustomVariableLoader) LoadVariables(variablesDir string, additionalFiles []string) (map[string]interface{}, error) {
// Load variables using the default loader
variables, err := l.DefaultVariableLoader.LoadVariables(variablesDir, additionalFiles)
if err != nil {
return nil, err
}
// Add extra variables
for k, v := range l.ExtraVariables {
variables[k] = v
}
return variables, nil
}
Simplified Approach
For a more simplified approach, you can create custom functions that encapsulate the generation process:
// generateFiles generates files using a custom content generator
func generateFiles(cfg *config.Config, contentGenerator ContentGenerator) ([]generator.GeneratedFile, error) {
var generatedFiles []generator.GeneratedFile
// Template scanning, variable loading, and content generation logic...
return generatedFiles, nil
}
This approach allows you to focus on the specific part of the generation process that you want to customize, while reusing the rest of the logic.
Template Features
- Built-in string processing functions (
lcfirst,ucfirst,default,file,currentYear,dict) - Support for variables in output paths, e.g.,
__variableName__. - Sub-templates: Use
{{ include "path/to/sub_template.tpl" . }}to include other template files in a template. Sub-templates can access variables from the parent template. Maximum nesting depth is limited to 2 levels to prevent circular references.
Sub-template Usage Instructions
-
Path lookup: Sub-template paths are first looked up as relative paths to the parent template. If an absolute path is specified, it is used directly.
-
Circular references: The nesting depth of sub-templates is limited to 2 levels, beyond which an error will be reported.
-
Variable passing: Sub-templates can access variables defined in the parent template.
-
Sub-template naming: To prevent sub-templates from being generated independently, include the string
__child__in the sub-template file name or path. Template files containing__child__will be automatically skipped during generation with a notification. For example:child__child__.tplor__child__/template.tpl.
Error Handling
The generator provides detailed error reports, including file paths and line numbers.
Contributing
Issues and pull requests are welcome.
License
This project is licensed under the MIT License.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
v1
command
|
|
|
v2
command
|
|
|
examples
|
|
|
custom_content_generator
command
|
|
|
custom_generator
command
|
|
|
custom_scanner
command
|
|
|
simplified_scanner
command
|
|
|
simplified_variable_loader
command
|
|
|
internal
|
|
|
pkg
|
|
|
utils
Package utils provides utility functions for the generator package.
|
Package utils provides utility functions for the generator package. |