Generator



Generators specify the mapping from inputs to templates. They will have input schema(s) and some configuration options for users, and 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] | *[...]
	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

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 | *"./"

	// Required fields for hof
	// ------------------------

	// In is passed to every template
	In: {
		SERVER: Server
	}

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

	_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"
		},
	]

	// 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 or sub-routes in the "full-example" section

	...
}
2023 Hofstadter, Inc