with one click
zio-golem-code-generation
// Generating Scala code in the golem subproject. Use when adding code generation steps, build-time source generators, scalameta AST construction, or sbt/Mill sourceGenerators to the golem/ subtree.
// Generating Scala code in the golem subproject. Use when adding code generation steps, build-time source generators, scalameta AST construction, or sbt/Mill sourceGenerators to the golem/ subtree.
Explains the zio-golem WIT folder structure and how to regenerate the agent_guest.wasm base image. Use when working with WIT definitions, upgrading Golem versions, or regenerating the guest runtime WASM.
Compile, publish, and test the ZIO Golem Scala.js SDK. Use when working on the golem/ subtree: building the SDK, publishing locally, compiling/running the example demo, regenerating the agent_guest.wasm, or debugging end-to-end deployment.
Run and debug Golem Scala SDK integration tests. Use when running golem integration tests, debugging test failures, or working with GolemExamplesIntegrationSpec.
| name | zio-golem-code-generation |
| description | Generating Scala code in the golem subproject. Use when adding code generation steps, build-time source generators, scalameta AST construction, or sbt/Mill sourceGenerators to the golem/ subtree. |
Guidelines for writing Scala code generators in the golem subproject.
Always construct generated code as scalameta AST nodes using quasiquotes (q"...", t"...", source"...", param"..."). Never use string interpolation or text templates to produce Scala source files.
// ✅ Correct — typed AST
val tree = q"""
object $name {
def register(): Unit = {
..$registrations
()
}
}
"""
// ❌ Wrong — string interpolation
val code = s"""object $name {
def register(): Unit = { ... }
}"""
golem/codegen/)All build-time code generation logic lives in golem/codegen/, a pure (no ZIO, no sbt, no Mill) Scala library that cross-compiles to Scala 2.12 (for sbt) and Scala 3.3.7 (for Mill). Both plugins depend on this shared library.
Because the library must compile under Scala 2.12:
import scala.meta.dialects.Scala213 for the implicit dialect needed by quasiquotes and .parse[T] calls.dialects.Scala3(code).parse[Source] (explicit dialect application) rather than implicit val d: Dialect = ... which causes ambiguity.parseMeta[T](code)(implicit parse: Parse[T]) helper pattern for snippet parsing.Parse dotted strings into scalameta AST nodes for use in quasiquotes:
private def parseMeta[T](code: String)(implicit parse: Parse[T]): T =
Scala213(code).parse[T].get
private def parseTermRef(dotted: String): Term.Ref =
parseMeta[Term](dotted).asInstanceOf[Term.Ref]
private def parseType(tpe: String): Type =
parseMeta[Type](tpe)
private def parseImporter(dotted: String): List[Importer] =
parseMeta[Stat](s"import $dotted").asInstanceOf[Import].importers
Generators should expose a pure, effect-free API that accepts source text and returns generated outputs + diagnostics:
object MyCodegen {
final case class GeneratedFile(relativePath: String, content: String)
final case class Warning(path: Option[String], message: String)
final case class Result(files: Seq[GeneratedFile], warnings: Seq[Warning])
def generate(inputs: ...): Result = {
// 1. Parse/scan inputs
// 2. Build scalameta AST via quasiquotes
// 3. Pretty-print via .syntax
// 4. Return GeneratedFile with relative path + content
}
}
The plugin wrappers (sbt/Mill) handle file I/O, logging, and build-tool integration.
The sbt plugin GolemPlugin is an AutoPlugin compiled as part of the meta-build via ProjectRef in project/plugins.sbt. It hooks into sourceGenerators:
Compile / sourceGenerators += Def.task {
val inputs = scalaSources.map { f =>
MyCodegen.SourceInput(f.getAbsolutePath, IO.read(f))
}
val result = MyCodegen.generate(inputs)
result.warnings.foreach(w => log.warn(s"[golem] ${w.message}"))
result.files.map { gf =>
val out = managedRoot / gf.relativePath
IO.write(out, gf.content)
out
}
}.taskValue
For new generators:
golem/codegen/src/main/scala/golem/codegen/.golem/sbt/src/main/scala/golem/sbt/.Compile / sourceGenerators as a .taskValue.FileFunction.cached with FileInfo.hash if the generation has an input file (schema, WIT, etc.) to avoid unnecessary regeneration.The Mill plugin GolemAutoRegister is a trait mixed into ScalaJSModule. It uses generatedSources and T { ... } tasks. Follow the same pattern as golemGeneratedAutoRegisterSources.
All generation logic lives in golem/codegen/. The sbt and Mill plugins are thin wrappers that:
generate(...) functionWhen adding a new generation step, implement the logic once in golem/codegen/, then add thin wrappers in both GolemPlugin.scala and GolemAutoRegister.scala.
Scans sources for @agentImplementation classes using scalameta's parser, then generates RegisterAgents.scala and per-package __GolemAutoRegister_*.scala files using scalameta quasiquotes.
Files:
golem/codegen/src/main/scala/golem/codegen/autoregister/AutoRegisterCodegen.scala — shared logicgolem/sbt/src/main/scala/golem/sbt/GolemPlugin.scala — sbt wrappergolem/mill/src/golem/mill/GolemAutoRegister.scala — Mill wrapperMacros generate code at compile time, not as a build step. They live in golem/macros/ and use scala.quoted.*:
AgentDefinitionMacro — extracts AgentMetadata from @agentDefinition traitsAgentImplementationMacro — generates implementation wrappers from @agentImplementation classesAgentClientMacro — generates RPC client typesAgentCompanionMacro — generates companion object boilerplate (get, getPhantom, etc.)These are not build-time code generators. Do not confuse them with sourceGenerators.
Follow this pipeline for new generators:
1. Load schema/input (WIT file, annotation scan, external spec)
2. Parse into models (typed case classes, not raw strings)
3. Classify/transform (determine what code to emit)
4. Build AST (scalameta quasiquotes)
5. Pretty-print (.syntax on the AST root)
6. Return (GeneratedFile with relativePath + content)
The plugin wrappers handle file writing, formatting, and incremental build integration.
ModelGenerator, ClientGenerator, etc.) and mix them into the main codegen class if complexity warrants it.dialects.Scala3 for parsing user sources (with Scala213 fallback). Use Scala213 for quasiquote construction (compatible with both 2.12 and 3.x codegen host)./** Generated. Do not edit. */ as a comment in generated objects/classes.sourceManaged (sbt) or T.dest (Mill), never to source directories.