AST and node builders
ast
ast is kubb/kit's namespace for the entire AST surface, the same way TypeScript groups its node constructors under ts.factory. It carries the factory node builders, the transform and collect visitors, the guards, the ref and string helpers, and the macro engine. The namespace is backed by the internal @kubb/ast library. Always reach it through kubb/kit.
import { } from 'kubb/kit'
const = ..({
: [..({ : 'Pet', : 'object', : [] })],
: [],
})Node building goes through ast.factory. ast.factory.createFile, ast.factory.createSource, and ast.factory.createText build the FileNode tree a generator returns.
import { } from 'kubb/kit'
const = ..({
: 'pet.ts',
: './pet.ts',
: [..({ : [..('export type Pet = { id: number }')] })],
})For why the AST exists and how it fits the pipeline, see AST concepts.
Schema node types
A SchemaNode is discriminated by its type. The values fall into three families.
Structural types
| Type | Description | TypeScript |
|---|---|---|
object | Object with named properties | { name: string; age: number } |
array | Sequence of items | string[] |
tuple | Fixed-length array with typed positions | [string, number, boolean] |
union | One of multiple types | string | number |
intersection | Combination of multiple types | A & B |
enum | Fixed set of literal values | 'active' | 'inactive' |
Scalar types
| Type | Description | TypeScript |
|---|---|---|
string | Text value | string |
number | Numeric value | number |
integer | Whole number | number |
bigint | Large integer | bigint |
boolean | True/false | boolean |
null | Null value | null |
any | Any value | any |
unknown | Unknown value | unknown |
void | No value | void |
never | Never produced | never |
Special types
| Type | Description | Example |
|---|---|---|
ref | Reference to another schema | Pet (from $ref) |
date | ISO date | 2024-01-15 |
datetime | ISO datetime | 2024-01-15T10:30:00Z |
time | ISO time | 10:30:00 |
uuid | UUID string | 550e8400-e29b-41d4-a716-446655440000 |
email | Email address | [email protected] |
url | URL string | https://example.com |
blob | Binary data | Raw bytes |
Factory functions
Factories return defaulted, fully typed nodes. Use them in adapters and inside generator handlers. Never build AST literals by hand.
import { } from 'kubb/kit'
const = ..({
: [..({ : 'Pet', : 'object', : [] }), ..({ : 'Status', : 'enum', : ['active', 'inactive'] })],
: [..({ : 'listPets', : 'GET', : '/pets' })],
})The ast.factory namespace also provides constructors for source files and TypeScript-level artifacts that generators emit:
| Factory | Purpose |
|---|---|
createFile, createSource, createText | Build FileNodes emitted by generators. |
createImport, createExport | Emit import / export statements. |
createConst, createFunction, createArrowFunction, createJsx | Emit TypeScript declarations and JSX. |
createParameter | Describe operation parameters. |
createProperty, createType | Compose object properties and TypeScript types. |
createResponse, createRequestBody, createContent, createOutput | Model responses, request bodies, content entries, and generator outputs. |
createBreak | Emit line breaks between nodes. |
update | Apply an identity-preserving shallow update to any node. |
Visitors
Two visitor functions cover the common traversal patterns: transform rewrites the tree and collect gathers nodes. Visitor objects use lowercase, kind-style keys (input, operation, schema, property, parameter, response). To rewrite nodes inside a plugin, reach for macros. They add names, ordering, and composition on top of transform. For logging, validation, or statistics, collect the nodes you care about and loop over the result.
transform: synchronous, returns a new tree
import { } from 'kubb/kit'
const = ..({ : [], : [] })
const = .(, {
() {
if (. === 'object' && . === ) {
return { ..., : false }
}
return
},
() {
return { ..., : .?. ? . : ['untagged'] }
},
})Use transform to change AST structure, normalize inconsistencies, or annotate nodes.
NOTE
transform preserves identity through structural sharing. When a visitor leaves a node and all its descendants unchanged, transform returns the original reference, so unchanged subtrees and their arrays are reused, not copied. Returning the same node is a no-op. Returning a new node replaces it and rebuilds only its ancestors. A no-op pass allocates nothing, and you detect whether anything changed with result === input.
To apply a change and keep that guarantee, use the update factory instead of spreading by hand. It returns the same node when every field you pass already matches:
import { } from 'kubb/kit'
const = ..({ : 'Pet', : 'object', : [] })
..(, { : 'Pet' }) // -> same `node` reference (no change)
..(, { : 'Animal' }) // -> new node with `name` replacedcollect: gather matching nodes
import { } from 'kubb/kit'
const = ..({ : [], : [] })
const = .<.>(, {
() {
return . === 'POST' ? :
},
})
const = .<.>(, {
() {
return 'deprecated' in && . ? :
},
})
.(`POST operations: ${.}`)
.(`Deprecated schemas: ${.}`)Use collect to find specific nodes, filter by a criterion, or build a list for later processing.
Guards and narrowing
Kubb exports type guards and a narrowSchema helper for safe discrimination:
import { } from 'kubb/kit'
const = ..({ : [], : [] })
for (const of .<.>(, { : () => })) {
const = .(, 'object')
if () {
.(`object with ${..} properties`)
}
if (. === 'ref') {
.(`reference to: ${.}`)
}
}
for (const of .<.>(, { : () => })) {
if (.()) {
.(`${.} ${.}`)
}
}Refs and naming helpers
The ref and naming helpers ship on the ast namespace, alongside the other string and code-building utilities. Reach them the same way you reach the guards or node types.
| Helper | Purpose |
|---|---|
extractRefName | Turn '#/components/schemas/Pet' into 'Pet'. |
childName | Derive a child property name from context. |
enumPropName | Convert an enum value into a valid property name. |
import { } from 'kubb/kit'
const = .('#/components/schemas/Pet')
Constants
| Export | Purpose |
|---|---|
schemaTypes | Map of every schema type discriminant. |
Macros
A macro is a named, composable transform built on transform. Macros rewrite nodes before printing, with ordering, gating, and reuse that a bare visitor does not give you. See Macros concepts.
| Export | Purpose |
|---|---|
defineMacro | Type a macro and read it as one definition. |
composeMacros | Fold an ordered list of macros into one visitor. |
applyMacros | Run a list of macros over a node tree. |
Printers
Lower-level helpers for parsers that turn the AST into source code:
| Export | Purpose |
|---|---|
createPrinter | Typed helper for creating a Printer. |
createPrinter takes an overrides map to replace the handler for individual schema node types. Inside an override, this.base(node) runs the built-in handler the override replaced, so you can wrap its output instead of re-implementing it. Pass overrides through the overrides field rather than spreading them into nodes, otherwise this.base cannot find the original handler. The printer.nodes option on @kubb/plugin-ts, @kubb/plugin-zod, and @kubb/plugin-faker feeds this map. See Override a printer.
See Parsers concepts for how parsers consume printers. defineDialect is the adapter seam for spec-specific schema behavior. It keeps the shared converters generic, so an adapter supplies only the questions that differ between specs. See Schema dispatch and dialects.
Collect every operation tag
import { } from 'kubb/kit'
const = ..({ : [], : [] })
const = new (
.<string>(, {
() {
return .?.[0]
},
}),
)
.([...])