@kubb/ast 5.0.0-beta.62 → 5.0.0-beta.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/{defineMacro-Vw-xQvq7.d.ts → defineMacro-BATi7xoC.d.ts} +4 -5
  2. package/dist/{defineMacro-BryDiuQl.cjs → defineMacro-CEZHaCXE.cjs} +5 -5
  3. package/dist/defineMacro-CEZHaCXE.cjs.map +1 -0
  4. package/dist/{defineMacro-CNkUpxia.js → defineMacro-Shz8f6SG.js} +3 -3
  5. package/dist/defineMacro-Shz8f6SG.js.map +1 -0
  6. package/dist/factory.cjs +56 -56
  7. package/dist/factory.cjs.map +1 -1
  8. package/dist/factory.d.ts +5 -5
  9. package/dist/factory.js +5 -5
  10. package/dist/factory.js.map +1 -1
  11. package/dist/{index-CTaN_ee_.d.ts → index-B9cc8MBS.d.ts} +46 -113
  12. package/dist/index.cjs +142 -271
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +12 -5
  15. package/dist/index.js +108 -236
  16. package/dist/index.js.map +1 -1
  17. package/dist/macros.cjs +12 -12
  18. package/dist/macros.cjs.map +1 -1
  19. package/dist/macros.d.ts +2 -2
  20. package/dist/macros.js +4 -4
  21. package/dist/macros.js.map +1 -1
  22. package/dist/{operationParams-B44p6EQy.d.ts → operationParams-k5CKwSWZ.d.ts} +9 -2
  23. package/dist/{visitor-DepQEKyp.js → refs-BjNDuCBD.js} +115 -120
  24. package/dist/refs-BjNDuCBD.js.map +1 -0
  25. package/dist/{visitor-B7Zlnp4c.cjs → refs-u5SDdyV7.cjs} +139 -144
  26. package/dist/refs-u5SDdyV7.cjs.map +1 -0
  27. package/dist/{response-BeG6hq2D.cjs → schema-BkvrrOAr.cjs} +171 -153
  28. package/dist/schema-BkvrrOAr.cjs.map +1 -0
  29. package/dist/{response-Rd1uisM1.js → schema-Cbnxmz4b.js} +166 -154
  30. package/dist/schema-Cbnxmz4b.js.map +1 -0
  31. package/dist/{types-DQTgVSwE.d.ts → types-BB_xgRJ3.d.ts} +55 -79
  32. package/dist/types.d.ts +5 -5
  33. package/dist/{utils-BDI3kXlb.cjs → utils-CEepwqmb.cjs} +74 -67
  34. package/dist/utils-CEepwqmb.cjs.map +1 -0
  35. package/dist/{utils-DW9rnfsD.js → utils-DaXkewb1.js} +56 -49
  36. package/dist/utils-DaXkewb1.js.map +1 -0
  37. package/dist/utils.cjs +11 -11
  38. package/dist/utils.d.ts +44 -29
  39. package/dist/utils.js +3 -3
  40. package/package.json +1 -1
  41. package/src/constants.ts +3 -6
  42. package/src/defineMacro.ts +1 -1
  43. package/src/defineNode.ts +6 -9
  44. package/src/definePrinter.ts +5 -3
  45. package/src/dialect.ts +23 -13
  46. package/src/factory.ts +4 -4
  47. package/src/index.ts +1 -1
  48. package/src/infer.ts +1 -1
  49. package/src/macros/macroEnumName.ts +1 -1
  50. package/src/nodes/code.ts +8 -5
  51. package/src/nodes/file.ts +5 -4
  52. package/src/nodes/operation.ts +3 -0
  53. package/src/nodes/parameter.ts +5 -5
  54. package/src/nodes/property.ts +5 -5
  55. package/src/nodes/schema.ts +4 -4
  56. package/src/optionality.ts +15 -0
  57. package/src/types.ts +1 -2
  58. package/src/utils/codegen.ts +6 -7
  59. package/src/utils/fileMerge.ts +7 -8
  60. package/src/utils/operationParams.ts +8 -8
  61. package/src/utils/refs.ts +14 -18
  62. package/src/utils/schemaGraph.ts +41 -33
  63. package/src/utils/schemaMerge.ts +2 -2
  64. package/src/visitor.ts +1 -1
  65. package/dist/defineMacro-BryDiuQl.cjs.map +0 -1
  66. package/dist/defineMacro-CNkUpxia.js.map +0 -1
  67. package/dist/response-BeG6hq2D.cjs.map +0 -1
  68. package/dist/response-Rd1uisM1.js.map +0 -1
  69. package/dist/utils-BDI3kXlb.cjs.map +0 -1
  70. package/dist/utils-DW9rnfsD.js.map +0 -1
  71. package/dist/visitor-B7Zlnp4c.cjs.map +0 -1
  72. package/dist/visitor-DepQEKyp.js.map +0 -1
  73. package/src/dedupe.ts +0 -231
@@ -4,6 +4,9 @@ import type { ParameterNode } from './parameter.ts'
4
4
  import { createRequestBody, type RequestBodyNode } from './requestBody.ts'
5
5
  import type { ResponseNode } from './response.ts'
6
6
 
7
+ /**
8
+ * HTTP method an operation responds to.
9
+ */
7
10
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE'
8
11
 
9
12
  /**
@@ -1,4 +1,5 @@
1
1
  import { defineNode } from '../defineNode.ts'
2
+ import { optionality } from '../optionality.ts'
2
3
  import type { BaseNode } from './base.ts'
3
4
  import type { SchemaNode } from './schema.ts'
4
5
 
@@ -41,15 +42,14 @@ export type ParameterNode = BaseNode & {
41
42
  type UserParameterNode = Pick<ParameterNode, 'name' | 'in' | 'schema'> & Partial<Omit<ParameterNode, 'kind' | 'name' | 'in' | 'schema'>>
42
43
 
43
44
  /**
44
- * Definition for the {@link ParameterNode}. `required` defaults to `false`. When a `dialect` is
45
- * passed to `create`, the schema's `optional`/`nullish` flags are derived through its
46
- * `optionality`; without one, the schema is left as-is.
45
+ * Definition for the {@link ParameterNode}. `required` defaults to `false`, and the schema's
46
+ * `optional`/`nullish` flags are derived from it through {@link optionality}.
47
47
  */
48
48
  export const parameterDef = defineNode<ParameterNode, UserParameterNode>({
49
49
  kind: 'Parameter',
50
- build: (props, dialect) => {
50
+ build: (props) => {
51
51
  const required = props.required ?? false
52
- return { ...props, required, schema: dialect ? dialect.schema.optionality(props.schema, required) : props.schema }
52
+ return { ...props, required, schema: optionality(props.schema, required) }
53
53
  },
54
54
  children: ['schema'],
55
55
  visitorKey: 'parameter',
@@ -1,4 +1,5 @@
1
1
  import { defineNode } from '../defineNode.ts'
2
+ import { optionality } from '../optionality.ts'
2
3
  import type { BaseNode } from './base.ts'
3
4
  import type { SchemaNode } from './schema.ts'
4
5
 
@@ -37,15 +38,14 @@ export type PropertyNode = BaseNode & {
37
38
  export type UserPropertyNode = Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>
38
39
 
39
40
  /**
40
- * Definition for the {@link PropertyNode}. `required` defaults to `false`. When a `dialect` is
41
- * passed to `create`, the schema's `optional`/`nullish` flags are derived through its
42
- * `optionality`; without one, the schema is left as-is.
41
+ * Definition for the {@link PropertyNode}. `required` defaults to `false`, and the schema's
42
+ * `optional`/`nullish` flags are derived from it through {@link optionality}.
43
43
  */
44
44
  export const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
45
45
  kind: 'Property',
46
- build: (props, dialect) => {
46
+ build: (props) => {
47
47
  const required = props.required ?? false
48
- return { ...props, required, schema: dialect ? dialect.schema.optionality(props.schema, required) : props.schema }
48
+ return { ...props, required, schema: optionality(props.schema, required) }
49
49
  },
50
50
  children: ['schema'],
51
51
  visitorKey: 'property',
@@ -107,7 +107,7 @@ type SchemaNodeBase = BaseNode & {
107
107
  /**
108
108
  * Schema name for named definitions (for example, `"Pet"`).
109
109
  * Inline schemas omit this field.
110
- * `null` means kubb has processed this and determined there is no applicable name.
110
+ * `null` means Kubb has processed this and determined there is no applicable name.
111
111
  * `undefined` means the name has not been set yet.
112
112
  */
113
113
  name?: string | null
@@ -190,7 +190,7 @@ export type ObjectSchemaNode = SchemaNodeBase & {
190
190
  /**
191
191
  * Additional object properties behavior:
192
192
  * - `true`: allow any value
193
- * - `false`: reject unknown properties (maps to `.strict()` in Zod)
193
+ * - `false`: reject unknown properties
194
194
  * - `SchemaNode`: allow values that match that schema
195
195
  * - `undefined`: no additional properties constraint (open object)
196
196
  */
@@ -669,8 +669,8 @@ type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & {
669
669
 
670
670
  /**
671
671
  * Maps schema `type` to its underlying `primitive`.
672
- * Primitive types map to themselves. Special string formats map to `'string'`.
673
- * Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
672
+ * Primitive types map to themselves and special string formats map to `'string'`.
673
+ * Any type not listed here (such as `ref`, `enum`, `union`, `intersection`, `tuple`, `ipv4`, `ipv6`, `blob`) has no `primitive`.
674
674
  */
675
675
  const TYPE_TO_PRIMITIVE: Partial<Record<SchemaNode['type'], PrimitiveSchemaType>> = {
676
676
  string: 'string',
@@ -0,0 +1,15 @@
1
+ import type { SchemaNode } from './nodes/schema.ts'
2
+
3
+ /**
4
+ * Generic JSON Schema optionality: a non-required field is optional, and a
5
+ * non-required nullable field is nullish.
6
+ */
7
+ export function optionality(schema: SchemaNode, required: boolean): SchemaNode {
8
+ const nullable = schema.nullable ?? false
9
+
10
+ return {
11
+ ...schema,
12
+ optional: !required && !nullable ? true : undefined,
13
+ nullish: !required && nullable ? true : undefined,
14
+ }
15
+ }
package/src/types.ts CHANGED
@@ -1,5 +1,4 @@
1
- export type { DedupeCanonical, DedupeLookups, DedupePlan } from './dedupe.ts'
2
- export type { Dialect, SchemaDialect } from './dialect.ts'
1
+ export type { Dedupe, Dialect, SchemaDialect } from './dialect.ts'
3
2
  export type { DistributiveOmit, NodeDef } from './defineNode.ts'
4
3
  export type { InferSchemaNode, ParserOptions } from './infer.ts'
5
4
  export type * from './nodes/index.ts'
@@ -2,8 +2,8 @@ import { isIdentifier, singleQuote } from '@internals/utils'
2
2
  import { INDENT } from '../constants.ts'
3
3
 
4
4
  /**
5
- * Builds a JSDoc comment block from an array of lines, returning `fallback` when there are no
6
- * comments so callers always get a usable string.
5
+ * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no
6
+ * comments.
7
7
  *
8
8
  * @example
9
9
  * ```ts
@@ -65,9 +65,8 @@ export function objectKey(name: string): string {
65
65
 
66
66
  /**
67
67
  * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
68
- * level and closing the brace at column zero. Nested objects built the same way indent cumulatively,
69
- * so callers never re-parse the generated code. A trailing comma is added per entry to match the
70
- * formatter's multi-line style.
68
+ * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
69
+ * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
71
70
  *
72
71
  * @example
73
72
  * ```ts
@@ -85,8 +84,8 @@ export function buildObject(entries: Array<string>): string {
85
84
  /**
86
85
  * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
87
86
  * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
88
- * one level with a trailing comma and the closing bracket at column zero. Use it for `z.union([…])`,
89
- * `z.array([…])`, and similar member lists so objects inside them nest correctly.
87
+ * one level with a trailing comma and the closing bracket at column zero. Used for member lists such
88
+ * as `z.union([…])` and `z.array([…])`.
90
89
  *
91
90
  * @example
92
91
  * ```ts
@@ -1,9 +1,8 @@
1
1
  /**
2
- * File-member merging. `combineImports`, `combineExports`, and `combineSources` deduplicate and sort
3
- * the import, export, and source entries of one file, and drop imports nothing references. This works
4
- * on a file's members, not on schema content.
5
- *
6
- * For collapsing duplicate schema shapes by structural signature, see `dedupe.ts`.
2
+ * File-member merging. `combineImports` and `combineExports` deduplicate and sort the import and
3
+ * export entries of one file, while `combineSources` deduplicates source entries in original order.
4
+ * `combineImports` also drops imports nothing references. This works on a file's members, not on
5
+ * schema content.
7
6
  */
8
7
  import type { ExportNode, ImportNode, SourceNode } from '../nodes/index.ts'
9
8
  import { extractStringsFromNodes } from './extractStringsFromNodes.ts'
@@ -38,9 +37,9 @@ function sortKey(node: { name?: string | Array<unknown> | null; isTypeOnly?: boo
38
37
  }
39
38
 
40
39
  /**
41
- * Deduplicates and merges `SourceNode` objects by `name + isExportable + isTypeOnly`.
42
- *
43
- * Unnamed sources are deduplicated by object reference. Returns a deduplicated array in original order.
40
+ * Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
41
+ * key. Unnamed sources fall back to their extracted node strings as the name part of the key. Returns
42
+ * the deduplicated array in original order.
44
43
  */
45
44
  export function combineSources(sources: Array<SourceNode>): Array<SourceNode> {
46
45
  const seen = new Map<string, SourceNode>()
@@ -3,13 +3,6 @@ import { createFunctionParameter, createFunctionParameters, createIndexedAccessT
3
3
  import type { FunctionParameterNode, FunctionParametersNode, OperationNode, ParameterNode, TypeExpression, TypeLiteralNode } from '../nodes/index.ts'
4
4
  import { resolveGroupType } from './refs.ts'
5
5
 
6
- /**
7
- * Applies casing rules to parameter names and returns a new array without mutating the input.
8
- *
9
- * Run it before handing parameters to schema builders so output property keys get the right casing
10
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
11
- * original array is returned unchanged.
12
- */
13
6
  const caseParamsMemo = memoize(new WeakMap<Array<ParameterNode>, (casing: string) => Array<ParameterNode>>(), (params) =>
14
7
  memoize(new Map<string, Array<ParameterNode>>(), (casing: string) =>
15
8
  params.map((param) => {
@@ -19,6 +12,13 @@ const caseParamsMemo = memoize(new WeakMap<Array<ParameterNode>, (casing: string
19
12
  ),
20
13
  )
21
14
 
15
+ /**
16
+ * Applies casing rules to parameter names and returns a new array without mutating the input.
17
+ *
18
+ * Run it before handing parameters to schema builders so output property keys get the right casing
19
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
20
+ * original array is returned unchanged.
21
+ */
22
22
  export function caseParams(params: Array<ParameterNode>, casing: 'camelcase' | undefined): Array<ParameterNode> {
23
23
  if (!casing) return params
24
24
  return caseParamsMemo(params)(casing)
@@ -229,7 +229,7 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
229
229
  const pathName = paramNames?.path ?? 'pathParams'
230
230
 
231
231
  const wrapType = (type: string): string => (typeWrapper ? typeWrapper(type) : type)
232
- // Only plain type-name references are wrapped, they need casing applied.
232
+ // typeWrapper takes a type-name string, so only plain references are wrapped.
233
233
  // TypeLiteral and IndexedAccessType expressions are pre-resolved and pass through unchanged.
234
234
  const wrapTypeExpression = (type: TypeExpression): TypeExpression => (typeof type === 'string' ? wrapType(type) : type)
235
235
 
package/src/utils/refs.ts CHANGED
@@ -10,24 +10,20 @@ const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url',
10
10
  * Returns the last path segment of a reference string.
11
11
  *
12
12
  * @example
13
- * ```ts
14
- * extractRefName('#/components/schemas/Pet') // 'Pet'
15
- * ```
13
+ * `extractRefName('#/components/schemas/Pet') // 'Pet'`
16
14
  */
17
15
  export function extractRefName(ref: string): string {
18
16
  return ref.split('/').at(-1) ?? ref
19
17
  }
20
18
 
21
19
  /**
22
- * Resolves the schema name of a ref node, falling back through `ref` `name` nested `schema.name`.
20
+ * Resolves the schema name of a ref node. Uses the last segment of `ref` when set, otherwise falls
21
+ * back to `name` then nested `schema.name`.
23
22
  *
24
23
  * Returns `null` for non-ref nodes or when no name resolves.
25
24
  *
26
25
  * @example
27
- * ```ts
28
- * resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' })
29
- * // => 'Pet'
30
- * ```
26
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
31
27
  */
32
28
  export function resolveRefName(node: SchemaNode | undefined): string | null {
33
29
  if (!node || node.type !== 'ref') return null
@@ -40,11 +36,11 @@ export function resolveRefName(node: SchemaNode | undefined): string | null {
40
36
  * Builds a PascalCase child schema name by joining a parent name and property name.
41
37
  * Returns `null` when there is no parent to nest under.
42
38
  *
43
- * @example
44
- * ```ts
45
- * childName('Order', 'shipping_address') // 'OrderShippingAddress'
46
- * childName(undefined, 'params') // null
47
- * ```
39
+ * @example Nested under a parent
40
+ * `childName('Order', 'shipping_address') // 'OrderShippingAddress'`
41
+ *
42
+ * @example No parent
43
+ * `childName(undefined, 'params') // null`
48
44
  */
49
45
  export function childName(parentName: string | null | undefined, propName: string): string | null {
50
46
  return parentName ? pascalCase([parentName, propName].join(' ')) : null
@@ -55,9 +51,7 @@ export function childName(parentName: string | null | undefined, propName: strin
55
51
  * empty parts.
56
52
  *
57
53
  * @example
58
- * ```ts
59
- * enumPropName('Order', 'status', 'enum') // 'OrderStatusEnum'
60
- * ```
54
+ * `enumPropName('Order', 'status', 'enum') // 'OrderStatusEnum'`
61
55
  */
62
56
  export function enumPropName(parentName: string | null | undefined, propName: string, enumSuffix: string): string {
63
57
  return pascalCase([parentName, propName, enumSuffix].filter(Boolean).join(' '))
@@ -66,8 +60,10 @@ export function enumPropName(parentName: string | null | undefined, propName: st
66
60
  /**
67
61
  * Merges a ref node with its resolved schema, giving usage-site fields precedence.
68
62
  *
69
- * Usage-site fields (`description`, `readOnly`, `nullable`, `deprecated`) on the ref node override
70
- * the same fields in the resolved `node.schema`. Non-ref nodes are returned unchanged.
63
+ * Every field set on the ref node except `kind`, `type`, `name`, `ref`, and `schema` overrides the
64
+ * same field in the resolved `node.schema` (for example `description`, `nullable`, `readOnly`,
65
+ * `deprecated`). Fields left `undefined` on the ref do not shadow the resolved schema. Non-ref
66
+ * nodes and refs without a resolved `schema` are returned unchanged.
71
67
  *
72
68
  * @example
73
69
  * ```ts
@@ -4,23 +4,7 @@ import { collect, collectLazy } from '../visitor.ts'
4
4
  import { resolveRefName } from './refs.ts'
5
5
 
6
6
  /**
7
- * Collects every named schema referenced transitively from a node through its ref edges.
8
- *
9
- * Refs are followed by name only, so the resolved `node.schema` is never traversed inline.
10
- *
11
- * @example Collect refs from a single schema
12
- * ```ts
13
- * const names = collectReferencedSchemaNames(petSchema)
14
- * // → Set { 'Category', 'Tag' }
15
- * ```
16
- *
17
- * @example Accumulate refs from multiple schemas into one set
18
- * ```ts
19
- * const out = new Set<string>()
20
- * for (const schema of schemas) {
21
- * collectReferencedSchemaNames(schema, out)
22
- * }
23
- * ```
7
+ * Memoized inner pass that walks a single node and returns the names of every schema it references.
24
8
  */
25
9
  const collectSchemaRefs = memoize(new WeakMap<SchemaNode, ReadonlySet<string>>(), (node: SchemaNode): ReadonlySet<string> => {
26
10
  const refs = new Set<string>()
@@ -35,31 +19,35 @@ const collectSchemaRefs = memoize(new WeakMap<SchemaNode, ReadonlySet<string>>()
35
19
  return refs
36
20
  })
37
21
 
38
- export function collectReferencedSchemaNames(node: SchemaNode | undefined, out: Set<string> = new Set()): Set<string> {
39
- if (!node) return out
40
- for (const name of collectSchemaRefs(node)) out.add(name)
41
- return out
42
- }
43
-
44
22
  /**
45
- * Collects the names of all top-level schemas transitively used by a set of operations.
23
+ * Collects the names of every ref found anywhere inside a node's own subtree.
46
24
  *
47
- * An operation uses a schema when its parameters, request body, or responses reference it, directly
48
- * or through other named schemas. The walk is iterative, so reference cycles are safe.
25
+ * Each ref contributes its name only, so the schema it points to is never traversed here. Pass `out`
26
+ * to accumulate names from several nodes into one set.
49
27
  *
50
- * Pair it with `include` filters so schemas reachable only from excluded operations stay ungenerated.
51
- *
52
- * @example Only generate schemas referenced by included operations
28
+ * @example Collect refs from a single schema
53
29
  * ```ts
54
- * const includedOps = operations.filter((op) => resolver.resolveOptions(op, { options, include }) !== null)
55
- * const allowed = collectUsedSchemaNames(includedOps, schemas)
30
+ * const names = collectReferencedSchemaNames(petSchema)
31
+ * // Set { 'Category', 'Tag' }
32
+ * ```
56
33
  *
34
+ * @example Accumulate refs from multiple schemas into one set
35
+ * ```ts
36
+ * const out = new Set<string>()
57
37
  * for (const schema of schemas) {
58
- * if (schema.name && !allowed.has(schema.name)) continue
59
- * // … generate schema
38
+ * collectReferencedSchemaNames(schema, out)
60
39
  * }
61
40
  * ```
62
41
  */
42
+ export function collectReferencedSchemaNames(node: SchemaNode | undefined, out: Set<string> = new Set()): Set<string> {
43
+ if (!node) return out
44
+ for (const name of collectSchemaRefs(node)) out.add(name)
45
+ return out
46
+ }
47
+
48
+ /**
49
+ * Memoized two-level cache keyed first on the operations array, then on the schemas array.
50
+ */
63
51
  const collectUsedSchemaNamesMemo = memoize(new WeakMap<ReadonlyArray<OperationNode>, (schemas: ReadonlyArray<SchemaNode>) => Set<string>>(), (ops) =>
64
52
  memoize(new WeakMap<ReadonlyArray<SchemaNode>, Set<string>>(), (schemas) => computeUsedSchemaNames(ops, schemas)),
65
53
  )
@@ -92,6 +80,26 @@ function computeUsedSchemaNames(operations: ReadonlyArray<OperationNode>, schema
92
80
  return result
93
81
  }
94
82
 
83
+ /**
84
+ * Collects the names of all top-level schemas transitively used by a set of operations.
85
+ *
86
+ * An operation uses a schema when its parameters, request body, or responses reference it, directly
87
+ * or through other named schemas. Once a name is added to the result it is not revisited, so
88
+ * reference cycles terminate.
89
+ *
90
+ * Pair it with `include` filters so schemas reachable only from excluded operations stay ungenerated.
91
+ *
92
+ * @example Only generate schemas referenced by included operations
93
+ * ```ts
94
+ * const includedOps = operations.filter((op) => resolver.resolveOptions(op, { options, include }) !== null)
95
+ * const allowed = collectUsedSchemaNames(includedOps, schemas)
96
+ *
97
+ * for (const schema of schemas) {
98
+ * if (schema.name && !allowed.has(schema.name)) continue
99
+ * // generate schema
100
+ * }
101
+ * ```
102
+ */
95
103
  export function collectUsedSchemaNames(operations: ReadonlyArray<OperationNode>, schemas: ReadonlyArray<SchemaNode>): Set<string> {
96
104
  return collectUsedSchemaNamesMemo(operations)(schemas)
97
105
  }
@@ -3,8 +3,8 @@ import { createSchema, type SchemaNode } from '../nodes/schema.ts'
3
3
 
4
4
  /**
5
5
  * Merges a run of adjacent anonymous object members into one. Named or non-object members break the
6
- * run and pass through. Stays a construction-time helper, not a macro, so callers keep control of the
7
- * member boundaries (such as keeping synthetic discriminant objects out of a run).
6
+ * run and pass through unchanged. The merge follows member order, so callers control which members
7
+ * combine by where they place them in the sequence.
8
8
  *
9
9
  * @example
10
10
  * ```ts
package/src/visitor.ts CHANGED
@@ -111,7 +111,7 @@ type ParentNodeMap = [
111
111
  * @example
112
112
  * ```ts
113
113
  * type SchemaParent = ParentOf<SchemaNode>
114
- * // InputNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode
114
+ * // InputNode | ContentNode | SchemaNode | PropertyNode | ParameterNode
115
115
  * ```
116
116
  */
117
117
  export type ParentOf<T extends Node, TEntries extends ReadonlyArray<[Node, unknown]> = ParentNodeMap> = TEntries extends [
@@ -1 +0,0 @@
1
- {"version":3,"file":"defineMacro-BryDiuQl.cjs","names":["visitorKeys","transform"],"sources":["../src/defineMacro.ts"],"sourcesContent":["import type { VisitorDepth } from './constants.ts'\nimport type { VisitorKey } from './defineNode.ts'\nimport { visitorKeys } from './defineNode.ts'\nimport type { Node } from './nodes/index.ts'\nimport type { Visitor, VisitorContext } from './visitor.ts'\nimport { transform } from './visitor.ts'\n\n/**\n * Ordering hint shared by macros and plugins. `pre` runs before unmarked items, `post` after,\n * and `undefined` keeps declaration order.\n */\nexport type Enforce = 'pre' | 'post'\n\n/**\n * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain\n * list keeps its authored order.\n */\nfunction enforceWeight(enforce?: Enforce): number {\n if (enforce === 'pre') return 0\n if (enforce === 'post') return 2\n return 1\n}\n\n/**\n * A named, composable transform over the Kubb AST. It carries the same per-kind callbacks as a\n * {@link Visitor} (`schema`, `operation`, …), plus a `name`, an optional `enforce` order, and an\n * optional `when` gate. Macros run on the shared AST, so the same macro works across every adapter\n * and output target. Exports follow the `macro<Name>` convention, mirroring plugins (`pluginTs`).\n */\nexport type Macro = Visitor & {\n /**\n * Macro identifier, surfaced in diagnostics. Follows the `macro<Name>` convention.\n */\n name: string\n /**\n * Ordering hint. `pre` macros run before unmarked macros, `post` macros run after.\n * Ordering within a bucket follows list order.\n */\n enforce?: Enforce\n /**\n * Gate checked against the current node before any callback runs. When it returns `false`\n * the macro is skipped for that node.\n */\n when?: (node: Node) => boolean\n}\n\n/**\n * Types a macro for inference and a single construction site, mirroring `definePlugin`.\n * Adds no runtime behavior.\n *\n * @example\n * ```ts\n * const macroUntagged = defineMacro({\n * name: 'untagged',\n * operation(node) {\n * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }\n * },\n * })\n * ```\n */\nexport function defineMacro(macro: Macro): Macro {\n return macro\n}\n\ntype MacroCallback = (node: Node, context: VisitorContext) => Node | null | undefined\n\ntype ChainProps = {\n macros: ReadonlyArray<Macro>\n key: VisitorKey\n node: Node\n context: VisitorContext\n}\n\n/**\n * Runs every macro's callback for one node kind in order, chaining the result so each macro sees\n * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the\n * original reference (structural sharing).\n */\nfunction chain({ macros, key, node, context }: ChainProps): Node | undefined {\n let current = node\n\n for (const macro of macros) {\n const callback = macro[key] as MacroCallback | undefined\n if (!callback) continue\n if (macro.when && !macro.when(current)) continue\n\n const next = callback(current, context)\n if (next != null) current = next\n }\n\n return current === node ? undefined : current\n}\n\n/**\n * Folds an ordered list of macros into a single {@link Visitor} that `transform` (and the per-plugin\n * transform layer in `@kubb/core`) can run. Macros are stable-sorted by `enforce`, then applied\n * sequentially per node so later macros see earlier output. This differs from a plain visitor, which\n * has no names, ordering, or composition.\n *\n * @example\n * ```ts\n * const visitor = composeMacros([macroSimplifyUnion, macroDiscriminatorEnum])\n * const next = transform(root, visitor)\n * ```\n */\nexport function composeMacros(macros: ReadonlyArray<Macro>): Visitor {\n const ordered = [...macros].sort((a, b) => enforceWeight(a.enforce) - enforceWeight(b.enforce))\n\n const visitor: Visitor = {}\n for (const key of visitorKeys) {\n if (!ordered.some((macro) => typeof macro[key] === 'function')) continue\n\n const callback = (node: Node, context: VisitorContext) => chain({ macros: ordered, key, node, context })\n ;(visitor as Record<VisitorKey, MacroCallback>)[key] = callback\n }\n\n return visitor\n}\n\n/**\n * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s\n * structural sharing, so an empty or no-op macro list returns the same reference. Pass\n * `depth: 'shallow'` to rewrite the root node only.\n *\n * @example\n * ```ts\n * const next = applyMacros(root, [macroIntegerToString])\n * ```\n *\n * @example Apply to the root node only\n * ```ts\n * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })\n * ```\n */\nexport function applyMacros<TNode extends Node>(root: TNode, macros: ReadonlyArray<Macro>, options?: { depth?: VisitorDepth }): TNode {\n if (macros.length === 0) return root\n\n return transform(root, { ...composeMacros(macros), ...options }) as TNode\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,cAAc,SAA2B;CAChD,IAAI,YAAY,OAAO,OAAO;CAC9B,IAAI,YAAY,QAAQ,OAAO;CAC/B,OAAO;AACT;;;;;;;;;;;;;;;AAuCA,SAAgB,YAAY,OAAqB;CAC/C,OAAO;AACT;;;;;;AAgBA,SAAS,MAAM,EAAE,QAAQ,KAAK,MAAM,WAAyC;CAC3E,IAAI,UAAU;CAEd,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,MAAM;EACvB,IAAI,CAAC,UAAU;EACf,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,GAAG;EAExC,MAAM,OAAO,SAAS,SAAS,OAAO;EACtC,IAAI,QAAQ,MAAM,UAAU;CAC9B;CAEA,OAAO,YAAY,OAAO,KAAA,IAAY;AACxC;;;;;;;;;;;;;AAcA,SAAgB,cAAc,QAAuC;CACnE,MAAM,UAAU,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,cAAc,EAAE,OAAO,IAAI,cAAc,EAAE,OAAO,CAAC;CAE9F,MAAM,UAAmB,CAAC;CAC1B,KAAK,MAAM,OAAOA,iBAAAA,aAAa;EAC7B,IAAI,CAAC,QAAQ,MAAM,UAAU,OAAO,MAAM,SAAS,UAAU,GAAG;EAEhE,MAAM,YAAY,MAAY,YAA4B,MAAM;GAAE,QAAQ;GAAS;GAAK;GAAM;EAAQ,CAAC;EACtG,QAA+C,OAAO;CACzD;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,YAAgC,MAAa,QAA8B,SAA2C;CACpI,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,OAAOC,gBAAAA,UAAU,MAAM;EAAE,GAAG,cAAc,MAAM;EAAG,GAAG;CAAQ,CAAC;AACjE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"defineMacro-CNkUpxia.js","names":[],"sources":["../src/defineMacro.ts"],"sourcesContent":["import type { VisitorDepth } from './constants.ts'\nimport type { VisitorKey } from './defineNode.ts'\nimport { visitorKeys } from './defineNode.ts'\nimport type { Node } from './nodes/index.ts'\nimport type { Visitor, VisitorContext } from './visitor.ts'\nimport { transform } from './visitor.ts'\n\n/**\n * Ordering hint shared by macros and plugins. `pre` runs before unmarked items, `post` after,\n * and `undefined` keeps declaration order.\n */\nexport type Enforce = 'pre' | 'post'\n\n/**\n * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain\n * list keeps its authored order.\n */\nfunction enforceWeight(enforce?: Enforce): number {\n if (enforce === 'pre') return 0\n if (enforce === 'post') return 2\n return 1\n}\n\n/**\n * A named, composable transform over the Kubb AST. It carries the same per-kind callbacks as a\n * {@link Visitor} (`schema`, `operation`, …), plus a `name`, an optional `enforce` order, and an\n * optional `when` gate. Macros run on the shared AST, so the same macro works across every adapter\n * and output target. Exports follow the `macro<Name>` convention, mirroring plugins (`pluginTs`).\n */\nexport type Macro = Visitor & {\n /**\n * Macro identifier, surfaced in diagnostics. Follows the `macro<Name>` convention.\n */\n name: string\n /**\n * Ordering hint. `pre` macros run before unmarked macros, `post` macros run after.\n * Ordering within a bucket follows list order.\n */\n enforce?: Enforce\n /**\n * Gate checked against the current node before any callback runs. When it returns `false`\n * the macro is skipped for that node.\n */\n when?: (node: Node) => boolean\n}\n\n/**\n * Types a macro for inference and a single construction site, mirroring `definePlugin`.\n * Adds no runtime behavior.\n *\n * @example\n * ```ts\n * const macroUntagged = defineMacro({\n * name: 'untagged',\n * operation(node) {\n * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }\n * },\n * })\n * ```\n */\nexport function defineMacro(macro: Macro): Macro {\n return macro\n}\n\ntype MacroCallback = (node: Node, context: VisitorContext) => Node | null | undefined\n\ntype ChainProps = {\n macros: ReadonlyArray<Macro>\n key: VisitorKey\n node: Node\n context: VisitorContext\n}\n\n/**\n * Runs every macro's callback for one node kind in order, chaining the result so each macro sees\n * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the\n * original reference (structural sharing).\n */\nfunction chain({ macros, key, node, context }: ChainProps): Node | undefined {\n let current = node\n\n for (const macro of macros) {\n const callback = macro[key] as MacroCallback | undefined\n if (!callback) continue\n if (macro.when && !macro.when(current)) continue\n\n const next = callback(current, context)\n if (next != null) current = next\n }\n\n return current === node ? undefined : current\n}\n\n/**\n * Folds an ordered list of macros into a single {@link Visitor} that `transform` (and the per-plugin\n * transform layer in `@kubb/core`) can run. Macros are stable-sorted by `enforce`, then applied\n * sequentially per node so later macros see earlier output. This differs from a plain visitor, which\n * has no names, ordering, or composition.\n *\n * @example\n * ```ts\n * const visitor = composeMacros([macroSimplifyUnion, macroDiscriminatorEnum])\n * const next = transform(root, visitor)\n * ```\n */\nexport function composeMacros(macros: ReadonlyArray<Macro>): Visitor {\n const ordered = [...macros].sort((a, b) => enforceWeight(a.enforce) - enforceWeight(b.enforce))\n\n const visitor: Visitor = {}\n for (const key of visitorKeys) {\n if (!ordered.some((macro) => typeof macro[key] === 'function')) continue\n\n const callback = (node: Node, context: VisitorContext) => chain({ macros: ordered, key, node, context })\n ;(visitor as Record<VisitorKey, MacroCallback>)[key] = callback\n }\n\n return visitor\n}\n\n/**\n * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s\n * structural sharing, so an empty or no-op macro list returns the same reference. Pass\n * `depth: 'shallow'` to rewrite the root node only.\n *\n * @example\n * ```ts\n * const next = applyMacros(root, [macroIntegerToString])\n * ```\n *\n * @example Apply to the root node only\n * ```ts\n * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })\n * ```\n */\nexport function applyMacros<TNode extends Node>(root: TNode, macros: ReadonlyArray<Macro>, options?: { depth?: VisitorDepth }): TNode {\n if (macros.length === 0) return root\n\n return transform(root, { ...composeMacros(macros), ...options }) as TNode\n}\n"],"mappings":";;;;;;;;AAiBA,SAAS,cAAc,SAA2B;CAChD,IAAI,YAAY,OAAO,OAAO;CAC9B,IAAI,YAAY,QAAQ,OAAO;CAC/B,OAAO;AACT;;;;;;;;;;;;;;;AAuCA,SAAgB,YAAY,OAAqB;CAC/C,OAAO;AACT;;;;;;AAgBA,SAAS,MAAM,EAAE,QAAQ,KAAK,MAAM,WAAyC;CAC3E,IAAI,UAAU;CAEd,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,MAAM;EACvB,IAAI,CAAC,UAAU;EACf,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,GAAG;EAExC,MAAM,OAAO,SAAS,SAAS,OAAO;EACtC,IAAI,QAAQ,MAAM,UAAU;CAC9B;CAEA,OAAO,YAAY,OAAO,KAAA,IAAY;AACxC;;;;;;;;;;;;;AAcA,SAAgB,cAAc,QAAuC;CACnE,MAAM,UAAU,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,cAAc,EAAE,OAAO,IAAI,cAAc,EAAE,OAAO,CAAC;CAE9F,MAAM,UAAmB,CAAC;CAC1B,KAAK,MAAM,OAAO,aAAa;EAC7B,IAAI,CAAC,QAAQ,MAAM,UAAU,OAAO,MAAM,SAAS,UAAU,GAAG;EAEhE,MAAM,YAAY,MAAY,YAA4B,MAAM;GAAE,QAAQ;GAAS;GAAK;GAAM;EAAQ,CAAC;EACtG,QAA+C,OAAO;CACzD;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,YAAgC,MAAa,QAA8B,SAA2C;CACpI,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,OAAO,UAAU,MAAM;EAAE,GAAG,cAAc,MAAM;EAAG,GAAG;CAAQ,CAAC;AACjE"}