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