npm.io
5.0.0 • Published 5d ago

@kubb/ast

Licence
MIT
Version
5.0.0
Deps
0
Vulns
0
Weekly
0
Stars
1.8K

@kubb/ast

Spec-agnostic AST layer for Kubb

Defines the node tree, visitor pattern, factory functions, and type guards used across every Kubb code generation plugin.

Imports

Path Contents
@kubb/ast Runtime: node definitions, guards, visitor, macro engine, constants
ast.factory (via @kubb/ast) Node constructors (createSchema, createFile, and friends), the ts.factory analogue
@kubb/ast/types Types only: all node interfaces, type aliases, visitor types
kubb/kit Re-exports the ast and factory namespaces, the way most Kubb code reaches the AST without a direct dependency

@kubb/ast is an internal library. Inside the Kubb ecosystem the whole surface travels on the ast namespace from kubb/kit, so plugins and generators reach it there instead of depending on this package directly. The examples below import from @kubb/ast for clarity; through kubb/kit the same calls read as ast.walk, ast.factory.createSchema, and so on.

Node tree

RootNode
├── schemas: SchemaNode[]
└── operations: OperationNode[]
    ├── parameters: ParameterNode[]   → SchemaNode
    ├── requestBody?: SchemaNode
    └── responses: ResponseNode[]     → SchemaNode?

SchemaNode (discriminated union)
  object        → properties: PropertyNode[] → SchemaNode
  array | tuple → items: SchemaNode[]
  union | intersection → members: SchemaNode[]
  enum | ref | string | number | integer | bigint
  boolean | null | any | unknown | void
  date | datetime | time | uuid | email | url | blob

Usage

Factory

Constructors are available as ast.factory from @kubb/ast, mirroring ts.factory.createX.

import { ast } from '@kubb/ast'

const { createInput, createSchema, createProperty } = ast.factory

const root = createInput({
  schemas: [
    createSchema({
      name: 'Pet',
      type: 'object',
      properties: [
        createProperty({
          name: 'id',
          schema: createSchema({ type: 'integer' }),
          required: true,
        }),
        createProperty({
          name: 'name',
          schema: createSchema({ type: 'string' }),
          required: true,
        }),
      ],
    }),
  ],
})
Visitor
import { collectSync, transform } from '@kubb/ast'

// Immutable transformation
const updated = transform(root, {
  schema(node) {
    return { ...node, description: 'generated' }
  },
})

// Extraction
const types = collectSync<string>(root, {
  schema(node) {
    return node.type
  },
})
Guards
import { narrowSchema, schemaDef } from '@kubb/ast'
import type { Node } from '@kubb/ast/types'

function process(node: Node) {
  if (schemaDef.is(node)) {
    const obj = narrowSchema(node, 'object')
    obj?.properties?.forEach((p) => console.log(p.name))
  }
}
Refs
import { resolveRefName } from '@kubb/ast'

resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'

Adding a node

Adding a node touches three files. The barrels and visitor tables derive the rest.

  1. Define the node in its own src/nodes/*.ts file. Call defineNode and export the resulting fooDef, the createFoo constructor, and the node's type.
  2. Add fooDef to the nodeDefs array in src/registry.ts.
  3. Re-export createFoo from src/factory.ts.

Everything else follows from there. @kubb/ast/types picks up the node type through export type *, the @kubb/ast barrel picks up fooDef through export * from './registry.ts', and the visitor tables (VISITOR_KEYS, VISITOR_KEY_BY_KIND, nodeRebuilders) come from the def's children, visitorKey, and rebuild fields. registry.test.ts fails when a def has no matching factory.create*, so missing wiring is caught in CI.

Supporting Kubb

Kubb is an open source project, and its development is funded entirely by sponsors. If you would like to become a sponsor, please consider:

My sponsors

License

MIT

Keywords