@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.
- package/dist/{defineMacro-Vw-xQvq7.d.ts → defineMacro-BATi7xoC.d.ts} +4 -5
- package/dist/{defineMacro-BryDiuQl.cjs → defineMacro-CEZHaCXE.cjs} +5 -5
- package/dist/defineMacro-CEZHaCXE.cjs.map +1 -0
- package/dist/{defineMacro-CNkUpxia.js → defineMacro-Shz8f6SG.js} +3 -3
- package/dist/defineMacro-Shz8f6SG.js.map +1 -0
- package/dist/factory.cjs +56 -56
- package/dist/factory.cjs.map +1 -1
- package/dist/factory.d.ts +5 -5
- package/dist/factory.js +5 -5
- package/dist/factory.js.map +1 -1
- package/dist/{index-CTaN_ee_.d.ts → index-B9cc8MBS.d.ts} +46 -113
- package/dist/index.cjs +142 -271
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +108 -236
- package/dist/index.js.map +1 -1
- package/dist/macros.cjs +12 -12
- package/dist/macros.cjs.map +1 -1
- package/dist/macros.d.ts +2 -2
- package/dist/macros.js +4 -4
- package/dist/macros.js.map +1 -1
- package/dist/{operationParams-B44p6EQy.d.ts → operationParams-k5CKwSWZ.d.ts} +9 -2
- package/dist/{visitor-DepQEKyp.js → refs-BjNDuCBD.js} +115 -120
- package/dist/refs-BjNDuCBD.js.map +1 -0
- package/dist/{visitor-B7Zlnp4c.cjs → refs-u5SDdyV7.cjs} +139 -144
- package/dist/refs-u5SDdyV7.cjs.map +1 -0
- package/dist/{response-BeG6hq2D.cjs → schema-BkvrrOAr.cjs} +171 -153
- package/dist/schema-BkvrrOAr.cjs.map +1 -0
- package/dist/{response-Rd1uisM1.js → schema-Cbnxmz4b.js} +166 -154
- package/dist/schema-Cbnxmz4b.js.map +1 -0
- package/dist/{types-DQTgVSwE.d.ts → types-BB_xgRJ3.d.ts} +55 -79
- package/dist/types.d.ts +5 -5
- package/dist/{utils-BDI3kXlb.cjs → utils-CEepwqmb.cjs} +74 -67
- package/dist/utils-CEepwqmb.cjs.map +1 -0
- package/dist/{utils-DW9rnfsD.js → utils-DaXkewb1.js} +56 -49
- package/dist/utils-DaXkewb1.js.map +1 -0
- package/dist/utils.cjs +11 -11
- package/dist/utils.d.ts +44 -29
- package/dist/utils.js +3 -3
- package/package.json +1 -1
- package/src/constants.ts +3 -6
- package/src/defineMacro.ts +1 -1
- package/src/defineNode.ts +6 -9
- package/src/definePrinter.ts +5 -3
- package/src/dialect.ts +23 -13
- package/src/factory.ts +4 -4
- package/src/index.ts +1 -1
- package/src/infer.ts +1 -1
- package/src/macros/macroEnumName.ts +1 -1
- package/src/nodes/code.ts +8 -5
- package/src/nodes/file.ts +5 -4
- package/src/nodes/operation.ts +3 -0
- package/src/nodes/parameter.ts +5 -5
- package/src/nodes/property.ts +5 -5
- package/src/nodes/schema.ts +4 -4
- package/src/optionality.ts +15 -0
- package/src/types.ts +1 -2
- package/src/utils/codegen.ts +6 -7
- package/src/utils/fileMerge.ts +7 -8
- package/src/utils/operationParams.ts +8 -8
- package/src/utils/refs.ts +14 -18
- package/src/utils/schemaGraph.ts +41 -33
- package/src/utils/schemaMerge.ts +2 -2
- package/src/visitor.ts +1 -1
- package/dist/defineMacro-BryDiuQl.cjs.map +0 -1
- package/dist/defineMacro-CNkUpxia.js.map +0 -1
- package/dist/response-BeG6hq2D.cjs.map +0 -1
- package/dist/response-Rd1uisM1.js.map +0 -1
- package/dist/utils-BDI3kXlb.cjs.map +0 -1
- package/dist/utils-DW9rnfsD.js.map +0 -1
- package/dist/visitor-B7Zlnp4c.cjs.map +0 -1
- package/dist/visitor-DepQEKyp.js.map +0 -1
- package/src/dedupe.ts +0 -231
package/src/nodes/operation.ts
CHANGED
|
@@ -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
|
/**
|
package/src/nodes/parameter.ts
CHANGED
|
@@ -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
|
|
45
|
-
*
|
|
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
|
|
50
|
+
build: (props) => {
|
|
51
51
|
const required = props.required ?? false
|
|
52
|
-
return { ...props, required, schema:
|
|
52
|
+
return { ...props, required, schema: optionality(props.schema, required) }
|
|
53
53
|
},
|
|
54
54
|
children: ['schema'],
|
|
55
55
|
visitorKey: 'parameter',
|
package/src/nodes/property.ts
CHANGED
|
@@ -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
|
|
41
|
-
*
|
|
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
|
|
46
|
+
build: (props) => {
|
|
47
47
|
const required = props.required ?? false
|
|
48
|
-
return { ...props, required, schema:
|
|
48
|
+
return { ...props, required, schema: optionality(props.schema, required) }
|
|
49
49
|
},
|
|
50
50
|
children: ['schema'],
|
|
51
51
|
visitorKey: 'property',
|
package/src/nodes/schema.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
673
|
-
*
|
|
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 {
|
|
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'
|
package/src/utils/codegen.ts
CHANGED
|
@@ -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
|
|
6
|
-
* comments
|
|
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.
|
|
69
|
-
*
|
|
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.
|
|
89
|
-
* `z.
|
|
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
|
package/src/utils/fileMerge.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* File-member merging. `combineImports
|
|
3
|
-
*
|
|
4
|
-
* on a file's members, not on
|
|
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
|
|
42
|
-
*
|
|
43
|
-
*
|
|
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
|
-
//
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
70
|
-
*
|
|
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
|
package/src/utils/schemaGraph.ts
CHANGED
|
@@ -4,23 +4,7 @@ import { collect, collectLazy } from '../visitor.ts'
|
|
|
4
4
|
import { resolveRefName } from './refs.ts'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
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
|
|
23
|
+
* Collects the names of every ref found anywhere inside a node's own subtree.
|
|
46
24
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* @example Only generate schemas referenced by included operations
|
|
28
|
+
* @example Collect refs from a single schema
|
|
53
29
|
* ```ts
|
|
54
|
-
* const
|
|
55
|
-
*
|
|
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
|
-
*
|
|
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
|
}
|
package/src/utils/schemaMerge.ts
CHANGED
|
@@ -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.
|
|
7
|
-
*
|
|
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 |
|
|
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"}
|