Plugin Development
Most teams never need this page. If the built-in plugins already generate what you need, use them.
Plugin development is the advanced path for building Typeweaver plugins that hook into the generation lifecycle and emit new artifact families from the same spec: framework adapters, infra helpers, custom clients, internal SDK glue, and similar outputs.
When to build a plugin
Build a plugin when all of these are true:
- the output should stay tied to the Typeweaver contract
- regeneration should happen as part of the normal
typeweaver generateworkflow - the output is broad enough that templates and file generation pay off
Do not start with a plugin if a small post-processing script or a few handwritten helpers will do.
Install the generation package
Plugin authors need the generation package in addition to core:
npm install -D @rexeus/typeweaver-gen
The public building blocks
The main APIs plugin authors touch are:
BasePluginfrom@rexeus/typeweaver-genGeneratorContextNormalizedSpecrenderTemplate(...)writeFile(...)copyLibFiles(...)
That is enough for most generators.
Plugin lifecycle
Plugins run through four phases:
| Phase | Purpose |
|---|---|
initialize(context) | Validate prerequisites or read config before generation starts |
collectResources(normalizedSpec) | Inspect or transform the normalized resource model |
generate(context) | Render templates and write generated files |
finalize(context) | Do any cleanup or last-step output work |
You only need to implement the phases your plugin actually uses.
Minimal plugin shape
import { BasePlugin, type GeneratorContext } from "@rexeus/typeweaver-gen";
export class MyPlugin extends BasePlugin {
public name = "my-plugin";
public override generate(context: GeneratorContext): void {
for (const resource of context.normalizedSpec.resources) {
const content = context.renderTemplate("Resource.ejs", {
resource,
coreDir: context.coreDir,
});
context.writeFile(`${resource.name}/${resource.name}Artifacts.ts`, content);
}
}
}
GeneratorContext in practice
GeneratorContext gives your plugin the normalized contract plus file-writing helpers.
The pieces you will use most often are:
normalizedSpec.resourcesfor resource and operation iterationnormalizedSpec.responsesfor canonical response definitionswriteFile(relativePath, content)to emit tracked filesrenderTemplate(templatePath, data)to render EJS templatesgetResourceOutputDir(resourceName)and related path helpers
The normalized model keeps plugin code focused on generation, not on spec parsing.
Templates
Templates are usually the cleanest way to keep generators readable.
const content = context.renderTemplate("HttpAdapter.ejs", {
resource,
operation,
coreDir: context.coreDir,
});
The template receives the data object as locals. Keep template inputs small and explicit.
Shipping runtime helpers with copyLibFiles
If your generated files depend on shared runtime code, place those helper files in your plugin's lib output and copy them into the generated folder.
import path from "node:path";
import { fileURLToPath } from "node:url";
import { BasePlugin, type GeneratorContext } from "@rexeus/typeweaver-gen";
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
export class MyPlugin extends BasePlugin {
public name = "my-plugin";
public override generate(context: GeneratorContext): void {
this.copyLibFiles(context, path.join(moduleDir, "lib"), this.name);
}
}
That is how plugins such as aws-cdk ship reusable runtime helpers alongside generated files.
Dependency ordering
Plugins can declare dependencies with depends.
export class AwsLikePlugin extends BasePlugin {
public name = "aws-like";
public override depends = ["types"];
}
The CLI loads plugins in dependency order. If a dependency is missing, generation fails early instead of producing partial output.
Guardrails for good plugins
Keep plugin scope tight:
- one plugin should usually solve one generation concern
- prefer reading
NormalizedSpecover re-parsing authoring files - prefer
writeFile(...)over manual file writes so output stays tracked - keep generated code deterministic so repeated runs produce stable diffs
- do not hide contract changes behind custom conventions that spec authors cannot see
What to avoid
Avoid plugins that:
- duplicate existing built-in output with only naming tweaks
- depend on private normalization details that are not part of the documented surface
- mix unrelated concerns such as clients, infrastructure, and runtime bootstrapping in one generator
If your goal is just a project-specific artifact, Custom Generators is usually the better starting point.