Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/5.x/guide/concepts/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
21 changes: 21 additions & 0 deletions docs/5.x/reference/kit/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ const myGenerator = defineGenerator({
| `operation()` | `OperationNode` (per API operation) | `TElement \| Array<FileNode> \| void` | Generate hooks, clients, handlers. Called once per operation |
| `operations()` | `Array<OperationNode>` (all operations) | `TElement \| Array<FileNode> \| 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 |
Expand Down
8 changes: 4 additions & 4 deletions snippets/how-to/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,15 +36,15 @@ 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'

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'] }
},
Expand Down