Generator



Generators specify the mapping from inputs through templates to output files. Using the schema and user input, you decide what files to generate, what inputs to provide to templates, and can reshape or enrich the inputs. Your generator file definds this process to map the In field to the Out field for hof to process.


Generator Schema

HofGenerators and Files have their own schemas. Only the core fields are shown here. You can find the real schemas in the hof repository.

Generator Schemas

// Definition for a generator
Generator: {
	// Base directory for the output
	Outdir: string | *"./"

	// "Global" input, merged with In per file before generating output
	In: {...} | *{...}

	// you may also supply additional user config
	// often you will construct "In" from
	// more meaningful fields presented to the user

	// The list fo files for hof to generate
	Out: [...File] | *[]

	// Template (top-level) TemplateConfig (globs+config)
	Templates: [...Templates] | *[Templates & {Globs: ["./templates/**/*"], TrimPrefix: "./templates/"}]

	// Partial (nested) TemplateConfig (globs+config)
	Partials: [...Templates] | *[Templates & {Globs: ["./partials/**/*"], TrimPrefix: "./partials/"}]

	// Statics are copied directly into the output, bypassing the rendering
	Statics: [...Statics] | *[Statics & {Globs: ["./static/**/*"], TrimPrefix: "./static/"}]

	// ... other fields for generator writers
}

// A file which should be generated by hof
File: {
	// The local input data, defaults to the generator In fields
	In: {...}

	// The full path under the output location
	// empty implies don't generate, even though it may endup in the list
	Filepath: string | *""

	// One of the following should be set
	// The template contents
	TemplateContent: string | *""
	// Path for the loaded templates
	TemplatePath: string | *""

	// ... advanced template configuration
}

Mapping In to Out

Your goal as a generator writer is to fill in the Out field from the In field. The In field will be passed through the templates and partials if they are defined in the Out list.

In represents the data presented to the templates.
  • You can have your users set values in meaningful fields and collect them
  • You can add any other separate or calculated data
  • The top-level In is passed to all templates and can be extended with the per-template field
Out holds the files to be rendered by hof
  • Is a list of HofGeneratorFiles, where each…
  • Has a filepath for where it will be written under Outdir
  • Has an In value which will be unified with the top-level

There are typically two types of files (templates)

  • Files which are generated once per application. Think of a main.go or index.js. We often call these “once” files or templates.
  • Files which are generated for each sub-resource. These might be routes in a server or commands in a CLI. We often call these “repeated” files or templates.
You can use as many helper fields or calculations as you want
  • Calculate commonly used fields or values for In
  • Interpolate common base paths for filepaths
  • Create file lists for repeated templates.

Server Generator

The following is the generator for our simple REST server.

gen/server.cue

package gen

import (
	"github.com/hofstadter-io/hof/schema/gen"

	"hof.io/docs/example/schema"
)

// Generator definition
Generator: gen.Generator & {

	// User inputs to this generator
	// -----------------------------

	// The server design conforming to the server schema
	Server: schema.Server

	// Base output directory, defaults to current
	Outdir: string | *"./"

	// In & Out fields for hof
	// ------------------------

	// In is passed to every template as the root keys
	In: {
		// a convention for making root keys stand out visually
		SERVER: Server
	}

	// Actual files generated by hof, combined into a single list
	Out: [...gen.File] & _All

	// Everything below here is for convenience
	_All: [
		for _, F in _OnceFiles {F},
		for _, F in _RouteFiles {F},
	]

	// Note, we can omit Templates, Partials, and Statics
	// since the default values are sufficient for us

	// Internal fields for mapping Input to templates
	// ----------------------------------------------

	// Files that are generated once per server
	_OnceFiles: [...gen.File] & [
			{
			TemplatePath: "go.mod"
			Filepath:     "go.mod"
		},
		{
			TemplatePath: "server.go"
			Filepath:     "server.go"
		},
		{
			TemplatePath: "router.go"
			Filepath:     "router.go"
		},
		{
			TemplatePath: "middleware.go"
			Filepath:     "middleware.go"
		},
		// a conditional file
		if Server.Auth != _|_ {
			TemplatePath: "auth.go"
			Filepath:     "auth.go"
		},
	]

	// Routes, we create a file per route in the Server
	_RouteFiles: [...gen.File] & [
			for _, R in Server.Routes {
			In: {
				ROUTE: {
					R
				}
			}
			TemplatePath: "route.go"
			Filepath:     "routes/\(In.ROUTE.Name).go"
		},
	]

	// We'll see how to handle nested routes and resources in upcoming sections
}