Skip to content
Kit

Lifecycle hooks

Every kubb:* hook a build fires, its payload, and when it fires. Listen with kubb.hooks.hook(name, handler) or from a plugin's hooks map.

Hooks let you run code in every build phase. Each phase fires a kubb:* hook on one shared emitter, typed by KubbHooks. Handlers can be async, and Kubb awaits each one.

ListenerReceives
A plugin's hooks mapThe hooks from kubb:setup:end to kubb:build:end, including its own kubb:plugin:setup. It misses the lifecycle and generation hooks, kubb:setup:start, and the output passes.
kubb.hooks.hook(name, handler) from createKubbEvery hook except kubb:plugin:setup

Listening

Attach listeners before you call build(). hook() returns a function that removes the listener.

import { createKubb } from 'kubb'

const kubb = createKubb({
  input: './petStore.yaml',
  output: { path: './gen' },
})

kubb.hooks.hook('kubb:plugin:end', ({ plugin, duration }) => {
  console.log(`${plugin.name} finished in ${duration}ms`)
})

kubb.hooks.hook('kubb:build:end', ({ files }) => {
  console.log(`Generated ${files.length} files`)
})

await kubb.build()

Firing order

A full generation run fires the hooks in this order.

kubb:lifecyclekubb:lifecycle:start
kubb:lifecyclekubb:generationsetupbuildformatlinthooksplugin-tsfilesplugin-zodschemaoperationoperationsplugin:setup ×2plugins:endkubb:info · success · warn · error · diagnostic fire at any point
Pause the playhead to read a hook. On the wide trace, hover or focus a span to jump to the hook that opens it.

Generation run

.generate() fires the generation and setup hooks. The CLI adds the lifecycle pair around all configs. A direct .build() or .safeBuild() fires none of them.

HookPayloadFires
kubb:lifecycle:start{ version }When kubb generate starts, before the first config. CLI only.
kubb:generation:start{ config }When a run begins, before setup.
kubb:setup:startnoneBefore the driver and storage are set up.
kubb:setup:endnoneAfter setup, before the build pipeline.
kubb:generation:end{ config, storage, status, diagnostics, filesCreated, hrStart }When the run finishes, after the output passes.
kubb:generation:summary{ duration, fileCount, failedPlugins, status }Right after kubb:generation:end, with the run's totals. duration is in milliseconds.
kubb:lifecycle:endnoneAfter the last config. CLI only.

Build pipeline

.generate(), .build(), and .safeBuild() all fire these hooks.

HookPayloadFires
kubb:plugin:setupKubbPluginSetupContextOnce per plugin, after its setup and before kubb:build:start.
kubb:build:start{ config, adapter, meta, nodeCounts, getPlugin, files, upsertFile }When the AST is ready, before plugins run. Skipped when no adapter parsed input.
kubb:plugin:start{ plugin }Before a plugin's generators run.
kubb:generate:schema(node, ctx)For each schema node.
kubb:generate:operation(node, ctx)For each operation node.
kubb:generate:operations(nodes, ctx)Once, with every operation node.
kubb:plugin:end{ plugin, duration, success, error, config, files, upsertFile }After a plugin finishes.
kubb:plugins:end{ config, files, upsertFile }After every plugin, before files reach the storage. Add aggregate files, such as a barrel, here.
kubb:files:processing:start{ files }Before the generated files are written.
kubb:files:processing:update{ files }As files are written. files holds progress records ({ processed, total, percentage, file, config }), not FileNodes.
kubb:file:processed{ file, source, stored }After each file is printed and handed to the storage. stored is the previous content, or null. See File hook payloads.
kubb:files:processing:end{ files, unchanged }After every file is written. unchanged lists the paths whose content did not change.
kubb:build:end{ files, config, outputDir }After every file is written and the adapter has settled.

Output passes

These hooks fire after a build without errors, only from .generate() with fsStorage. Each group fires only when the config sets output.format, output.lint, or output.postGenerate.

HookPayloadFires
kubb:format:startnoneBefore the formatter runs.
kubb:format:endnoneAfter formatting.
kubb:lint:startnoneBefore the linter runs.
kubb:lint:endnoneAfter linting.
kubb:hooks:startnoneBefore the postGenerate commands run.
kubb:hook:start{ id, command, name, args }Before one postGenerate command runs.
kubb:hook:line{ id, line }For each output line of a postGenerate command.
kubb:hook:end{ id, command, name, args, success, error }After one postGenerate command finishes. A failed command also carries stdout and stderr.
kubb:hooks:endnoneAfter every postGenerate command.

Messaging

These hooks carry log messages and diagnostics, and fire at any point.

HookPayloadFires for
kubb:info{ message, info }An informational message.
kubb:success{ message, info }A step that completed.
kubb:warn{ message, info }A problem that does not fail the build.
kubb:error{ error, meta }An error without a diagnostic code, so its stack survives.
kubb:diagnostic{ diagnostic }A coded error, warning, or info, or an update notice. See diagnostics.

How Kubb works explains the lifecycle.

Less common cases

Hook listener scope

When your plugin listens through its hooks map, it gets the hooks from kubb:setup:end to kubb:build:end, including its own kubb:plugin:setup.

  • Kubb adds these listeners during setup and removes them when the build ends.
  • They miss the lifecycle and generation hooks, kubb:setup:start, and the output passes.
  • kubb:plugin:setup fires once per plugin, after its setup and before kubb:build:start. Kubb calls the plugin's own handler directly.
  • kubb:lifecycle:start fires when kubb generate starts, before the first config. .generate() and the bundler plugins skip it.

File hook payloads

When you listen to kubb:file:processed, you get { file, source, stored } after each file is printed and handed to the storage.

FieldHolds
sourceThe text before any output pass.
storedWhat the storage held at that path, or null when the path was empty or output.clean emptied its folder.