Code Generation
Code generation is central to hof
.
hof
’s ad-hoc code generation feature enables developers to
generate files for any programming language or framework
by rendering templates with data.
This page introduces code generation through hof
.
With hof gen
, you can combine CUE, Yaml, or JSON arguments
with templates to generate files and directories of code automatically.
Through flags and configurations, you have complete control over
the file generation process and the final content created.
hof
’s template-based code generation has two approaches:
ad-hoc and a more sophisticated configuration or module-based method.
hof gen -T
is code gen from flagshof gen -G
is code gen from config
In this section, we will explore the ad-hoc method which used the -T
flag.
The first example is a step-by-step guide
on creating a generator using CUE based configuration.
The getting-started/create section
will introduce hof create
, which enables running generators directly from git repositories.
Each of these methods utilizes the same core concepts and processes, and they are suitable for different use cases:
hof gen -T
is ideal for simpler scenarios when just a few files are involved.hof gen -G
is designed for large-scale code generation and reusable modules.hof create
is intended for one-time setup and application bootstrapping.
Code generation topics are discussed in a dedicated section.
Data + Templates
By running the command hof gen interlude.json -T interlude.template
,
users can perform ad-hoc template rendering to combine any data source with any template.
hof
’s templates are built on Go’s text/template
package with extra helpers added.
By using a hyphen symbol “-
”, you can stream any data into hof gen
.
This feature can be helpful when you need to render an API response or process the output of a command.
Writing to file
Use =
(equal) to write to a file by name.
If you want to write all outputs to a directory, use the -O
flag.
Files will have the same name as the template if not set individually.
You can set the filename from data by using an inline template as the output name. Make sure you “wrap it in quotes”.
You can combine these options to control the output directory and the filename.
Write Data Files
Omit the template path at the beginning
and hof
will infer the data format
from the output file extension
Multiple Templates
You can use the -T
flag multiple times.
Each is independent and can have different options for
the data and schemas from the CUE entrypoints. (we’ll see this below)
Watching for Changes
Use the -w/--watch
flag to watch for changes and re-render output.
Think of the -w/--watch
flag as a live-reload option that
monitors your code and automatically re-renders your output when changes are detected.
There are additional watch flags available if automatic detection misses files.
On Using CUE
hof
’s inputs are cue
’s inputs, or “CUE entry points”.
The inputs hold CUE values, which can be intermixed with your data to apply schemas, enrich the data, or transform before rendering.
When running commands, the CUE entrypoints are combined into a single CUE value.
If you want to place data files in a specific path, use the @path.to.location
syntax.
The final data passed to a template must be concrete or fully specified. This means the value needs to be like JSON data before template rendering accepts them. As you will see, hof provides flexibility and control for how the CUE values are selected, combined, and mapped to templates.
We keep parity with cue
, so tooling from the broader ecosystem
still works on our inputs and reduces context-switching costs.
With hof, you can take advantage of all the possibilities and power of CUE
for selecting, combining, and mapping the values to templates for rendering.
You can safely use all the possibilities and power of CUE here.
Setup for Examples
We will be using the following inputs in the examples below. We define a schema and write our types as data values in CUE.
Schema & Data
We can use cue
to see what the full data looks like
$ cue export data.cue schema.cue
Starting Template
Controlling Code Generation
You can control how input data and schemas are combined with templates
and written to files by using the flexible format of the -T
flag for code generation.
Selecting Values and Schemas
Use :<path>
to select a value and @<path>
to apply a schema
We can remove the .Input
from our templates and pick the data and schema with flags.
This approach can be helpful when we don’t have control over the input data or if it comes in a data format.
Partial Templates
Partial templates are fragments that are used in other templates. Unlike regular templates, these do not map to an output file. You can capture repeated sections like the fields of a struct or the arguments to a function.
Additionally, partials can invoke other partials, which is helpful for modularizing templates into logical components.
There are two ways to define and use partial templates:
- Use the
{{ define "name" }}
syntax in a regular template - User the
-P
to load them from a file
For example, we could extract the generation of fields into its template. We won’t here, but as an example could include complex tasks like struct tags for Go fields.
Repeated Templates
In addition to looping over data and applying a template fragment, you can use repeated templates to write a file for each element in a CUE or data iterable, such as a list or struct field. This is useful for creating a separate file for each item, be it a type, API route, UI component, or DB migration.
To render a file for each element in the input to a -T
flag, use []
.
Understanding the -T flag
The -T
flag in hof gen has a flexible format that
allows you to customize your templates’ input data, schema, and output path.
-T "<template-path>:<input-path>[@schema-path]=<out-path>"
This flag allows you to
- Render multiple templates by using
-T
more than once - Select a value with
:<input-path>
- Select a schema with
@<schema-path>
- Write to file with
=<out-path>
- Control the output filename with
="{{ .name }}.txt"
- Render a single template multiple times with
="[]{{ .filepath }}"
You can find more examples in the hof ad-hoc render tests.
Generators and Modules
Hof Generators are CUE code that define a hof gen
command,
defined in CUE modules and shared with Git repositories.
Turn any ad-hoc hof gen ... -T ...
args and flags into a generator by
adding --as-module <module name>
to the end.
This will generate the equivalent CUE code version for the command.
You can now run `hof gen
Several files are generated, including a CUE file that houses your generator and additional files for configuring a CUE module.
The next page will cover modules in general and the the-walkthrough is a walkthrough on creating a full-stack application generator from scratch.