@kubb/ast 5.0.0-alpha.3 → 5.0.0-alpha.30
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 +1123 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +283 -17
- package/dist/index.js +1102 -154
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/visitor-z-5U8NoF.d.ts +2200 -0
- package/package.json +3 -2
- package/src/constants.ts +118 -4
- package/src/factory.ts +332 -18
- package/src/guards.ts +63 -8
- package/src/index.ts +25 -7
- package/src/infer.ts +130 -0
- package/src/mocks.ts +12 -5
- package/src/nodes/base.ts +32 -4
- package/src/nodes/function.ts +201 -0
- package/src/nodes/http.ts +17 -5
- package/src/nodes/index.ts +21 -5
- package/src/nodes/operation.ts +69 -6
- package/src/nodes/parameter.ts +27 -1
- package/src/nodes/property.ts +23 -1
- package/src/nodes/response.ts +29 -3
- package/src/nodes/root.ts +41 -10
- package/src/nodes/schema.ts +440 -42
- package/src/printer.ts +172 -60
- package/src/refs.ts +36 -4
- package/src/resolvers.ts +45 -0
- package/src/transformers.ts +156 -0
- package/src/types.ts +13 -2
- package/src/utils.ts +431 -4
- package/src/visitor.ts +378 -81
- package/dist/visitor-oFfdU8QA.d.ts +0 -653
package/src/visitor.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import type { VisitorDepth } from './constants.ts'
|
|
2
2
|
import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
|
|
3
|
+
import { createParameter, createProperty } from './factory.ts'
|
|
3
4
|
import type { Node, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode } from './nodes/index.ts'
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Creates a small async concurrency limiter.
|
|
8
|
+
*
|
|
9
|
+
* At most `concurrency` tasks are in flight at once. Extra tasks are queued.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const limit = createLimit(2)
|
|
14
|
+
* for (const task of [taskA, taskB, taskC]) {
|
|
15
|
+
* await limit(() => task())
|
|
16
|
+
* }
|
|
17
|
+
* // only 2 tasks run at the same time
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
5
20
|
function createLimit(concurrency: number) {
|
|
6
21
|
let active = 0
|
|
7
22
|
const queue: Array<() => void> = []
|
|
@@ -31,64 +46,227 @@ function createLimit(concurrency: number) {
|
|
|
31
46
|
type LimitFn = ReturnType<typeof createLimit>
|
|
32
47
|
|
|
33
48
|
/**
|
|
34
|
-
*
|
|
49
|
+
* Ordered mapping of `[NodeType, ParentType]` pairs.
|
|
50
|
+
*
|
|
51
|
+
* `ParentOf` uses this map to find parent types.
|
|
35
52
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
type ParentNodeMap = [
|
|
54
|
+
[RootNode, undefined],
|
|
55
|
+
[OperationNode, RootNode],
|
|
56
|
+
[SchemaNode, RootNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode],
|
|
57
|
+
[PropertyNode, SchemaNode],
|
|
58
|
+
[ParameterNode, OperationNode],
|
|
59
|
+
[ResponseNode, OperationNode],
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolves the parent node type for a given AST node type.
|
|
64
|
+
*
|
|
65
|
+
* This is used by visitor context so `ctx.parent` is correctly typed
|
|
66
|
+
* for each callback.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* type RootParent = ParentOf<RootNode>
|
|
71
|
+
* // undefined
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* type PropertyParent = ParentOf<PropertyNode>
|
|
77
|
+
* // SchemaNode
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* type SchemaParent = ParentOf<SchemaNode>
|
|
83
|
+
* // RootNode | OperationNode | SchemaNode | PropertyNode | ParameterNode | ResponseNode
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export type ParentOf<T extends Node, TEntries extends ReadonlyArray<[Node, unknown]> = ParentNodeMap> = TEntries extends [
|
|
87
|
+
infer TEntry extends [Node, unknown],
|
|
88
|
+
...infer TRest extends ReadonlyArray<[Node, unknown]>,
|
|
89
|
+
]
|
|
90
|
+
? T extends TEntry[0]
|
|
91
|
+
? TEntry[1]
|
|
92
|
+
: ParentOf<T, TRest>
|
|
93
|
+
: Node
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Traversal context passed as the second argument to every visitor callback.
|
|
97
|
+
* `parent` is typed from the current node type.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const visitor: Visitor = {
|
|
102
|
+
* schema(node, { parent }) {
|
|
103
|
+
* // parent type is narrowed by node kind
|
|
104
|
+
* },
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export type VisitorContext<T extends Node = Node> = {
|
|
38
109
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
110
|
+
* Parent node of the currently visited node.
|
|
111
|
+
* For `RootNode`, this is `undefined`.
|
|
41
112
|
*/
|
|
42
|
-
|
|
113
|
+
parent?: ParentOf<T>
|
|
43
114
|
}
|
|
44
115
|
|
|
45
116
|
/**
|
|
46
|
-
* Synchronous visitor
|
|
117
|
+
* Synchronous visitor used by `transform`.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const visitor: Visitor = {
|
|
122
|
+
* operation(node) {
|
|
123
|
+
* return { ...node, operationId: `x_${node.operationId}` }
|
|
124
|
+
* },
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
47
127
|
*/
|
|
48
128
|
export type Visitor = {
|
|
49
|
-
root?(node: RootNode): void | RootNode
|
|
50
|
-
operation?(node: OperationNode): void | OperationNode
|
|
51
|
-
schema?(node: SchemaNode): void | SchemaNode
|
|
52
|
-
property?(node: PropertyNode): void | PropertyNode
|
|
53
|
-
parameter?(node: ParameterNode): void | ParameterNode
|
|
54
|
-
response?(node: ResponseNode): void | ResponseNode
|
|
129
|
+
root?(node: RootNode, context: VisitorContext<RootNode>): void | RootNode
|
|
130
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): void | OperationNode
|
|
131
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): void | SchemaNode
|
|
132
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): void | PropertyNode
|
|
133
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): void | ParameterNode
|
|
134
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): void | ResponseNode
|
|
55
135
|
}
|
|
56
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Utility type for values that can be returned directly or asynchronously.
|
|
139
|
+
*/
|
|
57
140
|
type MaybePromise<T> = T | Promise<T>
|
|
58
141
|
|
|
59
142
|
/**
|
|
60
143
|
* Async visitor for `walk`. Synchronous `Visitor` objects are compatible.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const visitor: AsyncVisitor = {
|
|
148
|
+
* async operation(node) {
|
|
149
|
+
* await Promise.resolve(node.operationId)
|
|
150
|
+
* },
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
61
153
|
*/
|
|
62
154
|
export type AsyncVisitor = {
|
|
63
|
-
root?(node: RootNode): MaybePromise<void | RootNode>
|
|
64
|
-
operation?(node: OperationNode): MaybePromise<void | OperationNode>
|
|
65
|
-
schema?(node: SchemaNode): MaybePromise<void | SchemaNode>
|
|
66
|
-
property?(node: PropertyNode): MaybePromise<void | PropertyNode>
|
|
67
|
-
parameter?(node: ParameterNode): MaybePromise<void | ParameterNode>
|
|
68
|
-
response?(node: ResponseNode): MaybePromise<void | ResponseNode>
|
|
155
|
+
root?(node: RootNode, context: VisitorContext<RootNode>): MaybePromise<void | RootNode>
|
|
156
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): MaybePromise<void | OperationNode>
|
|
157
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): MaybePromise<void | SchemaNode>
|
|
158
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): MaybePromise<void | PropertyNode>
|
|
159
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): MaybePromise<void | ParameterNode>
|
|
160
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): MaybePromise<void | ResponseNode>
|
|
69
161
|
}
|
|
70
162
|
|
|
71
163
|
/**
|
|
72
|
-
* Visitor
|
|
164
|
+
* Visitor used by `collect`.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* const visitor: CollectVisitor<string> = {
|
|
169
|
+
* operation(node) {
|
|
170
|
+
* return node.operationId
|
|
171
|
+
* },
|
|
172
|
+
* }
|
|
173
|
+
* ```
|
|
73
174
|
*/
|
|
74
175
|
export type CollectVisitor<T> = {
|
|
75
|
-
root?(node: RootNode): T | undefined
|
|
76
|
-
operation?(node: OperationNode): T | undefined
|
|
77
|
-
schema?(node: SchemaNode): T | undefined
|
|
78
|
-
property?(node: PropertyNode): T | undefined
|
|
79
|
-
parameter?(node: ParameterNode): T | undefined
|
|
80
|
-
response?(node: ResponseNode): T | undefined
|
|
176
|
+
root?(node: RootNode, context: VisitorContext<RootNode>): T | undefined
|
|
177
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): T | undefined
|
|
178
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): T | undefined
|
|
179
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): T | undefined
|
|
180
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): T | undefined
|
|
181
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): T | undefined
|
|
81
182
|
}
|
|
82
183
|
|
|
83
184
|
/**
|
|
84
|
-
*
|
|
185
|
+
* Options for `transform`.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* const options: TransformOptions = { depth: 'deep', schema: (node) => node }
|
|
190
|
+
* ```
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```ts
|
|
194
|
+
* // Only transform the current node, not nested children
|
|
195
|
+
* const options: TransformOptions = { depth: 'shallow', schema: (node) => node }
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export type TransformOptions = Visitor & {
|
|
199
|
+
/**
|
|
200
|
+
* Traversal depth (`'deep'` by default).
|
|
201
|
+
* @default 'deep'
|
|
202
|
+
*/
|
|
203
|
+
depth?: VisitorDepth
|
|
204
|
+
/**
|
|
205
|
+
* Internal parent override used during recursion.
|
|
206
|
+
*/
|
|
207
|
+
parent?: Node
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Options for `walk`.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const options: WalkOptions = { depth: 'deep', concurrency: 10, root: () => {} }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export type WalkOptions = AsyncVisitor & {
|
|
219
|
+
/**
|
|
220
|
+
* Traversal depth (`'deep'` by default).
|
|
221
|
+
* @default 'deep'
|
|
222
|
+
*/
|
|
223
|
+
depth?: VisitorDepth
|
|
224
|
+
/**
|
|
225
|
+
* Maximum number of sibling nodes visited concurrently.
|
|
226
|
+
* @default 30
|
|
227
|
+
*/
|
|
228
|
+
concurrency?: number
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Options for `collect`.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const options: CollectOptions<string> = { depth: 'shallow', schema: () => undefined }
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
export type CollectOptions<T> = CollectVisitor<T> & {
|
|
240
|
+
/**
|
|
241
|
+
* Traversal depth (`'deep'` by default).
|
|
242
|
+
* @default 'deep'
|
|
243
|
+
*/
|
|
244
|
+
depth?: VisitorDepth
|
|
245
|
+
/**
|
|
246
|
+
* Internal parent override used during recursion.
|
|
247
|
+
*/
|
|
248
|
+
parent?: Node
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns the immediate traversable children of `node`.
|
|
253
|
+
*
|
|
254
|
+
* For `Schema` nodes, children (`properties`, `items`, `members`, and non-boolean
|
|
255
|
+
* `additionalProperties`) are only included
|
|
256
|
+
* when `recurse` is `true`; shallow mode skips them.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* const children = getChildren(operationNode, true)
|
|
261
|
+
* // returns parameters, requestBody schema (if present), and responses
|
|
262
|
+
* ```
|
|
85
263
|
*/
|
|
86
264
|
function getChildren(node: Node, recurse: boolean): Array<Node> {
|
|
87
265
|
switch (node.kind) {
|
|
88
266
|
case 'Root':
|
|
89
267
|
return [...node.schemas, ...node.operations]
|
|
90
268
|
case 'Operation':
|
|
91
|
-
return [...node.parameters, ...(node.requestBody ? [node.requestBody] : []), ...node.responses]
|
|
269
|
+
return [...node.parameters, ...(node.requestBody?.schema ? [node.requestBody.schema] : []), ...node.responses]
|
|
92
270
|
case 'Schema': {
|
|
93
271
|
const children: Array<Node> = []
|
|
94
272
|
|
|
@@ -97,6 +275,7 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
|
|
|
97
275
|
if ('properties' in node && node.properties.length > 0) children.push(...node.properties)
|
|
98
276
|
if ('items' in node && node.items) children.push(...node.items)
|
|
99
277
|
if ('members' in node && node.members) children.push(...node.members)
|
|
278
|
+
if ('additionalProperties' in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties)
|
|
100
279
|
|
|
101
280
|
return children
|
|
102
281
|
}
|
|
@@ -106,159 +285,277 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
|
|
|
106
285
|
return [node.schema]
|
|
107
286
|
case 'Response':
|
|
108
287
|
return node.schema ? [node.schema] : []
|
|
288
|
+
case 'FunctionParameter':
|
|
289
|
+
case 'ParameterGroup':
|
|
290
|
+
case 'FunctionParameters':
|
|
291
|
+
case 'Type':
|
|
292
|
+
return []
|
|
109
293
|
}
|
|
110
294
|
}
|
|
111
295
|
|
|
112
296
|
/**
|
|
113
297
|
* Depth-first traversal for side effects. Visitor return values are ignored.
|
|
114
|
-
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
298
|
+
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
299
|
+
* (default: `WALK_CONCURRENCY`).
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```ts
|
|
303
|
+
* await walk(root, {
|
|
304
|
+
* operation(node) {
|
|
305
|
+
* console.log(node.operationId)
|
|
306
|
+
* },
|
|
307
|
+
* })
|
|
308
|
+
* ```
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* // Visit only the current node
|
|
313
|
+
* await walk(root, { depth: 'shallow', root: () => {} })
|
|
314
|
+
* ```
|
|
115
315
|
*/
|
|
116
|
-
export async function walk(node: Node,
|
|
316
|
+
export async function walk(node: Node, options: WalkOptions): Promise<void> {
|
|
117
317
|
const recurse = (options.depth ?? visitorDepths.deep) === visitorDepths.deep
|
|
118
318
|
const limit = createLimit(options.concurrency ?? WALK_CONCURRENCY)
|
|
119
|
-
|
|
319
|
+
|
|
320
|
+
return _walk(node, options, recurse, limit, undefined)
|
|
120
321
|
}
|
|
121
322
|
|
|
122
|
-
async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn): Promise<void> {
|
|
323
|
+
async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn, parent: Node | undefined): Promise<void> {
|
|
123
324
|
switch (node.kind) {
|
|
124
325
|
case 'Root':
|
|
125
|
-
await limit(() => visitor.root?.(node))
|
|
326
|
+
await limit(() => visitor.root?.(node, { parent: parent as ParentOf<RootNode> }))
|
|
126
327
|
break
|
|
127
328
|
case 'Operation':
|
|
128
|
-
await limit(() => visitor.operation?.(node))
|
|
329
|
+
await limit(() => visitor.operation?.(node, { parent: parent as ParentOf<OperationNode> }))
|
|
129
330
|
break
|
|
130
331
|
case 'Schema':
|
|
131
|
-
await limit(() => visitor.schema?.(node))
|
|
332
|
+
await limit(() => visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> }))
|
|
132
333
|
break
|
|
133
334
|
case 'Property':
|
|
134
|
-
await limit(() => visitor.property?.(node))
|
|
335
|
+
await limit(() => visitor.property?.(node, { parent: parent as ParentOf<PropertyNode> }))
|
|
135
336
|
break
|
|
136
337
|
case 'Parameter':
|
|
137
|
-
await limit(() => visitor.parameter?.(node))
|
|
338
|
+
await limit(() => visitor.parameter?.(node, { parent: parent as ParentOf<ParameterNode> }))
|
|
138
339
|
break
|
|
139
340
|
case 'Response':
|
|
140
|
-
await limit(() => visitor.response?.(node))
|
|
341
|
+
await limit(() => visitor.response?.(node, { parent: parent as ParentOf<ResponseNode> }))
|
|
342
|
+
break
|
|
343
|
+
case 'FunctionParameter':
|
|
344
|
+
case 'ParameterGroup':
|
|
345
|
+
case 'FunctionParameters':
|
|
141
346
|
break
|
|
142
347
|
}
|
|
143
348
|
|
|
144
349
|
const children = getChildren(node, recurse)
|
|
145
|
-
|
|
350
|
+
for (const child of children) {
|
|
351
|
+
await _walk(child, visitor, recurse, limit, node)
|
|
352
|
+
}
|
|
146
353
|
}
|
|
147
354
|
|
|
148
355
|
/**
|
|
149
|
-
*
|
|
356
|
+
* Runs a depth-first immutable transform.
|
|
357
|
+
*
|
|
358
|
+
* If a visitor returns a node, it replaces the current node.
|
|
359
|
+
* If it returns `undefined`, the current node stays the same.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* const next = transform(root, {
|
|
364
|
+
* operation(node) {
|
|
365
|
+
* return { ...node, operationId: `prefixed_${node.operationId}` }
|
|
366
|
+
* },
|
|
367
|
+
* })
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```ts
|
|
372
|
+
* // Shallow mode: only transform the input node
|
|
373
|
+
* const next = transform(root, { depth: 'shallow', root: (node) => node })
|
|
374
|
+
* ```
|
|
150
375
|
*/
|
|
151
|
-
export function transform(node: RootNode,
|
|
152
|
-
export function transform(node: OperationNode,
|
|
153
|
-
export function transform(node: SchemaNode,
|
|
154
|
-
export function transform(node: PropertyNode,
|
|
155
|
-
export function transform(node: ParameterNode,
|
|
156
|
-
export function transform(node: ResponseNode,
|
|
157
|
-
export function transform(node: Node,
|
|
158
|
-
export function transform(node: Node,
|
|
159
|
-
const
|
|
376
|
+
export function transform(node: RootNode, options: TransformOptions): RootNode
|
|
377
|
+
export function transform(node: OperationNode, options: TransformOptions): OperationNode
|
|
378
|
+
export function transform(node: SchemaNode, options: TransformOptions): SchemaNode
|
|
379
|
+
export function transform(node: PropertyNode, options: TransformOptions): PropertyNode
|
|
380
|
+
export function transform(node: ParameterNode, options: TransformOptions): ParameterNode
|
|
381
|
+
export function transform(node: ResponseNode, options: TransformOptions): ResponseNode
|
|
382
|
+
export function transform(node: Node, options: TransformOptions): Node
|
|
383
|
+
export function transform(node: Node, options: TransformOptions): Node {
|
|
384
|
+
const { depth, parent, ...visitor } = options
|
|
385
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
|
|
160
386
|
|
|
161
387
|
switch (node.kind) {
|
|
162
388
|
case 'Root': {
|
|
163
389
|
let root = node
|
|
164
|
-
const replaced = visitor.root?.(root)
|
|
390
|
+
const replaced = visitor.root?.(root, { parent: parent as ParentOf<RootNode> })
|
|
165
391
|
if (replaced) root = replaced
|
|
166
392
|
|
|
167
393
|
return {
|
|
168
394
|
...root,
|
|
169
|
-
schemas: root.schemas.map((s) => transform(s,
|
|
170
|
-
operations: root.operations.map((op) => transform(op,
|
|
395
|
+
schemas: root.schemas.map((s) => transform(s, { ...options, parent: root })),
|
|
396
|
+
operations: root.operations.map((op) => transform(op, { ...options, parent: root })),
|
|
171
397
|
}
|
|
172
398
|
}
|
|
173
399
|
case 'Operation': {
|
|
174
400
|
let op = node
|
|
175
|
-
const replaced = visitor.operation?.(op)
|
|
401
|
+
const replaced = visitor.operation?.(op, { parent: parent as ParentOf<OperationNode> })
|
|
176
402
|
if (replaced) op = replaced
|
|
177
403
|
|
|
178
404
|
return {
|
|
179
405
|
...op,
|
|
180
|
-
parameters: op.parameters.map((p) => transform(p,
|
|
181
|
-
requestBody: op.requestBody
|
|
182
|
-
|
|
406
|
+
parameters: op.parameters.map((p) => transform(p, { ...options, parent: op })),
|
|
407
|
+
requestBody: op.requestBody
|
|
408
|
+
? { ...op.requestBody, schema: op.requestBody.schema ? transform(op.requestBody.schema, { ...options, parent: op }) : undefined }
|
|
409
|
+
: undefined,
|
|
410
|
+
responses: op.responses.map((r) => transform(r, { ...options, parent: op })),
|
|
183
411
|
}
|
|
184
412
|
}
|
|
185
413
|
case 'Schema': {
|
|
186
414
|
let schema = node
|
|
187
|
-
const replaced = visitor.schema?.(schema)
|
|
415
|
+
const replaced = visitor.schema?.(schema, { parent: parent as ParentOf<SchemaNode> })
|
|
188
416
|
if (replaced) schema = replaced
|
|
189
417
|
|
|
418
|
+
const childOptions = { ...options, parent: schema }
|
|
419
|
+
|
|
190
420
|
return {
|
|
191
421
|
...schema,
|
|
192
|
-
...('properties' in schema && recurse ? { properties: schema.properties.map((p) => transform(p,
|
|
193
|
-
...('items' in schema && recurse ? { items: schema.items?.map((i) => transform(i,
|
|
194
|
-
...('members' in schema && recurse ? { members: schema.members?.map((m) => transform(m,
|
|
195
|
-
|
|
422
|
+
...('properties' in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {}),
|
|
423
|
+
...('items' in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {}),
|
|
424
|
+
...('members' in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {}),
|
|
425
|
+
...('additionalProperties' in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true
|
|
426
|
+
? { additionalProperties: transform(schema.additionalProperties, childOptions) }
|
|
427
|
+
: {}),
|
|
428
|
+
} as SchemaNode
|
|
196
429
|
}
|
|
197
430
|
case 'Property': {
|
|
198
431
|
let prop = node
|
|
199
|
-
const replaced = visitor.property?.(prop)
|
|
432
|
+
const replaced = visitor.property?.(prop, { parent: parent as ParentOf<PropertyNode> })
|
|
200
433
|
if (replaced) prop = replaced
|
|
201
434
|
|
|
202
|
-
return {
|
|
435
|
+
return createProperty({
|
|
203
436
|
...prop,
|
|
204
|
-
schema: transform(prop.schema,
|
|
205
|
-
}
|
|
437
|
+
schema: transform(prop.schema, { ...options, parent: prop }),
|
|
438
|
+
})
|
|
206
439
|
}
|
|
207
440
|
case 'Parameter': {
|
|
208
441
|
let param = node
|
|
209
|
-
const replaced = visitor.parameter?.(param)
|
|
442
|
+
const replaced = visitor.parameter?.(param, { parent: parent as ParentOf<ParameterNode> })
|
|
210
443
|
if (replaced) param = replaced
|
|
211
444
|
|
|
212
|
-
return {
|
|
445
|
+
return createParameter({
|
|
213
446
|
...param,
|
|
214
|
-
schema: transform(param.schema,
|
|
215
|
-
}
|
|
447
|
+
schema: transform(param.schema, { ...options, parent: param }),
|
|
448
|
+
})
|
|
216
449
|
}
|
|
217
450
|
case 'Response': {
|
|
218
451
|
let response = node
|
|
219
|
-
const replaced = visitor.response?.(response)
|
|
452
|
+
const replaced = visitor.response?.(response, { parent: parent as ParentOf<ResponseNode> })
|
|
220
453
|
if (replaced) response = replaced
|
|
221
454
|
|
|
222
455
|
return {
|
|
223
456
|
...response,
|
|
224
|
-
schema:
|
|
457
|
+
schema: transform(response.schema, { ...options, parent: response }),
|
|
225
458
|
}
|
|
226
459
|
}
|
|
460
|
+
case 'FunctionParameter':
|
|
461
|
+
case 'ParameterGroup':
|
|
462
|
+
case 'FunctionParameters':
|
|
463
|
+
case 'Type':
|
|
464
|
+
return node
|
|
227
465
|
}
|
|
228
466
|
}
|
|
229
467
|
|
|
230
468
|
/**
|
|
231
|
-
*
|
|
469
|
+
* Composes multiple visitors into one visitor, applied left to right.
|
|
470
|
+
*
|
|
471
|
+
* For each node kind, output from one visitor is input to the next.
|
|
472
|
+
* If a visitor returns `undefined`, the previous node value is kept.
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```ts
|
|
476
|
+
* const visitor = composeTransformers(
|
|
477
|
+
* { operation: (node) => ({ ...node, operationId: `a_${node.operationId}` }) },
|
|
478
|
+
* { operation: (node) => ({ ...node, operationId: `b_${node.operationId}` }) },
|
|
479
|
+
* )
|
|
480
|
+
* ```
|
|
232
481
|
*/
|
|
233
|
-
export function
|
|
234
|
-
|
|
482
|
+
export function composeTransformers(...visitors: Array<Visitor>): Visitor {
|
|
483
|
+
return {
|
|
484
|
+
root(node, context) {
|
|
485
|
+
return visitors.reduce<RootNode>((acc, v) => v.root?.(acc, context) ?? acc, node)
|
|
486
|
+
},
|
|
487
|
+
operation(node, context) {
|
|
488
|
+
return visitors.reduce<OperationNode>((acc, v) => v.operation?.(acc, context) ?? acc, node)
|
|
489
|
+
},
|
|
490
|
+
schema(node, context) {
|
|
491
|
+
return visitors.reduce<SchemaNode>((acc, v) => v.schema?.(acc, context) ?? acc, node)
|
|
492
|
+
},
|
|
493
|
+
property(node, context) {
|
|
494
|
+
return visitors.reduce<PropertyNode>((acc, v) => v.property?.(acc, context) ?? acc, node)
|
|
495
|
+
},
|
|
496
|
+
parameter(node, context) {
|
|
497
|
+
return visitors.reduce<ParameterNode>((acc, v) => v.parameter?.(acc, context) ?? acc, node)
|
|
498
|
+
},
|
|
499
|
+
response(node, context) {
|
|
500
|
+
return visitors.reduce<ResponseNode>((acc, v) => v.response?.(acc, context) ?? acc, node)
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Runs a depth-first synchronous collection pass.
|
|
507
|
+
*
|
|
508
|
+
* Non-`undefined` values returned by visitor callbacks are appended to the result.
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```ts
|
|
512
|
+
* const ids = collect(root, {
|
|
513
|
+
* operation(node) {
|
|
514
|
+
* return node.operationId
|
|
515
|
+
* },
|
|
516
|
+
* })
|
|
517
|
+
* ```
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```ts
|
|
521
|
+
* // Collect from only the current node
|
|
522
|
+
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
export function collect<T>(node: Node, options: CollectOptions<T>): Array<T> {
|
|
526
|
+
const { depth, parent, ...visitor } = options
|
|
527
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
|
|
235
528
|
const results: Array<T> = []
|
|
236
529
|
|
|
237
530
|
let v: T | undefined
|
|
238
531
|
switch (node.kind) {
|
|
239
532
|
case 'Root':
|
|
240
|
-
v = visitor.root?.(node)
|
|
533
|
+
v = visitor.root?.(node, { parent: parent as ParentOf<RootNode> })
|
|
241
534
|
break
|
|
242
535
|
case 'Operation':
|
|
243
|
-
v = visitor.operation?.(node)
|
|
536
|
+
v = visitor.operation?.(node, { parent: parent as ParentOf<OperationNode> })
|
|
244
537
|
break
|
|
245
538
|
case 'Schema':
|
|
246
|
-
v = visitor.schema?.(node)
|
|
539
|
+
v = visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> })
|
|
247
540
|
break
|
|
248
541
|
case 'Property':
|
|
249
|
-
v = visitor.property?.(node)
|
|
542
|
+
v = visitor.property?.(node, { parent: parent as ParentOf<PropertyNode> })
|
|
250
543
|
break
|
|
251
544
|
case 'Parameter':
|
|
252
|
-
v = visitor.parameter?.(node)
|
|
545
|
+
v = visitor.parameter?.(node, { parent: parent as ParentOf<ParameterNode> })
|
|
253
546
|
break
|
|
254
547
|
case 'Response':
|
|
255
|
-
v = visitor.response?.(node)
|
|
548
|
+
v = visitor.response?.(node, { parent: parent as ParentOf<ResponseNode> })
|
|
549
|
+
break
|
|
550
|
+
case 'FunctionParameter':
|
|
551
|
+
case 'ParameterGroup':
|
|
552
|
+
case 'FunctionParameters':
|
|
256
553
|
break
|
|
257
554
|
}
|
|
258
555
|
if (v !== undefined) results.push(v)
|
|
259
556
|
|
|
260
557
|
for (const child of getChildren(node, recurse)) {
|
|
261
|
-
for (const item of collect(child,
|
|
558
|
+
for (const item of collect(child, { ...options, parent: node })) {
|
|
262
559
|
results.push(item)
|
|
263
560
|
}
|
|
264
561
|
}
|