diff --git a/docs/5.x/guide/concepts/generators.md b/docs/5.x/guide/concepts/generators.md index f9982b2..2879c18 100644 --- a/docs/5.x/guide/concepts/generators.md +++ b/docs/5.x/guide/concepts/generators.md @@ -26,3 +26,7 @@ A generator implements up to three methods, and each maps to a slice of the spec - `operations` runs a single time with the whole set at once. Reach for it when an index or a router has to see every operation before it writes anything. Each method receives the generator context and returns the files that node becomes. It can hand back file nodes directly, return a renderer element, or write through the context and return nothing. The [generator reference](/docs/5.x/reference/kit/generators) lists every field on that context. + +## Scoping a generator with `match` + +A plugin sometimes registers several generators for the same node type, where only one should run per node, for example one hook generator per query variant. Give a generator a `match(node, ctx)` predicate to declare that scope: when it returns `false`, the engine skips `schema` or `operation` for that node entirely, instead of calling the generator and having it classify the node and bail out itself. See the [generator reference](/docs/5.x/reference/kit/generators#match) for the full signature. diff --git a/docs/5.x/reference/kit/generators.md b/docs/5.x/reference/kit/generators.md index ec5bc26..504b45e 100644 --- a/docs/5.x/reference/kit/generators.md +++ b/docs/5.x/reference/kit/generators.md @@ -42,6 +42,27 @@ const myGenerator = defineGenerator({ | `operation()` | `OperationNode` (per API operation) | `TElement \| Array \| void` | Generate hooks, clients, handlers. Called once per operation | | `operations()` | `Array` (all operations) | `TElement \| Array \| void` | Generate index or barrel files. Called once after all operations | +### Scoping with `match` {#match} + +Add a `match(node, ctx)` predicate to skip `schema` or `operation` for nodes a generator does not apply to. When `match` returns `false`, the engine skips that node entirely: no context work beyond what it already builds per node, and no call to `schema`/`operation`. Omit `match` to run for every node, the default when it is unset. `match` does not gate `operations()`, which already runs once on the full batch rather than per node. + +This is useful when a plugin registers several generators for the same node type and only one should run per node, for example one hook generator per query variant in `@kubb/plugin-react-query`. Without `match`, every generator runs for every node and has to classify and bail out on its own. + +```typescript twoslash [scoped-generator.ts] +import { ast, defineGenerator } from 'kubb/kit' + +const getOnlyGenerator = defineGenerator({ + name: 'get-only-generator', + match(node, ctx) { + return ast.isHttpOperationNode(node) && node.method.toLowerCase() === 'get' + }, + operation(node, ctx) { + // node is already known to be a GET operation here + return null + }, +}) +``` + ### `GeneratorContext` properties (the `ctx` argument passed to each method) | Property | Type | Purpose | diff --git a/snippets/how-to/macros.md b/snippets/how-to/macros.md index 19b9a11..4ba5d13 100644 --- a/snippets/how-to/macros.md +++ b/snippets/how-to/macros.md @@ -6,13 +6,13 @@ The engine (`defineMacro`, `composeMacros`, `applyMacros`, and the `Macro` type) ## Shape -A macro carries the per-kind callbacks of a [visitor](/docs/5.x/reference/kit/ast#visitors), plus a `name`, an optional `enforce` order, and an optional `when` gate. +A macro carries the per-kind callbacks of a [visitor](/docs/5.x/reference/kit/ast#visitors), plus a `name`, an optional `enforce` order, and an optional `match` predicate. ```typescript [Type definition] type Macro = { name: string enforce?: 'pre' | 'post' - when?: (node: Node) => boolean + match?: (node: Node) => boolean schema?(node: SchemaNode, context): SchemaNode | null | undefined operation?(node: OperationNode, context): OperationNode | null | undefined // input, output, property, parameter, response @@ -36,7 +36,7 @@ const macroIntegerToString = ast.defineMacro({ }) ``` -The `when` gate skips a macro for nodes it does not care about, and `enforce` places a macro before or after the unmarked ones. +The `match` predicate skips a macro for nodes it does not care about, and `enforce` places a macro before or after the unmarked ones. ```typescript twoslash [enforce.ts] import { ast } from 'kubb/kit' @@ -44,7 +44,7 @@ import { ast } from 'kubb/kit' const macroUntagged = ast.defineMacro({ name: 'untagged', enforce: 'post', - when: (node) => node.kind === 'Operation', + match: (node) => node.kind === 'Operation', operation(node) { return node.tags?.length ? undefined : { ...node, tags: ['untagged'] } },