Beta You're reading the docs for Kubb v5, which is currently in beta. View the stable v4 docs
Skip to content

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.

ast-namespace.ts
typescript
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.

factory.ts
typescript
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.

factories.ts
typescript
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

transform.ts
typescript
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:

update.ts
typescript
import {  } from 'kubb/kit'

const  = ..({ : 'Pet', : 'object', : [] })

..(, { : 'Pet' }) // -> same `node` reference (no change)
..(, { : 'Animal' }) // -> new node with `name` replaced

collect: gather matching nodes

collect.ts
typescript
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:

guards.ts
typescript
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.
refs.ts
typescript
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

tags.ts
typescript
import {  } from 'kubb/kit'

const  = ..({ : [], : [] })

const  = new (
  .<string>(, {
    () {
      return .?.[0]
    },
  }),
)

.([...])