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

Parsers

A parser turns a FileNode into the source string that storage writes to disk. Each parser registers the file extensions it handles, and the file processor in @kubb/core routes each emitted file to the matching parser.

A parser has two jobs:

print(...nodes) is called by plugins to render language-specific AST nodes into a string before staging them on FileNode.sources. parse(file) is called by the file processor after all plugins have run, to join the staged sources into the final output string.

TIP

For TypeScript and JavaScript output use the built-in @kubb/parser-ts. It is added by default when you import defineConfig from the kubb package. Build a custom parser only when you target a different language, such as Python, Kotlin, or Rust.

Quick start

A minimal parser registers its extensions and concatenates each source:

parserText.ts
typescript
import {  } from '@kubb/core'

export const  = ({
  : 'parser-text',
  : ['.txt'],
  () {
    return .
      .(() => . ?? [])
      .(() => (. === 'Text' ? . : ''))
      .('\n')
  },
  (...) {
    return .().('\n')
  },
})

Wire it into your config:

kubb.config.ts
typescript
import {  } from 'kubb'
import { ,  } from '@kubb/parser-ts'
import {  } from './parserText.ts'
Cannot find module './parserText.ts' or its corresponding type declarations.
export default ({ : { : './petStore.yaml' }, : { : './src/gen' }, : [, , ], })

Anatomy

Every value returned from defineParser matches the Parser interface from @kubb/core:

Property Type Required When called Purpose
name string Yes Unique parser identifier. Convention is parser-<id>.
extNames Array<FileNode['extname']> | undefined Yes File extensions this parser handles. Set to undefined to register a catch-all fallback.
parse (file: FileNode, options?: { extname?: FileNode['extname'] }) => string Yes By the file processor after all plugins run Serializes the file's staged sources into the final output string. Must return synchronously.
print (...nodes: TNode[]) => string Yes By plugins, before files are staged Renders compiler AST nodes to source text. The node type is parser-specific, for example ts.Node for parserTs.

IMPORTANT

If two parsers register the same extension, the first one in the parsers array wins. Order matters.

NOTE

parse() is synchronous. The file processor streams files through a synchronous pipeline, so returning a Promise is not supported. Do async work before the file reaches the parser and pass the result through FileNode.

NOTE

Formatting and linting (Prettier, Biome, oxlint) run after parse(). Keep parse() focused on producing syntactically valid output.

When no parser matches a file's extension, the file processor joins the file's source strings directly.

Streaming

The file processor is internal to @kubb/core and processes files one at a time. The build driver enqueues each file as plugins emit it, the processor runs it through parse(), and the result lands in storage without buffering the full set. Progress surfaces as start, update (with { file, source, processed, total, percentage }), and end events on the main event bus, which the built-in reporters render. Memory stays flat regardless of build size because each file is pulled through the pipeline one at a time.

Naming convention

Parsers share the layout of plugins and adapters:

Surface Pattern Example
npm package @<scope>/parser-<name> or kubb-parser-<name> @kubb/parser-ts
Parser runtime name The output language or format (lowercase) 'typescript', 'markdown'
Factory export parser<Name> (camelCase) parserTs, parserMd

Parsers export a plain Parser object, not a factory function. Pass them directly to parsers: in defineConfig:

naming.ts
typescript
import {  } from '@kubb/core'

export const  = ({
  : 'custom',
  : ['.custom'],
  () {
    return ..(() => . ?? '').('\n')
  },
  (...) {
    return .().('\n')
  },
})

TIP

Parsers compose by extension. parserTs (.ts, .js) and parserTsx (.tsx, .jsx) ship in the same @kubb/parser-ts package and register side by side.

Built-in parsers

@kubb/parser-ts

The default parser for TypeScript and JavaScript output. It uses the official TypeScript compiler to resolve import paths, deduplicate declarations, print JSDoc, and rewrite extensions based on output.extension. See the @kubb/parser-ts reference for the full option list.

shell
bun add -d @kubb/parser-ts@beta
shell
pnpm add -D @kubb/parser-ts@beta
shell
npm install --save-dev @kubb/parser-ts@beta
shell
yarn add -D @kubb/parser-ts@beta
Export Extensions handled Notes
parserTs .ts, .js TypeScript and plain JavaScript output.
parserTsx .tsx, .jsx Same as parserTs with JSX support.

Both expose parse(file, options?) and print(...nodes: ts.Node[]). Call parserTs.print(node) from a plugin to render a TypeScript compiler node to its source string before staging it on FileNode.sources.

kubb.config.ts
typescript
import {  } from 'kubb'
import { ,  } from '@kubb/parser-ts'

export default ({
  : { : './petStore.yaml' },
  : { : './src/gen' },
  : [, ],
})

TIP

defineConfig from the kubb package installs parserTs, parserTsx, and parserMd automatically. Set parsers: only when you add a custom parser or need to change the registration order.

Creating a custom parser

Use defineParser from @kubb/core. It is an identity wrapper that infers the parser type. It returns the object you pass in unchanged, with no per-build options:

parserPython.ts
typescript
import {  } from '@kubb/core'

export const  = ({
  : 'parser-python',
  : ['.py', '.pyi'],
  () {
    const : <string> = []

    if (.) {
      .(.)
    }

    for (const  of .) {
      for (const  of . ?? []) {
        if (. === 'Text') {
          .(.)
        }
      }
    }

    if (.) {
      .(.)
    }

    return .('\n')
  },
  (...) {
    return .().('\n')
  },
})

Register it alongside the built-ins:

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from '@kubb/parser-ts'
import {  } from './parserPython.ts'
Cannot find module './parserPython.ts' or its corresponding type declarations.
export default ({ : { : './petStore.yaml' }, : { : './src/gen' }, : [, ], })

TIP

Set extNames: undefined to register a catch-all fallback that runs when no other parser matches. Useful for a default .txt writer or for inspecting what files the build produces.

NOTE

parse() runs synchronously, so external formatting (a service call, a child process, or a worker thread) must finish before the file reaches the parser. Stage the pre-formatted output on FileNode.sources[].nodes inside a generator, then let the parser join it verbatim.