AST API
@kubb/ast is the package behind Kubb's universal Abstract Syntax Tree. This page documents its callable surface: node factories, the three visitors, type guards, and helpers. For why the AST exists and how it fits the pipeline, see AST concepts.
NOTE
@kubb/core re-exports @kubb/ast as the ast namespace, with node constructors under ast.factory the way TypeScript groups them under ts.factory. Most plugins do not need @kubb/ast as a direct dependency. Install it only for named imports without the ast. prefix, taking constructors from the @kubb/ast/factory subpath.
Quick start
The public surface is a handful of factories, three visitors, and a few guards:
import { } from '@kubb/core'
const : . = ..({
: [..({ : 'Pet', : 'object', : [] })],
: [..({ : 'listPets', : 'GET', : '/pets' })],
})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/core'
const = ..({
: [..({ : 'Pet', : 'object', : [] }), ..({ : 'Status', : 'enum', : ['active', 'inactive'] })],
: [..({ : 'listPets', : 'GET', : '/pets' })],
})The @kubb/ast/factory subpath 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
Three visitor functions cover the common traversal patterns. 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.
walk: async traversal with side effects
import { } from '@kubb/core'
const = ..({ : [], : [] })
await .(, {
async () {
.(`Found ${.} ${.}`)
},
async () {
if ('deprecated' in && .) {
.(`Schema ${'name' in ? . : '?'} is deprecated`)
}
},
})Use walk to log, validate, collect statistics, or trigger a side effect per node.
transform: synchronous, returns a new tree
import { } from '@kubb/core'
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/core'
const = ..({ : 'Pet', : 'object', : [] })
..(, { : 'Pet' }) // -> same `node` reference (no change)
..(, { : 'Animal' }) // -> new node with `name` replacedcollect: gather matching nodes
import { } from '@kubb/core'
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/ast exports type guards and a narrowSchema helper for safe discrimination:
import { } from '@kubb/core'
const = ..({ : [], : [] })
await .(, {
async () {
const = .(, 'object')
if () {
.(`object with ${..} properties`)
}
if (. === 'ref') {
.(`reference to: ${.}`)
}
},
async () {
if (.()) {
.(`${.} ${.}`)
}
},
})Refs and naming helpers
The ref and naming helpers live in the @kubb/ast/utils subpath, alongside the other string and code-building utilities.
| Helper | Import from | Purpose |
|---|---|---|
extractRefName | @kubb/ast/utils | Turn '#/components/schemas/Pet' into 'Pet'. |
childName | @kubb/ast/utils | Derive a child property name from context. |
enumPropName | @kubb/ast/utils | Convert an enum value into a valid property name. |
findDiscriminator | @kubb/ast/utils | Locate a discriminator on a oneOf/union schema. |
import { } from '@kubb/ast/utils'
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. |
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 Adapters.
Examples
Collect every operation tag
import { } from '@kubb/core'
const = ..({ : [], : [] })
const = new (
.<string>(, {
() {
return .?.[0]
},
}),
)
.([...])