@kubb/ast 5.0.0-alpha.4 → 5.0.0-alpha.6
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/index.cjs +332 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +332 -30
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/{visitor-oFfdU8QA.d.ts → visitor-D1tc_9X5.d.ts} +89 -44
- package/package.json +3 -2
- package/src/constants.ts +1 -0
- package/src/index.ts +1 -1
- package/src/nodes/index.ts +1 -0
- package/src/nodes/root.ts +9 -3
- package/src/nodes/schema.ts +29 -3
- package/src/printer.ts +70 -42
- package/src/types.ts +1 -0
- package/src/utils.ts +25 -1
- package/src/visitor.ts +11 -1
package/src/nodes/schema.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import type { BaseNode } from './base.ts'
|
|
2
2
|
import type { PropertyNode } from './property.ts'
|
|
3
3
|
|
|
4
|
-
export type PrimitiveSchemaType =
|
|
4
|
+
export type PrimitiveSchemaType =
|
|
5
|
+
| 'string'
|
|
6
|
+
| 'number'
|
|
7
|
+
| 'integer'
|
|
8
|
+
| 'bigint'
|
|
9
|
+
| 'boolean'
|
|
10
|
+
| 'null'
|
|
11
|
+
| 'any'
|
|
12
|
+
| 'unknown'
|
|
13
|
+
| 'void'
|
|
14
|
+
| 'never'
|
|
15
|
+
| 'object'
|
|
16
|
+
| 'array'
|
|
17
|
+
| 'date'
|
|
5
18
|
|
|
6
19
|
export type ComplexSchemaType = 'tuple' | 'union' | 'intersection' | 'enum'
|
|
7
20
|
|
|
@@ -14,7 +27,7 @@ export type SchemaType = PrimitiveSchemaType | ComplexSchemaType | SpecialSchema
|
|
|
14
27
|
|
|
15
28
|
export type ScalarSchemaType = Exclude<
|
|
16
29
|
SchemaType,
|
|
17
|
-
'object' | 'array' | 'tuple' | 'union' | 'intersection' | 'enum' | 'ref' | 'datetime' | 'date' | 'time' | 'string' | 'number' | 'integer' | 'bigint'
|
|
30
|
+
'object' | 'array' | 'tuple' | 'union' | 'intersection' | 'enum' | 'ref' | 'datetime' | 'date' | 'time' | 'string' | 'number' | 'integer' | 'bigint' | 'url'
|
|
18
31
|
>
|
|
19
32
|
|
|
20
33
|
/**
|
|
@@ -206,6 +219,17 @@ export type ScalarSchemaNode = SchemaNodeBase & {
|
|
|
206
219
|
type: ScalarSchemaType
|
|
207
220
|
}
|
|
208
221
|
|
|
222
|
+
/**
|
|
223
|
+
* URL schema, optionally carrying an Express-style path template for template literal generation.
|
|
224
|
+
*/
|
|
225
|
+
export type UrlSchemaNode = SchemaNodeBase & {
|
|
226
|
+
type: 'url'
|
|
227
|
+
/**
|
|
228
|
+
* Express-style path (e.g. `'/pets/:petId'`). When set, printers may emit a template literal type.
|
|
229
|
+
*/
|
|
230
|
+
path?: string
|
|
231
|
+
}
|
|
232
|
+
|
|
209
233
|
/**
|
|
210
234
|
* Maps each schema type string to its `SchemaNode` variant. Used by `narrowSchema`.
|
|
211
235
|
*/
|
|
@@ -229,9 +253,10 @@ export type SchemaNodeByType = {
|
|
|
229
253
|
any: ScalarSchemaNode
|
|
230
254
|
unknown: ScalarSchemaNode
|
|
231
255
|
void: ScalarSchemaNode
|
|
256
|
+
never: ScalarSchemaNode
|
|
232
257
|
uuid: ScalarSchemaNode
|
|
233
258
|
email: ScalarSchemaNode
|
|
234
|
-
url:
|
|
259
|
+
url: UrlSchemaNode
|
|
235
260
|
blob: ScalarSchemaNode
|
|
236
261
|
}
|
|
237
262
|
|
|
@@ -250,4 +275,5 @@ export type SchemaNode =
|
|
|
250
275
|
| TimeSchemaNode
|
|
251
276
|
| StringSchemaNode
|
|
252
277
|
| NumberSchemaNode
|
|
278
|
+
| UrlSchemaNode
|
|
253
279
|
| ScalarSchemaNode
|
package/src/printer.ts
CHANGED
|
@@ -2,11 +2,12 @@ import type { SchemaNode, SchemaNodeByType, SchemaType } from './nodes/index.ts'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Handler context for `definePrinter` — mirrors `PluginContext` from `@kubb/core`.
|
|
5
|
-
* Available as `this` inside each node handler
|
|
5
|
+
* Available as `this` inside each node handler and the optional root-level `print`.
|
|
6
|
+
* `this.print` always dispatches to the `nodes` handlers (node-level printer).
|
|
6
7
|
*/
|
|
7
8
|
export type PrinterHandlerContext<TOutput, TOptions extends object> = {
|
|
8
9
|
/**
|
|
9
|
-
* Recursively print a nested `SchemaNode
|
|
10
|
+
* Recursively print a nested `SchemaNode` using the node-level handlers.
|
|
10
11
|
*/
|
|
11
12
|
print: (node: SchemaNode) => TOutput | null | undefined
|
|
12
13
|
/**
|
|
@@ -28,19 +29,20 @@ export type PrinterHandler<TOutput, TOptions extends object, T extends SchemaTyp
|
|
|
28
29
|
* Shape of the type parameter passed to `definePrinter`.
|
|
29
30
|
* Mirrors `AdapterFactoryOptions` / `PluginFactoryOptions` from `@kubb/core`.
|
|
30
31
|
*
|
|
31
|
-
* - `TName`
|
|
32
|
-
* - `TOptions`
|
|
33
|
-
* - `TOutput`
|
|
32
|
+
* - `TName` — unique string identifier (e.g. `'zod'`, `'ts'`)
|
|
33
|
+
* - `TOptions` — options passed to and stored on the printer
|
|
34
|
+
* - `TOutput` — the type emitted by node handlers
|
|
35
|
+
* - `TPrintOutput` — the type emitted by the public `print` override (defaults to `TOutput`)
|
|
34
36
|
*/
|
|
35
|
-
export type PrinterFactoryOptions<TName extends string = string, TOptions extends object = object, TOutput = unknown> = {
|
|
37
|
+
export type PrinterFactoryOptions<TName extends string = string, TOptions extends object = object, TOutput = unknown, TPrintOutput = TOutput> = {
|
|
36
38
|
name: TName
|
|
37
39
|
options: TOptions
|
|
38
40
|
output: TOutput
|
|
41
|
+
printOutput: TPrintOutput
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
42
45
|
* The object returned by calling a `definePrinter` instance.
|
|
43
|
-
* Mirrors the shape of `Adapter` from `@kubb/core`.
|
|
44
46
|
*/
|
|
45
47
|
export type Printer<T extends PrinterFactoryOptions = PrinterFactoryOptions> = {
|
|
46
48
|
/**
|
|
@@ -52,15 +54,18 @@ export type Printer<T extends PrinterFactoryOptions = PrinterFactoryOptions> = {
|
|
|
52
54
|
*/
|
|
53
55
|
options: T['options']
|
|
54
56
|
/**
|
|
55
|
-
*
|
|
57
|
+
* Public printer. If the builder provides a root-level `print`, this calls that
|
|
58
|
+
* higher-level function (which may produce full declarations). Otherwise falls back
|
|
59
|
+
* to the node-level dispatcher
|
|
56
60
|
*/
|
|
57
|
-
print: (node: SchemaNode) => T['
|
|
58
|
-
/**
|
|
59
|
-
* Maps `print` over an array of `SchemaNode`s.
|
|
60
|
-
*/
|
|
61
|
-
for: (nodes: Array<SchemaNode>) => Array<T['output'] | null | undefined>
|
|
61
|
+
print: (node: SchemaNode) => T['printOutput'] | null | undefined
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Builder function passed to `definePrinter`. Receives the resolved options and returns the
|
|
66
|
+
* printer configuration: a unique `name`, the stored `options`, node-level `nodes` handlers,
|
|
67
|
+
* and an optional root-level `print` override.
|
|
68
|
+
*/
|
|
64
69
|
type PrinterBuilder<T extends PrinterFactoryOptions> = (options: T['options']) => {
|
|
65
70
|
name: T['name']
|
|
66
71
|
/**
|
|
@@ -70,6 +75,12 @@ type PrinterBuilder<T extends PrinterFactoryOptions> = (options: T['options']) =
|
|
|
70
75
|
nodes: Partial<{
|
|
71
76
|
[K in SchemaType]: PrinterHandler<T['output'], T['options'], K>
|
|
72
77
|
}>
|
|
78
|
+
/**
|
|
79
|
+
* Optional root-level print override. When provided, becomes the public `printer.print`.
|
|
80
|
+
* `this.print(node)` inside this function calls the node-level dispatcher (`nodes` handlers),
|
|
81
|
+
* not the override itself — so recursion is safe.
|
|
82
|
+
*/
|
|
83
|
+
print?: (this: PrinterHandlerContext<T['output'], T['options']>, node: SchemaNode) => T['printOutput'] | null | undefined
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
/**
|
|
@@ -77,53 +88,70 @@ type PrinterBuilder<T extends PrinterFactoryOptions> = (options: T['options']) =
|
|
|
77
88
|
* from `@kubb/core` — wraps a builder to make options optional and separates raw options
|
|
78
89
|
* from resolved options.
|
|
79
90
|
*
|
|
80
|
-
*
|
|
91
|
+
* The builder receives resolved options and returns:
|
|
92
|
+
* - `name` — a unique identifier for the printer
|
|
93
|
+
* - `options` — options stored on the returned printer instance
|
|
94
|
+
* - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
|
|
95
|
+
* - `print` _(optional)_ — a root-level override that becomes the public `printer.print`.
|
|
96
|
+
* Inside it, `this.print(node)` still dispatches to the `nodes` map — safe recursion, no infinite loop.
|
|
97
|
+
*
|
|
98
|
+
* When no `print` override is provided, `printer.print` is the node-level dispatcher directly.
|
|
99
|
+
*
|
|
100
|
+
* @example Basic usage — Zod schema printer
|
|
81
101
|
* ```ts
|
|
82
|
-
* type ZodPrinter = PrinterFactoryOptions<'zod', { strict?: boolean },
|
|
102
|
+
* type ZodPrinter = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
|
|
83
103
|
*
|
|
84
|
-
* export const zodPrinter = definePrinter<ZodPrinter>((options) => {
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* },
|
|
93
|
-
* object(node) {
|
|
94
|
-
* const props = node.properties
|
|
95
|
-
* ?.map(p => `${p.name}: ${this.print(p)}`)
|
|
96
|
-
* .join(', ') ?? ''
|
|
97
|
-
* return `z.object({ ${props} })`
|
|
98
|
-
* },
|
|
104
|
+
* export const zodPrinter = definePrinter<ZodPrinter>((options) => ({
|
|
105
|
+
* name: 'zod',
|
|
106
|
+
* options: { strict: options.strict ?? true },
|
|
107
|
+
* nodes: {
|
|
108
|
+
* string: () => 'z.string()',
|
|
109
|
+
* object(node) {
|
|
110
|
+
* const props = node.properties.map(p => `${p.name}: ${this.print(p.schema)}`).join(', ')
|
|
111
|
+
* return `z.object({ ${props} })`
|
|
99
112
|
* },
|
|
100
|
-
* }
|
|
101
|
-
* })
|
|
113
|
+
* },
|
|
114
|
+
* }))
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @example With a root-level `print` override to wrap output in a full declaration
|
|
118
|
+
* ```ts
|
|
119
|
+
* type TsPrinter = PrinterFactoryOptions<'ts', { typeName?: string }, ts.TypeNode, ts.Node>
|
|
102
120
|
*
|
|
103
|
-
* const
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
121
|
+
* export const printerTs = definePrinter<TsPrinter>((options) => ({
|
|
122
|
+
* name: 'ts',
|
|
123
|
+
* options,
|
|
124
|
+
* nodes: { string: () => factory.keywordTypeNodes.string },
|
|
125
|
+
* print(node) {
|
|
126
|
+
* const type = this.print(node) // calls the node-level dispatcher
|
|
127
|
+
* if (!type || !this.options.typeName) return type
|
|
128
|
+
* return factory.createTypeAliasDeclaration(this.options.typeName, type)
|
|
129
|
+
* },
|
|
130
|
+
* }))
|
|
107
131
|
* ```
|
|
108
132
|
*/
|
|
109
133
|
export function definePrinter<T extends PrinterFactoryOptions = PrinterFactoryOptions>(build: PrinterBuilder<T>): (options?: T['options']) => Printer<T> {
|
|
110
134
|
return (options) => {
|
|
111
|
-
const { name, options: resolvedOptions, nodes } = build(options ?? ({} as T['options']))
|
|
135
|
+
const { name, options: resolvedOptions, nodes, print: printOverride } = build(options ?? ({} as T['options']))
|
|
112
136
|
|
|
113
137
|
const context: PrinterHandlerContext<T['output'], T['options']> = {
|
|
114
138
|
options: resolvedOptions,
|
|
115
139
|
print: (node: SchemaNode) => {
|
|
116
|
-
const
|
|
117
|
-
const handler = nodes[
|
|
118
|
-
|
|
140
|
+
const schemaType = node.type
|
|
141
|
+
const handler = nodes[schemaType]
|
|
142
|
+
|
|
143
|
+
if (!handler) return undefined
|
|
144
|
+
|
|
145
|
+
const typedHandler = handler as PrinterHandler<T['output'], T['options']>
|
|
146
|
+
|
|
147
|
+
return typedHandler.call(context, node as SchemaNodeByType[SchemaType])
|
|
119
148
|
},
|
|
120
149
|
}
|
|
121
150
|
|
|
122
151
|
return {
|
|
123
152
|
name,
|
|
124
153
|
options: resolvedOptions,
|
|
125
|
-
print: context.print,
|
|
126
|
-
for: (nodes) => nodes.map(context.print),
|
|
154
|
+
print: (printOverride ? printOverride.bind(context) : context.print) as Printer<T>['print'],
|
|
127
155
|
}
|
|
128
156
|
}
|
|
129
157
|
}
|
package/src/types.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type {
|
|
|
35
35
|
StringSchemaNode,
|
|
36
36
|
TimeSchemaNode,
|
|
37
37
|
UnionSchemaNode,
|
|
38
|
+
UrlSchemaNode,
|
|
38
39
|
} from './nodes/index.ts'
|
|
39
40
|
export type { Printer, PrinterFactoryOptions, PrinterHandler, PrinterHandlerContext } from './printer.ts'
|
|
40
41
|
export type { RefMap } from './refs.ts'
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { camelCase, isValidVarName } from '@internals/utils'
|
|
2
|
+
|
|
1
3
|
import { narrowSchema } from './guards.ts'
|
|
2
|
-
import type { SchemaNode } from './nodes/index.ts'
|
|
4
|
+
import type { ParameterNode, SchemaNode } from './nodes/index.ts'
|
|
3
5
|
import type { SchemaType } from './nodes/schema.ts'
|
|
4
6
|
|
|
5
7
|
const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'])
|
|
@@ -22,3 +24,25 @@ export function isPlainStringType(node: SchemaNode): boolean {
|
|
|
22
24
|
|
|
23
25
|
return false
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Transforms the `name` field of each parameter node according to the given casing strategy.
|
|
30
|
+
*
|
|
31
|
+
* The original `params` array is never mutated — a new array of cloned nodes is returned.
|
|
32
|
+
* When no `casing` is provided the original array is returned as-is.
|
|
33
|
+
*
|
|
34
|
+
* Use this before passing parameters to schema builders so that property keys
|
|
35
|
+
* in the generated output match the desired casing while the original
|
|
36
|
+
* `OperationNode.parameters` array remains untouched for other consumers.
|
|
37
|
+
*/
|
|
38
|
+
export function applyParamsCasing(params: Array<ParameterNode>, casing: 'camelcase' | undefined): Array<ParameterNode> {
|
|
39
|
+
if (!casing) {
|
|
40
|
+
return params
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return params.map((param) => {
|
|
44
|
+
const transformed = casing === 'camelcase' || !isValidVarName(param.name) ? camelCase(param.name) : param.name
|
|
45
|
+
|
|
46
|
+
return { ...param, name: transformed }
|
|
47
|
+
})
|
|
48
|
+
}
|
package/src/visitor.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { VisitorDepth } from './constants.ts'
|
|
|
2
2
|
import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
|
|
3
3
|
import type { Node, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode } from './nodes/index.ts'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Creates a concurrency-limiting wrapper. At most `concurrency` promises may be
|
|
7
|
+
* in-flight simultaneously; additional calls are queued and dispatched as slots free.
|
|
8
|
+
*/
|
|
5
9
|
function createLimit(concurrency: number) {
|
|
6
10
|
let active = 0
|
|
7
11
|
const queue: Array<() => void> = []
|
|
@@ -81,7 +85,10 @@ export type CollectVisitor<T> = {
|
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
|
84
|
-
*
|
|
88
|
+
* Returns the immediate traversable children of `node`.
|
|
89
|
+
*
|
|
90
|
+
* For `Schema` nodes, children (properties, items, members) are only included
|
|
91
|
+
* when `recurse` is `true`; shallow traversal omits them entirely.
|
|
85
92
|
*/
|
|
86
93
|
function getChildren(node: Node, recurse: boolean): Array<Node> {
|
|
87
94
|
switch (node.kind) {
|
|
@@ -119,6 +126,9 @@ export async function walk(node: Node, visitor: AsyncVisitor, options: VisitorOp
|
|
|
119
126
|
return _walk(node, visitor, recurse, limit)
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Internal recursive walk implementation — calls visitor then recurses into children.
|
|
131
|
+
*/
|
|
122
132
|
async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn): Promise<void> {
|
|
123
133
|
switch (node.kind) {
|
|
124
134
|
case 'Root':
|