Skip to content

feat(core,ast): add a match predicate to Generator, aligned with Macro - #3828

Merged
stijnvanhulle merged 4 commits into
mainfrom
claude/core-generator-match-predicate
Jul 18, 2026
Merged

feat(core,ast): add a match predicate to Generator, aligned with Macro#3828
stijnvanhulle merged 4 commits into
mainfrom
claude/core-generator-match-predicate

Conversation

@stijnvanhulle

@stijnvanhulle stijnvanhulle commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

🎯 Changes

KubbDriver#runGenerators always calls every registered generator's schema/operation method for every node it applies to. A generator that only wants a subset of nodes has no declarative way to say so, so it gets called for every node and has to classify-and-bail at the top of its own method.

This adds an optional match predicate to Generator (packages/core/src/defineGenerator.ts):

match?: (node: SchemaNode | OperationNode, ctx: GeneratorContext<TOptions>) => PossiblePromise<boolean>

KubbDriver#runGenerators checks it in both dispatch loops (schema and operation) and skips a generator entirely for a node when match resolves false — no extra context work beyond what's already built per node, no render call. Omitting match keeps today's behavior of always running, so this is purely additive and doesn't change output for any existing generator.

renderGeneratorSchema/renderGeneratorOperation in packages/core/src/mocks.ts honor match too, so a generator's own unit tests exercise the same skip behavior as real dispatch. match intentionally does not gate operations, which already runs once per plugin on the full batch rather than per node.

Macro (packages/ast/src/defineMacro.ts) already had the same concept under a different name (when). Since that field hasn't shipped yet — it's part of the still-pending, unreleased macro-ast-transform-layer changeset — this PR renames it to match too, so both abstractions use the same name for "skip this callback for a node." No breaking change: nothing has consumed Macro.when in a release yet.

This unblocks a follow-up in kubb-labs/plugins reverting plugin-react-query's hand-rolled createOperationDispatcher (added in kubb-labs/plugins#728 to fix #3816) back to five plain declarative generators, each with its own match, instead of manually classifying once and calling jsxRenderer()/ctx.upsertFile() itself.

Fixes #3826.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is for the docs (no release).

Add an optional match(node, ctx) => boolean to Generator, checked by
KubbDriver#runGenerators before calling schema/operation. A generator
whose match resolves false is skipped for that node entirely, with no
extra context work and no render call. Omitting match keeps today's
behavior of always running, so this is purely additive.

renderGeneratorSchema/renderGeneratorOperation in mocks.ts honor match
too, so a generator's own unit tests exercise the same skip behavior as
real dispatch.

This replaces the need for a plugin to hand-roll its own dispatcher when
several of its generators only apply to a subset of nodes each, as
plugin-react-query's createOperationDispatcher does today in
kubb-labs/plugins#728.

Fixes #3826.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf
@changeset-bot

changeset-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c0f1b9d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@kubb/core Major
@kubb/adapter-oas Major
@kubb/cli Major
@kubb/kit Major
kubb Major
@kubb/mcp Major
@kubb/plugin-barrel Major
unplugin-kubb Major
@kubb/parser-md Major
@kubb/parser-ts Major
@kubb/renderer-jsx Major
@kubb/ast Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dosubot dosubot Bot added @kubb/core enhancement New feature or request labels Jul 18, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@kubb/adapter-oas

npm i https://pkg.pr.new/@kubb/adapter-oas@3828

@kubb/ast

npm i https://pkg.pr.new/@kubb/ast@3828

@kubb/cli

npm i https://pkg.pr.new/@kubb/cli@3828

@kubb/core

npm i https://pkg.pr.new/@kubb/core@3828

@kubb/kit

npm i https://pkg.pr.new/@kubb/kit@3828

kubb

npm i https://pkg.pr.new/kubb@3828

@kubb/mcp

npm i https://pkg.pr.new/@kubb/mcp@3828

@kubb/parser-md

npm i https://pkg.pr.new/@kubb/parser-md@3828

@kubb/parser-ts

npm i https://pkg.pr.new/@kubb/parser-ts@3828

@kubb/plugin-barrel

npm i https://pkg.pr.new/@kubb/plugin-barrel@3828

@kubb/renderer-jsx

npm i https://pkg.pr.new/@kubb/renderer-jsx@3828

unplugin-kubb

npm i https://pkg.pr.new/unplugin-kubb@3828

commit: c0f1b9d

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Size Change: +281 B (+0.07%)

Total Size: 410 kB

📦 View Changed
Filename Size Change
packages/ast/dist/index.js 13.8 kB +1 B (+0.01%)
packages/core/dist/index.cjs 27.8 kB +97 B (+0.35%)
packages/core/dist/index.js 27.7 kB +102 B (+0.37%)
packages/core/dist/mocks.cjs 1.85 kB +38 B (+2.09%)
packages/core/dist/mocks.js 1.77 kB +43 B (+2.48%)
ℹ️ View Unchanged
Filename Size Change
packages/adapter-oas/dist/index.cjs 24.4 kB 0 B
packages/adapter-oas/dist/index.js 24 kB 0 B
packages/adapter-oas/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/ast/dist/index.cjs 14.5 kB 0 B
packages/ast/dist/rolldown-runtime-CNktS9qV.js 291 B 0 B
packages/ast/dist/types.cjs 0 B 0 B 🆕
packages/ast/dist/types.js 31 B 0 B
packages/cli/dist/errors-CMvLO2NE.js 480 B 0 B
packages/cli/dist/errors-DNxBUo3k.cjs 537 B 0 B
packages/cli/dist/generate-DivmsV54.cjs 1.14 kB 0 B
packages/cli/dist/generate-DV_Srcbw.js 1.12 kB 0 B
packages/cli/dist/index.cjs 1.12 kB 0 B
packages/cli/dist/index.js 1.04 kB 0 B
packages/cli/dist/init-BMqpzODx.js 749 B 0 B
packages/cli/dist/init-C9-EwxER.cjs 777 B 0 B
packages/cli/dist/mcp-CYH5f0Cm.cjs 579 B 0 B
packages/cli/dist/mcp-vUOp-RlH.js 555 B 0 B
packages/cli/dist/package-BYGjY2e2.cjs 196 B 0 B
packages/cli/dist/package-C2WwUeML.js 137 B 0 B
packages/cli/dist/rolldown-runtime-Bx3C2hgW.cjs 576 B 0 B
packages/cli/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/cli/dist/run-B6GieDxN.cjs 12 kB 0 B
packages/cli/dist/run-BdtVrLKu.cjs 803 B 0 B
packages/cli/dist/run-BJhB7Qer.js 3.24 kB 0 B
packages/cli/dist/run-BuO4s2bO.js 12 kB 0 B
packages/cli/dist/run-CaOjZRiQ.js 758 B 0 B
packages/cli/dist/run-DAHp52w3.js 584 B 0 B
packages/cli/dist/run-DaVewTtW.cjs 3.33 kB 0 B
packages/cli/dist/run-iidDikJH.cjs 628 B 0 B
packages/cli/dist/Telemetry-7z2tIqPm.js 3.08 kB 0 B
packages/cli/dist/Telemetry-xcWb-UWT.cjs 3.2 kB 0 B
packages/cli/dist/tools-CzgGQa64.cjs 1.57 kB 0 B
packages/cli/dist/tools-DH1a7ZJH.js 1.49 kB 0 B
packages/cli/dist/validate-BWDhWrBx.cjs 581 B 0 B
packages/cli/dist/validate-dGW80XX9.js 559 B 0 B
packages/core/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/core/dist/usingCtx-BYOZEkiy.cjs 7.57 kB 0 B
packages/core/dist/usingCtx-C24YU3KM.js 7.05 kB 0 B
packages/kit/dist/index.cjs 5.87 kB 0 B
packages/kit/dist/index.js 5.69 kB 0 B
packages/kit/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/kit/dist/testing.cjs 221 B 0 B
packages/kit/dist/testing.js 61 B 0 B
packages/kubb/dist/astro.cjs 161 B 0 B
packages/kubb/dist/astro.js 81 B 0 B
packages/kubb/dist/config.cjs 153 B 0 B
packages/kubb/dist/config.js 82 B 0 B
packages/kubb/dist/defineConfig-BYCQRLaF.cjs 1.44 kB 0 B
packages/kubb/dist/defineConfig-CmqVIEF_.js 1.37 kB 0 B
packages/kubb/dist/esbuild.cjs 163 B 0 B
packages/kubb/dist/esbuild.js 83 B 0 B
packages/kubb/dist/farm.cjs 161 B 0 B
packages/kubb/dist/farm.js 80 B 0 B
packages/kubb/dist/index.cjs 233 B 0 B
packages/kubb/dist/index.js 111 B 0 B
packages/kubb/dist/jsx.cjs 250 B 0 B
packages/kubb/dist/jsx.js 71 B 0 B
packages/kubb/dist/jsx/jsx-dev-runtime.cjs 240 B 0 B
packages/kubb/dist/jsx/jsx-dev-runtime.js 79 B 0 B
packages/kubb/dist/jsx/jsx-runtime.cjs 234 B 0 B
packages/kubb/dist/jsx/jsx-runtime.js 75 B 0 B
packages/kubb/dist/kit.cjs 212 B 0 B
packages/kubb/dist/kit.js 54 B 0 B
packages/kubb/dist/kit/testing.cjs 219 B 0 B
packages/kubb/dist/kit/testing.js 62 B 0 B
packages/kubb/dist/nuxt.cjs 160 B 0 B
packages/kubb/dist/nuxt.js 80 B 0 B
packages/kubb/dist/rolldown-runtime-Bx3C2hgW.cjs 576 B 0 B
packages/kubb/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/kubb/dist/rolldown.cjs 160 B 0 B
packages/kubb/dist/rolldown.js 84 B 0 B
packages/kubb/dist/rollup.cjs 160 B 0 B
packages/kubb/dist/rollup.js 82 B 0 B
packages/kubb/dist/rspack.cjs 163 B 0 B
packages/kubb/dist/rspack.js 82 B 0 B
packages/kubb/dist/vite.cjs 160 B 0 B
packages/kubb/dist/vite.js 80 B 0 B
packages/kubb/dist/webpack.cjs 164 B 0 B
packages/kubb/dist/webpack.js 83 B 0 B
packages/mcp/dist/index.cjs 69.7 kB 0 B
packages/mcp/dist/index.js 69.1 kB 0 B
packages/mcp/dist/rolldown-runtime-G2anUtha.js 676 B 0 B
packages/parser-md/dist/index.cjs 1.29 kB 0 B
packages/parser-md/dist/index.js 1.25 kB 0 B
packages/parser-md/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/parser-ts/dist/index.cjs 6.22 kB 0 B
packages/parser-ts/dist/index.js 5.77 kB 0 B
packages/parser-ts/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/plugin-barrel/dist/index.cjs 5.49 kB 0 B
packages/plugin-barrel/dist/index.js 5.04 kB 0 B
packages/plugin-barrel/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/renderer-jsx/dist/index.cjs 5.5 kB 0 B
packages/renderer-jsx/dist/index.js 5.38 kB 0 B
packages/renderer-jsx/dist/jsx-dev-runtime.cjs 165 B 0 B
packages/renderer-jsx/dist/jsx-dev-runtime.js 79 B 0 B
packages/renderer-jsx/dist/jsx-runtime.cjs 567 B 0 B
packages/renderer-jsx/dist/jsx-runtime.js 508 B 0 B
packages/renderer-jsx/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/renderer-jsx/dist/rolldown-runtime-ClG-MNz_.cjs 202 B 0 B
packages/renderer-jsx/dist/types.cjs 0 B 0 B 🆕
packages/renderer-jsx/dist/types.js 31 B 0 B
packages/unplugin-kubb/dist/astro.cjs 300 B 0 B
packages/unplugin-kubb/dist/astro.js 296 B 0 B
packages/unplugin-kubb/dist/esbuild.cjs 191 B 0 B
packages/unplugin-kubb/dist/esbuild.js 187 B 0 B
packages/unplugin-kubb/dist/farm.cjs 191 B 0 B
packages/unplugin-kubb/dist/farm.js 184 B 0 B
packages/unplugin-kubb/dist/index.cjs 282 B 0 B
packages/unplugin-kubb/dist/index.js 199 B 0 B
packages/unplugin-kubb/dist/nuxt.cjs 331 B 0 B
packages/unplugin-kubb/dist/nuxt.js 329 B 0 B
packages/unplugin-kubb/dist/rolldown-runtime-C0LytTxp.js 168 B 0 B
packages/unplugin-kubb/dist/rolldown.cjs 193 B 0 B
packages/unplugin-kubb/dist/rolldown.js 188 B 0 B
packages/unplugin-kubb/dist/rollup.cjs 191 B 0 B
packages/unplugin-kubb/dist/rollup.js 186 B 0 B
packages/unplugin-kubb/dist/rspack.cjs 191 B 0 B
packages/unplugin-kubb/dist/rspack.js 186 B 0 B
packages/unplugin-kubb/dist/types.cjs 0 B 0 B 🆕
packages/unplugin-kubb/dist/types.js 31 B 0 B
packages/unplugin-kubb/dist/unpluginFactory-6ephcTBF.js 1.57 kB 0 B
packages/unplugin-kubb/dist/unpluginFactory-DKjcwMVB.cjs 1.71 kB 0 B
packages/unplugin-kubb/dist/unpluginFactory.cjs 157 B 0 B
packages/unplugin-kubb/dist/unpluginFactory.js 85 B 0 B
packages/unplugin-kubb/dist/vite.cjs 190 B 0 B
packages/unplugin-kubb/dist/vite.js 185 B 0 B
packages/unplugin-kubb/dist/webpack.cjs 192 B 0 B
packages/unplugin-kubb/dist/webpack.js 188 B 0 B

compressed-size-action

…wait checks

Macro.when hasn't shipped yet (still part of the pending, unreleased
macro-ast-transform-layer changeset), so rename it to match for
consistency with the new Generator.match predicate added in the previous
commit — same concept, same name, across both abstractions.

Also extract the await out of the compound if conditions in
KubbDriver#runGenerators and the mocks.ts test helpers into a plain
matches variable first, for readability.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf
@stijnvanhulle stijnvanhulle changed the title feat(core): add a match predicate to Generator feat(core,ast): add a match predicate to Generator, aligned with Macro Jul 18, 2026
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf
…-match-predicate

# Conflicts:
#	packages/core/src/KubbDriver.ts
@stijnvanhulle
stijnvanhulle merged commit 293ddf1 into main Jul 18, 2026
14 checks passed
@stijnvanhulle
stijnvanhulle deleted the claude/core-generator-match-predicate branch July 18, 2026 13:36
stijnvanhulle added a commit to kubb-labs/plugins that referenced this pull request Jul 18, 2026
…d of the dispatcher (#730)

* refactor(plugin-react-query): use the new core match predicate instead of the dispatcher

Give each of the five hook generators a match predicate instead of
classifying and bailing out inside operation(). This is what
createOperationDispatcher (added in #728 to fix kubb-labs/kubb#3816)
was working around: @kubb/core's Generator now supports match natively,
so the core driver skips a non-matching generator before calling it and
plugin.ts goes back to registering the five generators directly.

Generated output is unchanged (verified against
tests/3.0.x/pluginReactQuery.test.ts's 11-config snapshot suite, run
locally against a kubb build linked from ../kubb via pnpm-workspace.yaml's
commented-out overrides).

Requires kubb-labs/kubb#3828 to merge and release before this is
mergeable: without match support in the driver, these generators would
run unconditionally for every operation, since the old classify-and-bail
guards were moved into match. Do not merge until the kubb catalog
version here is bumped past that release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf

* docs: tighten the match predicate changeset

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf

* chore: bump kubb catalog to 5.0.0-beta.104 and simplify match predicates

The published beta.104 carries the match predicate support this branch
depends on. Simplify each generator's match to narrow SchemaNode |
OperationNode via a single cast plus isHttpOperationNode instead of a
node.kind string check, and drop the queryParam regex normalization in
favor of a plain includes() check against both the plain and optional key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnAWjXX2zoWWScgn7NsLaf

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request @kubb/core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a match predicate to Generator so plugins don't need manual dispatch workarounds react-query runs five operation generators for every operation

2 participants