Transform schemas with macros
Use a macro to transform schema or operation nodes before generation. Macros can rename symbols, change field types, or remove metadata across output targets.
Import the macro engine through ast from kubb/kit. For callback signatures and context properties, see the AST reference.
Writing a macro
Define the node transformation:
import { ast } from 'kubb/kit'
const macroIntegerToString = ast.defineMacro({
name: 'integer-to-string',
schema(node) {
return node.type === 'integer' ? { ...node, type: 'string' } : undefined
},
})The match predicate skips a macro for nodes it does not care about, and enforce places a macro before or after the unmarked ones.
import { ast } from 'kubb/kit'
const macroUntagged = ast.defineMacro({
name: 'untagged',
enforce: 'post',
match: (node) => node.kind === 'Operation',
operation(node) {
return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }
},
})Using macros in a plugin
Pass macros through a plugin's macros option, or register them from setup with addMacro and setMacros. Macros run per plugin, so one plugin's macros never change the nodes another plugin sees.
import { ast, defineExtension } from 'kubb/kit'
const macroDropDescriptions = ast.defineMacro({
name: 'drop-descriptions',
schema(node) {
return 'description' in node && node.description ? { ...node, description: undefined } : undefined
},
})
export const pluginRename = defineExtension({
kind: 'plugin',
meta: { name: 'plugin-rename' },
setup(options, kubb) {
kubb.addMacro(macroDropDescriptions)
return options
},
})Together, the two macros on this page turn a Pet schema node into the node on the right. Hover a line to see what changed it.
{ kind: 'Schema', name: 'Pet', description: 'A pet in the store', properties: { id: { type: 'integer', format: 'int64' }, name: { type: 'string' }, },}
{ kind: 'Schema', name: 'Pet', description: undefined, properties: { id: { type: 'string' }, name: { type: 'string' }, },}
integer-to-string returns a new node with type string, and the int64 format goes with it.
Macros run before resolver options are computed, so a renamed operationId or SchemaNode.name flows into resolveOptions, resolvePath, and resolveFile.
name. Every $ref to it still resolves to the old name, so imports and printed references point at a file the plugin no longer emits. Use macroRenameSchema, which renames the declaration and retargets the refs in one pass.Use the built-in macros
kubb/kit exports presets for common normalizations. Compose them with your own through a plugin's macros option. See the built-in macros table for what each one does.
import { ast, macroSimplifyUnion, macroDiscriminatorEnum, macroRenameSchema } from 'kubb/kit'
const root = ast.factory.createInput({ schemas: [], operations: [] })
const next = ast.applyMacros(root, [
macroSimplifyUnion,
macroDiscriminatorEnum({ propertyName: 'kind', values: ['cat', 'dog'] }),
macroRenameSchema({ from: 'Order', to: 'StoreOrder' }),
])Export macros from a shared module to reuse them across plugins and projects.