@manifesto-ai/codegen
v5.0.0
Published
Manifesto Codegen - Plugin-based code generation from DomainSchema
Maintainers
Readme
@manifesto-ai/codegen
Codegen generates canonical domain facade types from a Manifesto DomainSchema through a deterministic plugin pipeline.
What is Codegen?
Codegen transforms a DomainSchema into type-safe code artifacts. It runs plugins sequentially, each producing file patches that are validated, collision-checked, and flushed to disk.
In the Manifesto architecture:
DomainSchema -> CODEGEN -> Generated Files
|
Plugin pipeline
(deterministic, no runtime deps)What Codegen Does
| Responsibility | Description |
|----------------|-------------|
| Generate canonical domain facades | DomainSchema -> <domain>.domain.ts with state / computed / actions |
| Generate legacy TS/Zod artifacts | Optional low-level types.ts / base.ts output |
| Generate Zod schemas | DomainSchema types -> Zod validators with type annotations |
| Plugin pipeline | Run plugins sequentially with shared artifacts |
| Path safety | Validate and normalize output file paths |
| Collision detection | Prevent multiple plugins from writing to the same file |
| Deterministic output | Same schema always produces identical files |
What Codegen Does NOT Do
| NOT Responsible For | Who Is |
|--------------------|--------|
| Define schemas | SDK / Compiler (DomainSchema authoring) |
| Runtime validation | Application code using generated Zod schemas |
| Bundling or compilation | Build tools (tsc, esbuild, etc.) |
| Schema versioning | @manifesto-ai/core |
Installation
pnpm add @manifesto-ai/codegen
# or
npm install @manifesto-ai/codegenPeer dependency: @manifesto-ai/core must be installed separately.
Quick Example
import { generate, createDomainPlugin } from "@manifesto-ai/codegen";
import type { DomainSchema } from "@manifesto-ai/core";
const schema: DomainSchema = { /* your domain schema */ };
const result = await generate({
schema,
outDir: "./generated",
sourceId: "src/domain/hello.mel",
plugins: [createDomainPlugin()],
});
// result.files -> [{ path: "src/domain/hello.domain.ts", content: "..." }]
// result.diagnostics -> [] (empty = no warnings or errors)For compiler-driven emission during dev or build, inject Codegen explicitly into the compiler plugin:
import { defineConfig } from "vite";
import { melPlugin } from "@manifesto-ai/compiler/vite";
import { createCompilerCodegen } from "@manifesto-ai/codegen";
export default defineConfig({
plugins: [
melPlugin({
codegen: {
emit: createCompilerCodegen(),
timing: "transform",
},
}),
],
});This produces a canonical domain facade:
src/domain/hello.domain.ts
export interface HelloDomain {
readonly state: {
counter: number
hello: string
}
readonly computed: {
canDecrement: boolean
doubled: number
}
readonly actions: {
decrement: () => void
increment: () => void
}
}Legacy createTsPlugin() and createZodPlugin() remain available, but are deprecated in favor of createDomainPlugin().
See GUIDE.md for the full tutorial.
API Reference
Main Exports
// Entry point
function generate(options: GenerateOptions): Promise<GenerateResult>;
function createCompilerCodegen(options?: CompilerCodegenOptions): CompilerCodegenEmitter;
// Built-in plugins
function createDomainPlugin(options?: DomainPluginOptions): CodegenPlugin;
/** @deprecated */
function createTsPlugin(options?: TsPluginOptions): CodegenPlugin;
/** @deprecated */
function createZodPlugin(options?: ZodPluginOptions): CodegenPlugin;
// Key types
type GenerateOptions = {
schema: DomainSchema;
outDir: string;
plugins: CodegenPlugin[];
sourceId?: string; // Embedded in @generated header
stamp?: boolean; // Add timestamp to header (breaks determinism)
};
type GenerateResult = {
files: Array<{ path: string; content: string }>;
artifacts: Record<string, unknown>;
diagnostics: Diagnostic[];
};
type CompilerCodegenOptions = {
outDir?: string; // Default: "."
plugins?: CodegenPlugin[]; // Default: [createDomainPlugin()]
stamp?: boolean;
};
type CodegenPlugin = {
name: string;
generate(ctx: CodegenContext): CodegenOutput;
};See SPEC-v0.1.1.md for complete API reference.
Core Concepts
Plugin Pipeline
Plugins run in array order. Each plugin receives a context containing the schema and artifacts from all previous plugins. The canonical domain plugin is self-contained; the legacy TS plugin publishes type names and the legacy Zod plugin reads them to generate type-annotated schemas.
Artifacts
Plugins communicate through artifacts -- a namespaced key-value store. Plugin i sees frozen artifacts from plugins 0..i-1. This enables cross-plugin coordination without coupling.
Deterministic Output
Same DomainSchema always produces byte-identical output files. Fields and types are lexicographically sorted. No timestamps are included by default.
Relationship with Other Packages
@manifesto-ai/core -> CODEGEN -> Generated .ts files| Relationship | Package | How |
|--------------|---------|-----|
| Depends on | @manifesto-ai/core | Reads DomainSchema, TypeDefinition, TypeSpec |
| Used by | Build scripts / compiler plugin | Called during dev or build to generate type-safe code |
When to Use Codegen
Use Codegen when:
- You want a canonical
<domain>.domain.tsfacade forcreateManifesto<T>() - You want type-safe TypeScript interfaces from your DomainSchema
- You want Zod runtime validators that match your schema types
- You need deterministic, reproducible code generation in CI
- You are building a custom plugin for additional output formats
For schema authoring, see @manifesto-ai/core.
Documentation
| Document | Purpose | |----------|---------| | GUIDE.md | Step-by-step usage guide | | SPEC-v0.1.1.md | Complete specification | | ADR-CODEGEN-001.md | Architecture decisions | | VERSION-INDEX.md | Version tracking |
