@kubb/ast 5.0.0-beta.6 → 5.0.0-beta.60
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/LICENSE +17 -10
- package/README.md +50 -27
- package/dist/chunk-CNktS9qV.js +17 -0
- package/dist/extractStringsFromNodes-WMYJ8nQL.d.ts +82 -0
- package/dist/factory-Cl8Z7mcc.cjs +299 -0
- package/dist/factory-Cl8Z7mcc.cjs.map +1 -0
- package/dist/factory-Du7nEP4B.js +282 -0
- package/dist/factory-Du7nEP4B.js.map +1 -0
- package/dist/factory.cjs +29 -0
- package/dist/factory.d.ts +62 -0
- package/dist/factory.js +3 -0
- package/dist/index-BzjwdK2M.d.ts +2433 -0
- package/dist/index.cjs +442 -2180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +93 -3408
- package/dist/index.js +392 -2101
- package/dist/index.js.map +1 -1
- package/dist/operationParams-BZ07xDm0.d.ts +204 -0
- package/dist/response-DKxTr522.js +683 -0
- package/dist/response-DKxTr522.js.map +1 -0
- package/dist/response-DS5S3IG4.cjs +1058 -0
- package/dist/response-DS5S3IG4.cjs.map +1 -0
- package/dist/types-olVl9v5p.d.ts +764 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.js +1 -0
- package/dist/utils-D83JA6Xx.cjs +1645 -0
- package/dist/utils-D83JA6Xx.cjs.map +1 -0
- package/dist/utils-Dj_KoXMv.js +1389 -0
- package/dist/utils-Dj_KoXMv.js.map +1 -0
- package/dist/utils.cjs +34 -0
- package/dist/utils.d.ts +332 -0
- package/dist/utils.js +3 -0
- package/package.json +17 -6
- package/src/constants.ts +19 -64
- package/src/dedupe.ts +239 -0
- package/src/dialect.ts +53 -0
- package/src/factory.ts +67 -678
- package/src/guards.ts +10 -92
- package/src/index.ts +16 -43
- package/src/infer.ts +16 -14
- package/src/mocks.ts +7 -127
- package/src/node.ts +128 -0
- package/src/nodes/base.ts +5 -12
- package/src/nodes/code.ts +165 -74
- package/src/nodes/content.ts +56 -0
- package/src/nodes/file.ts +97 -36
- package/src/nodes/function.ts +216 -156
- package/src/nodes/http.ts +1 -35
- package/src/nodes/index.ts +23 -15
- package/src/nodes/input.ts +140 -0
- package/src/nodes/operation.ts +122 -68
- package/src/nodes/output.ts +23 -0
- package/src/nodes/parameter.ts +33 -3
- package/src/nodes/property.ts +36 -3
- package/src/nodes/requestBody.ts +61 -0
- package/src/nodes/response.ts +58 -13
- package/src/nodes/schema.ts +93 -17
- package/src/printer.ts +48 -42
- package/src/registry.ts +75 -0
- package/src/signature.ts +207 -0
- package/src/transformers.ts +50 -18
- package/src/types.ts +7 -68
- package/src/utils/codegen.ts +104 -0
- package/src/utils/extractStringsFromNodes.ts +34 -0
- package/src/utils/fileMerge.ts +184 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/operationParams.ts +353 -0
- package/src/utils/refs.ts +112 -0
- package/src/utils/schemaGraph.ts +169 -0
- package/src/utils/schemaTraversal.ts +86 -0
- package/src/utils/strings.ts +139 -0
- package/src/visitor.ts +227 -289
- package/dist/chunk--u3MIqq1.js +0 -8
- package/src/nodes/root.ts +0 -64
- package/src/refs.ts +0 -67
- package/src/resolvers.ts +0 -45
- package/src/utils.ts +0 -915
package/src/visitor.ts
CHANGED
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
import type { VisitorDepth } from './constants.ts'
|
|
2
2
|
import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
3
|
+
import type { NodeDef } from './node.ts'
|
|
4
|
+
import type {
|
|
5
|
+
ContentNode,
|
|
6
|
+
InputNode,
|
|
7
|
+
Node,
|
|
8
|
+
NodeKind,
|
|
9
|
+
OperationNode,
|
|
10
|
+
OutputNode,
|
|
11
|
+
ParameterNode,
|
|
12
|
+
PropertyNode,
|
|
13
|
+
RequestBodyNode,
|
|
14
|
+
ResponseNode,
|
|
15
|
+
SchemaNode,
|
|
16
|
+
} from './nodes/index.ts'
|
|
17
|
+
import { nodeDefs } from './registry.ts'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Child node fields per node kind, in traversal order (Babel's `VISITOR_KEYS`).
|
|
21
|
+
* Derived from each definition's `children`.
|
|
22
|
+
*/
|
|
23
|
+
const VISITOR_KEYS = Object.fromEntries(nodeDefs.flatMap((def) => (def.children ? [[def.kind, def.children] as const] : []))) as Partial<
|
|
24
|
+
Record<NodeKind, ReadonlyArray<string>>
|
|
25
|
+
>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Maps a node kind to the matching visitor callback name. Derived from each
|
|
29
|
+
* definition's `visitorKey`.
|
|
30
|
+
*/
|
|
31
|
+
const VISITOR_KEY_BY_KIND = Object.fromEntries(nodeDefs.flatMap((def) => (def.visitorKey ? [[def.kind, def.visitorKey] as const] : []))) as Partial<
|
|
32
|
+
Record<NodeKind, NonNullable<NodeDef['visitorKey']>>
|
|
33
|
+
>
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Per-kind builders rerun after children are rebuilt. Derived from each
|
|
37
|
+
* definition's `rebuild` flag.
|
|
38
|
+
*/
|
|
39
|
+
const nodeRebuilders = Object.fromEntries(
|
|
40
|
+
nodeDefs.flatMap((def) => (def.rebuild ? [[def.kind, def.create as unknown as (node: Node) => Node] as const] : [])),
|
|
41
|
+
) as Partial<Record<NodeKind, (node: Node) => Node>>
|
|
5
42
|
|
|
6
43
|
/**
|
|
7
44
|
* Creates a small async concurrency limiter.
|
|
@@ -54,7 +91,9 @@ type ParentNodeMap = [
|
|
|
54
91
|
[InputNode, undefined],
|
|
55
92
|
[OutputNode, undefined],
|
|
56
93
|
[OperationNode, InputNode],
|
|
57
|
-
[
|
|
94
|
+
[RequestBodyNode, OperationNode],
|
|
95
|
+
[ContentNode, RequestBodyNode | ResponseNode],
|
|
96
|
+
[SchemaNode, InputNode | ContentNode | SchemaNode | PropertyNode | ParameterNode],
|
|
58
97
|
[PropertyNode, SchemaNode],
|
|
59
98
|
[ParameterNode, OperationNode],
|
|
60
99
|
[ResponseNode, OperationNode],
|
|
@@ -63,8 +102,7 @@ type ParentNodeMap = [
|
|
|
63
102
|
/**
|
|
64
103
|
* Resolves the parent node type for a given AST node type.
|
|
65
104
|
*
|
|
66
|
-
*
|
|
67
|
-
* for each callback.
|
|
105
|
+
* Visitor context relies on this so `ctx.parent` is typed for each callback.
|
|
68
106
|
*
|
|
69
107
|
* @example
|
|
70
108
|
* ```ts
|
|
@@ -115,29 +153,43 @@ export type VisitorContext<T extends Node = Node> = {
|
|
|
115
153
|
}
|
|
116
154
|
|
|
117
155
|
/**
|
|
118
|
-
* Synchronous visitor
|
|
156
|
+
* Synchronous visitor consumed by `transform`. Each optional callback runs
|
|
157
|
+
* for the matching node type. Return a new node to replace it, or `undefined`
|
|
158
|
+
* to leave it untouched.
|
|
119
159
|
*
|
|
120
|
-
*
|
|
160
|
+
* Plugins typically expose `transformer` so users can supply a `Visitor` that
|
|
161
|
+
* rewrites the AST before printing.
|
|
162
|
+
*
|
|
163
|
+
* @example Prefix every operationId
|
|
121
164
|
* ```ts
|
|
122
165
|
* const visitor: Visitor = {
|
|
123
166
|
* operation(node) {
|
|
124
|
-
* return { ...node, operationId: `
|
|
167
|
+
* return { ...node, operationId: `api_${node.operationId}` }
|
|
168
|
+
* },
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @example Strip schema descriptions
|
|
173
|
+
* ```ts
|
|
174
|
+
* const visitor: Visitor = {
|
|
175
|
+
* schema(node) {
|
|
176
|
+
* return { ...node, description: undefined }
|
|
125
177
|
* },
|
|
126
178
|
* }
|
|
127
179
|
* ```
|
|
128
180
|
*/
|
|
129
181
|
export type Visitor = {
|
|
130
|
-
input?(node: InputNode, context: VisitorContext<InputNode>):
|
|
131
|
-
output?(node: OutputNode, context: VisitorContext<OutputNode>):
|
|
132
|
-
operation?(node: OperationNode, context: VisitorContext<OperationNode>):
|
|
133
|
-
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>):
|
|
134
|
-
property?(node: PropertyNode, context: VisitorContext<PropertyNode>):
|
|
135
|
-
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>):
|
|
136
|
-
response?(node: ResponseNode, context: VisitorContext<ResponseNode>):
|
|
182
|
+
input?(node: InputNode, context: VisitorContext<InputNode>): undefined | null | InputNode
|
|
183
|
+
output?(node: OutputNode, context: VisitorContext<OutputNode>): undefined | null | OutputNode
|
|
184
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): undefined | null | OperationNode
|
|
185
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): undefined | null | SchemaNode
|
|
186
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): undefined | null | PropertyNode
|
|
187
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): undefined | null | ParameterNode
|
|
188
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): undefined | null | ResponseNode
|
|
137
189
|
}
|
|
138
190
|
|
|
139
191
|
/**
|
|
140
|
-
*
|
|
192
|
+
* A visitor callback result that may be sync or async.
|
|
141
193
|
*/
|
|
142
194
|
type MaybePromise<T> = T | Promise<T>
|
|
143
195
|
|
|
@@ -153,14 +205,14 @@ type MaybePromise<T> = T | Promise<T>
|
|
|
153
205
|
* }
|
|
154
206
|
* ```
|
|
155
207
|
*/
|
|
156
|
-
|
|
157
|
-
input?(node: InputNode, context: VisitorContext<InputNode>): MaybePromise<
|
|
158
|
-
output?(node: OutputNode, context: VisitorContext<OutputNode>): MaybePromise<
|
|
159
|
-
operation?(node: OperationNode, context: VisitorContext<OperationNode>): MaybePromise<
|
|
160
|
-
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): MaybePromise<
|
|
161
|
-
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): MaybePromise<
|
|
162
|
-
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): MaybePromise<
|
|
163
|
-
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): MaybePromise<
|
|
208
|
+
type AsyncVisitor = {
|
|
209
|
+
input?(node: InputNode, context: VisitorContext<InputNode>): MaybePromise<undefined | null | InputNode>
|
|
210
|
+
output?(node: OutputNode, context: VisitorContext<OutputNode>): MaybePromise<undefined | null | OutputNode>
|
|
211
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): MaybePromise<undefined | null | OperationNode>
|
|
212
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): MaybePromise<undefined | null | SchemaNode>
|
|
213
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): MaybePromise<undefined | null | PropertyNode>
|
|
214
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): MaybePromise<undefined | null | ParameterNode>
|
|
215
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): MaybePromise<undefined | null | ResponseNode>
|
|
164
216
|
}
|
|
165
217
|
|
|
166
218
|
/**
|
|
@@ -175,14 +227,14 @@ export type AsyncVisitor = {
|
|
|
175
227
|
* }
|
|
176
228
|
* ```
|
|
177
229
|
*/
|
|
178
|
-
|
|
179
|
-
input?(node: InputNode, context: VisitorContext<InputNode>): T | undefined
|
|
180
|
-
output?(node: OutputNode, context: VisitorContext<OutputNode>): T | undefined
|
|
181
|
-
operation?(node: OperationNode, context: VisitorContext<OperationNode>): T | undefined
|
|
182
|
-
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): T | undefined
|
|
183
|
-
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): T | undefined
|
|
184
|
-
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): T | undefined
|
|
185
|
-
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): T | undefined
|
|
230
|
+
type CollectVisitor<T> = {
|
|
231
|
+
input?(node: InputNode, context: VisitorContext<InputNode>): T | null | undefined
|
|
232
|
+
output?(node: OutputNode, context: VisitorContext<OutputNode>): T | null | undefined
|
|
233
|
+
operation?(node: OperationNode, context: VisitorContext<OperationNode>): T | null | undefined
|
|
234
|
+
schema?(node: SchemaNode, context: VisitorContext<SchemaNode>): T | null | undefined
|
|
235
|
+
property?(node: PropertyNode, context: VisitorContext<PropertyNode>): T | null | undefined
|
|
236
|
+
parameter?(node: ParameterNode, context: VisitorContext<ParameterNode>): T | null | undefined
|
|
237
|
+
response?(node: ResponseNode, context: VisitorContext<ResponseNode>): T | null | undefined
|
|
186
238
|
}
|
|
187
239
|
|
|
188
240
|
/**
|
|
@@ -201,7 +253,7 @@ export type CollectVisitor<T> = {
|
|
|
201
253
|
*/
|
|
202
254
|
export type TransformOptions = Visitor & {
|
|
203
255
|
/**
|
|
204
|
-
* Traversal depth
|
|
256
|
+
* Traversal depth.
|
|
205
257
|
* @default 'deep'
|
|
206
258
|
*/
|
|
207
259
|
depth?: VisitorDepth
|
|
@@ -221,7 +273,7 @@ export type TransformOptions = Visitor & {
|
|
|
221
273
|
*/
|
|
222
274
|
export type WalkOptions = AsyncVisitor & {
|
|
223
275
|
/**
|
|
224
|
-
* Traversal depth
|
|
276
|
+
* Traversal depth.
|
|
225
277
|
* @default 'deep'
|
|
226
278
|
*/
|
|
227
279
|
depth?: VisitorDepth
|
|
@@ -242,7 +294,7 @@ export type WalkOptions = AsyncVisitor & {
|
|
|
242
294
|
*/
|
|
243
295
|
export type CollectOptions<T> = CollectVisitor<T> & {
|
|
244
296
|
/**
|
|
245
|
-
* Traversal depth
|
|
297
|
+
* Traversal depth.
|
|
246
298
|
* @default 'deep'
|
|
247
299
|
*/
|
|
248
300
|
depth?: VisitorDepth
|
|
@@ -252,61 +304,70 @@ export type CollectOptions<T> = CollectVisitor<T> & {
|
|
|
252
304
|
parent?: Node
|
|
253
305
|
}
|
|
254
306
|
|
|
307
|
+
const visitorKeysByKind = VISITOR_KEYS as Record<string, ReadonlyArray<string> | undefined>
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Returns `true` when `value` is an AST node (an object carrying a `kind`).
|
|
311
|
+
*/
|
|
312
|
+
function isNode(value: unknown): value is Node {
|
|
313
|
+
return typeof value === 'object' && value !== null && 'kind' in value
|
|
314
|
+
}
|
|
315
|
+
|
|
255
316
|
/**
|
|
256
|
-
* Returns the immediate traversable children of `node
|
|
317
|
+
* Returns the immediate traversable children of `node` based on {@link VISITOR_KEYS}.
|
|
257
318
|
*
|
|
258
|
-
*
|
|
259
|
-
* `additionalProperties`) are only included
|
|
260
|
-
* when `recurse` is `true`; shallow mode skips them.
|
|
319
|
+
* `Schema` children are only included when `recurse` is `true`. Shallow mode skips them.
|
|
261
320
|
*
|
|
262
321
|
* @example
|
|
263
322
|
* ```ts
|
|
264
323
|
* const children = getChildren(operationNode, true)
|
|
265
|
-
* // returns parameters,
|
|
324
|
+
* // returns parameters, the request body, and responses
|
|
266
325
|
* ```
|
|
267
326
|
*/
|
|
268
|
-
function getChildren(node: Node, recurse: boolean):
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if ('properties' in node && node.properties.length > 0) children.push(...node.properties)
|
|
282
|
-
if ('items' in node && node.items) children.push(...node.items)
|
|
283
|
-
if ('members' in node && node.members) children.push(...node.members)
|
|
284
|
-
if ('additionalProperties' in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties)
|
|
285
|
-
|
|
286
|
-
return children
|
|
327
|
+
function* getChildren(node: Node, recurse: boolean): Generator<Node, void, undefined> {
|
|
328
|
+
if (node.kind === 'Schema' && !recurse) return
|
|
329
|
+
|
|
330
|
+
const keys = visitorKeysByKind[node.kind]
|
|
331
|
+
if (!keys) return
|
|
332
|
+
|
|
333
|
+
const record = node as unknown as Record<string, unknown>
|
|
334
|
+
for (const key of keys) {
|
|
335
|
+
const value = record[key]
|
|
336
|
+
if (Array.isArray(value)) {
|
|
337
|
+
for (const item of value) if (isNode(item)) yield item
|
|
338
|
+
} else if (isNode(value)) {
|
|
339
|
+
yield value
|
|
287
340
|
}
|
|
288
|
-
case 'Property':
|
|
289
|
-
return [node.schema]
|
|
290
|
-
case 'Parameter':
|
|
291
|
-
return [node.schema]
|
|
292
|
-
case 'Response':
|
|
293
|
-
return node.schema ? [node.schema] : []
|
|
294
|
-
case 'FunctionParameter':
|
|
295
|
-
case 'ParameterGroup':
|
|
296
|
-
case 'FunctionParameters':
|
|
297
|
-
case 'Type':
|
|
298
|
-
return []
|
|
299
|
-
default:
|
|
300
|
-
return []
|
|
301
341
|
}
|
|
302
342
|
}
|
|
303
343
|
|
|
304
344
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
345
|
+
* Runs the visitor callback that matches `node.kind` with the traversal
|
|
346
|
+
* context. The result is a replacement node, a collected value, or `undefined`
|
|
347
|
+
* when no callback is registered for the kind.
|
|
308
348
|
*
|
|
309
|
-
*
|
|
349
|
+
* Shared by `walk`, `transform`, and `collectLazy` so node-kind dispatch lives
|
|
350
|
+
* in one place. `TResult` is the caller's expected return: the same node type
|
|
351
|
+
* for `transform`, the collected value type for `collectLazy`, ignored for `walk`.
|
|
352
|
+
*/
|
|
353
|
+
function applyVisitor<TResult>(node: Node, visitor: Visitor | AsyncVisitor | CollectVisitor<unknown>, parent: Node | undefined): TResult | null | undefined {
|
|
354
|
+
const key = VISITOR_KEY_BY_KIND[node.kind]
|
|
355
|
+
if (!key) return undefined
|
|
356
|
+
|
|
357
|
+
const fn = visitor[key] as ((node: Node, context: VisitorContext) => TResult | null | undefined) | undefined
|
|
358
|
+
|
|
359
|
+
return fn?.(node, { parent })
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Async depth-first traversal for side effects. Visitor return values are
|
|
364
|
+
* ignored. Use `transform` when you want to rewrite nodes.
|
|
365
|
+
*
|
|
366
|
+
* Sibling nodes at each depth run concurrently up to `options.concurrency`
|
|
367
|
+
* (defaults to `WALK_CONCURRENCY`). Higher values overlap I/O-bound visitor
|
|
368
|
+
* work. Lower values reduce memory pressure.
|
|
369
|
+
*
|
|
370
|
+
* @example Log every operation
|
|
310
371
|
* ```ts
|
|
311
372
|
* await walk(root, {
|
|
312
373
|
* operation(node) {
|
|
@@ -315,10 +376,9 @@ function getChildren(node: Node, recurse: boolean): Array<Node> {
|
|
|
315
376
|
* })
|
|
316
377
|
* ```
|
|
317
378
|
*
|
|
318
|
-
* @example
|
|
379
|
+
* @example Only visit the root node
|
|
319
380
|
* ```ts
|
|
320
|
-
*
|
|
321
|
-
* await walk(root, { depth: 'shallow', root: () => {} })
|
|
381
|
+
* await walk(root, { depth: 'shallow', input: () => {} })
|
|
322
382
|
* ```
|
|
323
383
|
*/
|
|
324
384
|
export async function walk(node: Node, options: WalkOptions): Promise<void> {
|
|
@@ -329,55 +389,26 @@ export async function walk(node: Node, options: WalkOptions): Promise<void> {
|
|
|
329
389
|
}
|
|
330
390
|
|
|
331
391
|
async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit: LimitFn, parent: Node | undefined): Promise<void> {
|
|
332
|
-
|
|
333
|
-
case 'Input':
|
|
334
|
-
await limit(() => visitor.input?.(node, { parent: parent as ParentOf<InputNode> }))
|
|
335
|
-
break
|
|
336
|
-
case 'Output':
|
|
337
|
-
await limit(() => visitor.output?.(node, { parent: parent as ParentOf<OutputNode> }))
|
|
338
|
-
break
|
|
339
|
-
case 'Operation':
|
|
340
|
-
await limit(() =>
|
|
341
|
-
visitor.operation?.(node, {
|
|
342
|
-
parent: parent as ParentOf<OperationNode>,
|
|
343
|
-
}),
|
|
344
|
-
)
|
|
345
|
-
break
|
|
346
|
-
case 'Schema':
|
|
347
|
-
await limit(() => visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> }))
|
|
348
|
-
break
|
|
349
|
-
case 'Property':
|
|
350
|
-
await limit(() => visitor.property?.(node, { parent: parent as ParentOf<PropertyNode> }))
|
|
351
|
-
break
|
|
352
|
-
case 'Parameter':
|
|
353
|
-
await limit(() =>
|
|
354
|
-
visitor.parameter?.(node, {
|
|
355
|
-
parent: parent as ParentOf<ParameterNode>,
|
|
356
|
-
}),
|
|
357
|
-
)
|
|
358
|
-
break
|
|
359
|
-
case 'Response':
|
|
360
|
-
await limit(() => visitor.response?.(node, { parent: parent as ParentOf<ResponseNode> }))
|
|
361
|
-
break
|
|
362
|
-
case 'FunctionParameter':
|
|
363
|
-
case 'ParameterGroup':
|
|
364
|
-
case 'FunctionParameters':
|
|
365
|
-
break
|
|
366
|
-
}
|
|
392
|
+
await limit(() => applyVisitor(node, visitor, parent))
|
|
367
393
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
394
|
+
// Visit siblings concurrently and let the shared `limit` cap how many callbacks
|
|
395
|
+
// run at once. Awaiting each child sequentially here would serialize the whole
|
|
396
|
+
// traversal and make `concurrency` inert. Every visitor callback would run one
|
|
397
|
+
// at a time regardless of the limit.
|
|
398
|
+
const children = Array.from(getChildren(node, recurse))
|
|
399
|
+
if (children.length === 0) return
|
|
400
|
+
|
|
401
|
+
await Promise.all(children.map((child) => _walk(child, visitor, recurse, limit, node)))
|
|
372
402
|
}
|
|
373
403
|
|
|
374
404
|
/**
|
|
375
|
-
*
|
|
405
|
+
* Synchronous depth-first transform. Each visitor callback can return a
|
|
406
|
+
* replacement node. Returning `undefined` keeps the original.
|
|
376
407
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
408
|
+
* The original tree is never mutated, a new tree is returned. Pass
|
|
409
|
+
* `depth: 'shallow'` to skip recursion into children.
|
|
379
410
|
*
|
|
380
|
-
* @example
|
|
411
|
+
* @example Prefix every operationId
|
|
381
412
|
* ```ts
|
|
382
413
|
* const next = transform(root, {
|
|
383
414
|
* operation(node) {
|
|
@@ -386,10 +417,12 @@ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit:
|
|
|
386
417
|
* })
|
|
387
418
|
* ```
|
|
388
419
|
*
|
|
389
|
-
* @example
|
|
420
|
+
* @example Replace only the root node
|
|
390
421
|
* ```ts
|
|
391
|
-
*
|
|
392
|
-
*
|
|
422
|
+
* const next = transform(root, {
|
|
423
|
+
* depth: 'shallow',
|
|
424
|
+
* input: (node) => ({ ...node, meta: { ...node.meta, title: 'Rewritten' } }),
|
|
425
|
+
* })
|
|
393
426
|
* ```
|
|
394
427
|
*/
|
|
395
428
|
export function transform(node: InputNode, options: TransformOptions): InputNode
|
|
@@ -404,189 +437,94 @@ export function transform(node: Node, options: TransformOptions): Node {
|
|
|
404
437
|
const { depth, parent, ...visitor } = options
|
|
405
438
|
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
|
|
406
439
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
let input = node
|
|
410
|
-
const replaced = visitor.input?.(input, {
|
|
411
|
-
parent: parent as ParentOf<InputNode>,
|
|
412
|
-
})
|
|
413
|
-
if (replaced) input = replaced
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
...input,
|
|
417
|
-
schemas: input.schemas.map((s) => transform(s, { ...options, parent: input })),
|
|
418
|
-
operations: input.operations.map((op) => transform(op, { ...options, parent: input })),
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
case 'Output': {
|
|
422
|
-
let output = node
|
|
423
|
-
const replaced = visitor.output?.(output, {
|
|
424
|
-
parent: parent as ParentOf<OutputNode>,
|
|
425
|
-
})
|
|
426
|
-
if (replaced) output = replaced
|
|
440
|
+
const visited = applyVisitor<Node>(node, visitor, parent) ?? node
|
|
441
|
+
const rebuilt = transformChildren(visited, options, recurse)
|
|
427
442
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const replaced = visitor.operation?.(op, {
|
|
433
|
-
parent: parent as ParentOf<OperationNode>,
|
|
434
|
-
})
|
|
435
|
-
if (replaced) op = replaced
|
|
436
|
-
|
|
437
|
-
return {
|
|
438
|
-
...op,
|
|
439
|
-
parameters: op.parameters.map((p) => transform(p, { ...options, parent: op })),
|
|
440
|
-
requestBody: op.requestBody
|
|
441
|
-
? {
|
|
442
|
-
...op.requestBody,
|
|
443
|
-
content: op.requestBody.content?.map((c) => ({
|
|
444
|
-
...c,
|
|
445
|
-
schema: c.schema ? transform(c.schema, { ...options, parent: op }) : undefined,
|
|
446
|
-
})),
|
|
447
|
-
}
|
|
448
|
-
: undefined,
|
|
449
|
-
responses: op.responses.map((r) => transform(r, { ...options, parent: op })),
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
case 'Schema': {
|
|
453
|
-
let schema = node
|
|
454
|
-
const replaced = visitor.schema?.(schema, {
|
|
455
|
-
parent: parent as ParentOf<SchemaNode>,
|
|
456
|
-
})
|
|
457
|
-
if (replaced) schema = replaced
|
|
458
|
-
|
|
459
|
-
const childOptions = { ...options, parent: schema }
|
|
460
|
-
|
|
461
|
-
return {
|
|
462
|
-
...schema,
|
|
463
|
-
...('properties' in schema && recurse
|
|
464
|
-
? {
|
|
465
|
-
properties: schema.properties.map((p) => transform(p, childOptions)),
|
|
466
|
-
}
|
|
467
|
-
: {}),
|
|
468
|
-
...('items' in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {}),
|
|
469
|
-
...('members' in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {}),
|
|
470
|
-
...('additionalProperties' in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true
|
|
471
|
-
? {
|
|
472
|
-
additionalProperties: transform(schema.additionalProperties, childOptions),
|
|
473
|
-
}
|
|
474
|
-
: {}),
|
|
475
|
-
} as SchemaNode
|
|
476
|
-
}
|
|
477
|
-
case 'Property': {
|
|
478
|
-
let prop = node
|
|
479
|
-
const replaced = visitor.property?.(prop, {
|
|
480
|
-
parent: parent as ParentOf<PropertyNode>,
|
|
481
|
-
})
|
|
482
|
-
if (replaced) prop = replaced
|
|
443
|
+
// Structural sharing: when the visitor and child rebuild both left this node
|
|
444
|
+
// untouched, return the original reference so callers can detect "nothing
|
|
445
|
+
// changed" by identity and ancestors can avoid reallocating.
|
|
446
|
+
if (rebuilt === node) return node
|
|
483
447
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
})
|
|
488
|
-
}
|
|
489
|
-
case 'Parameter': {
|
|
490
|
-
let param = node
|
|
491
|
-
const replaced = visitor.parameter?.(param, {
|
|
492
|
-
parent: parent as ParentOf<ParameterNode>,
|
|
493
|
-
})
|
|
494
|
-
if (replaced) param = replaced
|
|
448
|
+
const rebuild = nodeRebuilders[rebuilt.kind]
|
|
449
|
+
return rebuild ? rebuild(rebuilt) : rebuilt
|
|
450
|
+
}
|
|
495
451
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Immutably rebuilds a node's children using {@link VISITOR_KEYS}, transforming
|
|
454
|
+
* each child node and leaving non-node values (e.g. `additionalProperties: true`) intact.
|
|
455
|
+
* `Schema` children are skipped in shallow mode.
|
|
456
|
+
*/
|
|
457
|
+
function transformChildren(node: Node, options: TransformOptions, recurse: boolean): Node {
|
|
458
|
+
if (node.kind === 'Schema' && !recurse) return node
|
|
459
|
+
|
|
460
|
+
const keys = visitorKeysByKind[node.kind]
|
|
461
|
+
if (!keys) return node
|
|
462
|
+
|
|
463
|
+
const record = node as unknown as Record<string, unknown>
|
|
464
|
+
const childOptions = { ...options, parent: node }
|
|
465
|
+
let updates: Record<string, unknown> | undefined
|
|
466
|
+
|
|
467
|
+
for (const key of keys) {
|
|
468
|
+
if (!(key in record)) continue
|
|
469
|
+
const value = record[key]
|
|
470
|
+
if (Array.isArray(value)) {
|
|
471
|
+
let changed = false
|
|
472
|
+
const mapped = value.map((item) => {
|
|
473
|
+
if (!isNode(item)) return item
|
|
474
|
+
const next = transform(item, childOptions)
|
|
475
|
+
if (next !== item) changed = true
|
|
476
|
+
return next
|
|
505
477
|
})
|
|
506
|
-
if (
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
schema: transform(response.schema, { ...options, parent: response }),
|
|
511
|
-
}
|
|
478
|
+
if (changed) (updates ??= {})[key] = mapped
|
|
479
|
+
} else if (isNode(value)) {
|
|
480
|
+
const next = transform(value, childOptions)
|
|
481
|
+
if (next !== value) (updates ??= {})[key] = next
|
|
512
482
|
}
|
|
513
|
-
case 'FunctionParameter':
|
|
514
|
-
case 'ParameterGroup':
|
|
515
|
-
case 'FunctionParameters':
|
|
516
|
-
case 'Type':
|
|
517
|
-
return node
|
|
518
|
-
default:
|
|
519
|
-
return node
|
|
520
483
|
}
|
|
484
|
+
|
|
485
|
+
return updates ? ({ ...node, ...updates } as Node) : node
|
|
521
486
|
}
|
|
522
487
|
/**
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
* Non-`undefined` values returned by visitor callbacks are appended to the result.
|
|
488
|
+
* Lazy depth-first collection pass. Yields every non-null value returned by
|
|
489
|
+
* the visitor callbacks. Use `collect` for the eager array form.
|
|
526
490
|
*
|
|
527
|
-
* @example
|
|
491
|
+
* @example Collect every operationId
|
|
528
492
|
* ```ts
|
|
529
|
-
* const ids =
|
|
493
|
+
* const ids: string[] = []
|
|
494
|
+
* for (const id of collectLazy<string>(root, {
|
|
530
495
|
* operation(node) {
|
|
531
496
|
* return node.operationId
|
|
532
497
|
* },
|
|
533
|
-
* })
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
* @example
|
|
537
|
-
* ```ts
|
|
538
|
-
* // Collect from only the current node
|
|
539
|
-
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
498
|
+
* })) {
|
|
499
|
+
* ids.push(id)
|
|
500
|
+
* }
|
|
540
501
|
* ```
|
|
541
502
|
*/
|
|
542
|
-
export function
|
|
503
|
+
export function* collectLazy<T>(node: Node, options: CollectOptions<T>): Generator<T, void, undefined> {
|
|
543
504
|
const { depth, parent, ...visitor } = options
|
|
544
505
|
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
switch (node.kind) {
|
|
549
|
-
case 'Input':
|
|
550
|
-
v = visitor.input?.(node, { parent: parent as ParentOf<InputNode> })
|
|
551
|
-
break
|
|
552
|
-
case 'Output':
|
|
553
|
-
v = visitor.output?.(node, { parent: parent as ParentOf<OutputNode> })
|
|
554
|
-
break
|
|
555
|
-
case 'Operation':
|
|
556
|
-
v = visitor.operation?.(node, {
|
|
557
|
-
parent: parent as ParentOf<OperationNode>,
|
|
558
|
-
})
|
|
559
|
-
break
|
|
560
|
-
case 'Schema':
|
|
561
|
-
v = visitor.schema?.(node, { parent: parent as ParentOf<SchemaNode> })
|
|
562
|
-
break
|
|
563
|
-
case 'Property':
|
|
564
|
-
v = visitor.property?.(node, {
|
|
565
|
-
parent: parent as ParentOf<PropertyNode>,
|
|
566
|
-
})
|
|
567
|
-
break
|
|
568
|
-
case 'Parameter':
|
|
569
|
-
v = visitor.parameter?.(node, {
|
|
570
|
-
parent: parent as ParentOf<ParameterNode>,
|
|
571
|
-
})
|
|
572
|
-
break
|
|
573
|
-
case 'Response':
|
|
574
|
-
v = visitor.response?.(node, {
|
|
575
|
-
parent: parent as ParentOf<ResponseNode>,
|
|
576
|
-
})
|
|
577
|
-
break
|
|
578
|
-
case 'FunctionParameter':
|
|
579
|
-
case 'ParameterGroup':
|
|
580
|
-
case 'FunctionParameters':
|
|
581
|
-
break
|
|
582
|
-
}
|
|
583
|
-
if (v !== undefined) results.push(v)
|
|
506
|
+
|
|
507
|
+
const v = applyVisitor<T>(node, visitor, parent)
|
|
508
|
+
if (v != null) yield v
|
|
584
509
|
|
|
585
510
|
for (const child of getChildren(node, recurse)) {
|
|
586
|
-
|
|
587
|
-
results.push(item)
|
|
588
|
-
}
|
|
511
|
+
yield* collectLazy(child, { ...options, parent: node })
|
|
589
512
|
}
|
|
513
|
+
}
|
|
590
514
|
|
|
591
|
-
|
|
515
|
+
/**
|
|
516
|
+
* Eager depth-first collection pass. Gathers every non-null value the visitor
|
|
517
|
+
* callbacks return into an array.
|
|
518
|
+
*
|
|
519
|
+
* @example Collect every operationId
|
|
520
|
+
* ```ts
|
|
521
|
+
* const ids = collect<string>(root, {
|
|
522
|
+
* operation(node) {
|
|
523
|
+
* return node.operationId
|
|
524
|
+
* },
|
|
525
|
+
* })
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
export function collect<T>(node: Node, options: CollectOptions<T>): Array<T> {
|
|
529
|
+
return Array.from(collectLazy(node, options))
|
|
592
530
|
}
|