@kubb/ast 5.0.0-beta.57 → 5.0.0-beta.59
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/README.md +21 -7
- package/dist/casing-BE2R1RXg.cjs +88 -0
- package/dist/casing-BE2R1RXg.cjs.map +1 -0
- package/dist/chunk-CNktS9qV.js +17 -0
- package/dist/extractStringsFromNodes-WMYJ8nQL.d.ts +82 -0
- package/dist/factory-BmcGBdeg.cjs +1251 -0
- package/dist/factory-BmcGBdeg.cjs.map +1 -0
- package/dist/factory-Du7nEP4B.js +282 -0
- package/dist/factory-Du7nEP4B.js.map +1 -0
- package/dist/factory.cjs +28 -0
- package/dist/factory.d.ts +62 -0
- package/dist/factory.js +3 -0
- package/dist/{types-C5aVnRE1.d.ts → index-BzjwdK2M.d.ts} +94 -1146
- package/dist/index.cjs +220 -1481
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +96 -58
- package/dist/index.js +182 -1920
- 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/types-olVl9v5p.d.ts +764 -0
- package/dist/types.d.ts +5 -2
- package/dist/utils-BCtRXfhI.cjs +275 -0
- package/dist/utils-BCtRXfhI.cjs.map +1 -0
- package/dist/utils-SdZU0F3H.js +1336 -0
- package/dist/utils-SdZU0F3H.js.map +1 -0
- package/dist/utils.cjs +129 -13
- package/dist/utils.d.ts +127 -76
- package/dist/utils.js +3 -2
- package/package.json +5 -1
- package/src/constants.ts +8 -14
- package/src/dedupe.ts +8 -0
- package/src/factory.ts +18 -1
- package/src/guards.ts +1 -1
- package/src/index.ts +7 -50
- package/src/node.ts +1 -1
- package/src/nodes/base.ts +0 -10
- package/src/nodes/code.ts +29 -47
- package/src/nodes/content.ts +2 -2
- package/src/nodes/file.ts +28 -23
- package/src/nodes/function.ts +2 -17
- package/src/nodes/index.ts +1 -1
- package/src/nodes/input.ts +20 -19
- package/src/nodes/operation.ts +1 -1
- package/src/nodes/parameter.ts +0 -3
- package/src/nodes/property.ts +0 -3
- package/src/nodes/requestBody.ts +3 -6
- package/src/nodes/schema.ts +3 -2
- package/src/printer.ts +4 -4
- package/src/registry.ts +31 -26
- package/src/signature.ts +3 -3
- package/src/transformers.ts +28 -1
- package/src/types.ts +2 -53
- 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 +8 -296
- package/src/utils/operationParams.ts +353 -0
- package/src/utils/refs.ts +112 -0
- package/src/utils/schemaGraph.ts +169 -0
- package/src/utils/strings.ts +139 -0
- package/src/visitor.ts +43 -19
- package/dist/chunk-C0LytTxp.js +0 -8
- package/dist/utils-0p8ZO287.js +0 -570
- package/dist/utils-0p8ZO287.js.map +0 -1
- package/dist/utils-cdQ6Pzyi.cjs +0 -726
- package/dist/utils-cdQ6Pzyi.cjs.map +0 -1
- package/src/utils/ast.ts +0 -879
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
3
|
+
* Returns the string unchanged when no balanced quote pair is found.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* trimQuotes('"hello"') // 'hello'
|
|
8
|
+
* trimQuotes('hello') // 'hello'
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function trimQuotes(text: string): string {
|
|
12
|
+
if (text.length >= 2) {
|
|
13
|
+
const first = text[0]
|
|
14
|
+
const last = text[text.length - 1]
|
|
15
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'") || (first === '`' && last === '`')) {
|
|
16
|
+
return text.slice(1, -1)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return text
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
|
|
24
|
+
*
|
|
25
|
+
* Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
|
|
26
|
+
* code matches the repo style without a formatter.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* stringify('hello') // "'hello'"
|
|
31
|
+
* stringify('"hello"') // "'hello'"
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function stringify(value: string | number | boolean | undefined): string {
|
|
35
|
+
if (value === undefined || value === null) return "''"
|
|
36
|
+
const json = JSON.stringify(trimQuotes(value.toString()))
|
|
37
|
+
const inner = json.slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")
|
|
38
|
+
return `'${inner}'`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
|
|
43
|
+
* and the Unicode line terminators U+2028 and U+2029.
|
|
44
|
+
*
|
|
45
|
+
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function jsStringEscape(input: unknown): string {
|
|
53
|
+
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
|
|
54
|
+
switch (character) {
|
|
55
|
+
case '"':
|
|
56
|
+
case "'":
|
|
57
|
+
case '\\':
|
|
58
|
+
return `\\${character}`
|
|
59
|
+
case '\n':
|
|
60
|
+
return '\\n'
|
|
61
|
+
case '\r':
|
|
62
|
+
return '\\r'
|
|
63
|
+
case '\u2028':
|
|
64
|
+
return '\\u2028'
|
|
65
|
+
case '\u2029':
|
|
66
|
+
return '\\u2029'
|
|
67
|
+
default:
|
|
68
|
+
return ''
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
75
|
+
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
76
|
+
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
|
|
81
|
+
* toRegExpString('^(?im)foo', null) // '/^foo/im'
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function toRegExpString(text: string, func: string | null = 'RegExp'): string {
|
|
85
|
+
const raw = trimQuotes(text)
|
|
86
|
+
|
|
87
|
+
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i)
|
|
88
|
+
const replacementTarget = match?.[1] ?? ''
|
|
89
|
+
const matchedFlags = match?.[2]
|
|
90
|
+
const cleaned = raw
|
|
91
|
+
.replace(/^\\?\//, '')
|
|
92
|
+
.replace(/\\?\/$/, '')
|
|
93
|
+
.replace(replacementTarget, '')
|
|
94
|
+
|
|
95
|
+
const { source, flags } = new RegExp(cleaned, matchedFlags)
|
|
96
|
+
|
|
97
|
+
if (func === null) return `/${source}/${flags}`
|
|
98
|
+
|
|
99
|
+
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ''})`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Renders a plain object as multi-line `key: value` source for embedding in generated code. Nested
|
|
104
|
+
* objects recurse with fixed indentation, so the result drops straight into an object literal
|
|
105
|
+
* without re-parsing.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* stringifyObject({ foo: 'bar', nested: { a: 1 } })
|
|
110
|
+
* // 'foo: bar,\nnested: {\n a: 1\n }'
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function stringifyObject(value: Record<string, unknown>): string {
|
|
114
|
+
const items = Object.entries(value)
|
|
115
|
+
.map(([key, val]) => {
|
|
116
|
+
if (val !== null && typeof val === 'object') {
|
|
117
|
+
return `${key}: {\n ${stringifyObject(val as Record<string, unknown>)}\n }`
|
|
118
|
+
}
|
|
119
|
+
return `${key}: ${val}`
|
|
120
|
+
})
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
return items.join(',\n')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Renders a dotted path or string array as an optional-chaining accessor expression rooted at
|
|
127
|
+
* `accessor`. Returns `null` for an empty path.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* getNestedAccessor('pagination.next.id', 'lastPage')
|
|
132
|
+
* // "lastPage?.['pagination']?.['next']?.['id']"
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export function getNestedAccessor(param: string | Array<string>, accessor: string): string | null {
|
|
136
|
+
const parts = Array.isArray(param) ? param : param.split('.')
|
|
137
|
+
if (parts.length === 0 || (parts.length === 1 && parts[0] === '')) return null
|
|
138
|
+
return `${accessor}?.['${`${parts.join("']?.['")}']`}`
|
|
139
|
+
}
|
package/src/visitor.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { VisitorDepth } from './constants.ts'
|
|
2
2
|
import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
|
|
3
|
-
import {
|
|
3
|
+
import type { NodeDef } from './node.ts'
|
|
4
4
|
import type {
|
|
5
5
|
ContentNode,
|
|
6
6
|
InputNode,
|
|
7
7
|
Node,
|
|
8
|
+
NodeKind,
|
|
8
9
|
OperationNode,
|
|
9
10
|
OutputNode,
|
|
10
11
|
ParameterNode,
|
|
@@ -13,6 +14,31 @@ import type {
|
|
|
13
14
|
ResponseNode,
|
|
14
15
|
SchemaNode,
|
|
15
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>>
|
|
16
42
|
|
|
17
43
|
/**
|
|
18
44
|
* Creates a small async concurrency limiter.
|
|
@@ -76,8 +102,7 @@ type ParentNodeMap = [
|
|
|
76
102
|
/**
|
|
77
103
|
* Resolves the parent node type for a given AST node type.
|
|
78
104
|
*
|
|
79
|
-
*
|
|
80
|
-
* for each callback.
|
|
105
|
+
* Visitor context relies on this so `ctx.parent` is typed for each callback.
|
|
81
106
|
*
|
|
82
107
|
* @example
|
|
83
108
|
* ```ts
|
|
@@ -133,8 +158,7 @@ export type VisitorContext<T extends Node = Node> = {
|
|
|
133
158
|
* to leave it untouched.
|
|
134
159
|
*
|
|
135
160
|
* Plugins typically expose `transformer` so users can supply a `Visitor` that
|
|
136
|
-
* rewrites
|
|
137
|
-
* before printing.
|
|
161
|
+
* rewrites the AST before printing.
|
|
138
162
|
*
|
|
139
163
|
* @example Prefix every operationId
|
|
140
164
|
* ```ts
|
|
@@ -165,7 +189,7 @@ export type Visitor = {
|
|
|
165
189
|
}
|
|
166
190
|
|
|
167
191
|
/**
|
|
168
|
-
*
|
|
192
|
+
* A visitor callback result that may be sync or async.
|
|
169
193
|
*/
|
|
170
194
|
type MaybePromise<T> = T | Promise<T>
|
|
171
195
|
|
|
@@ -229,7 +253,7 @@ type CollectVisitor<T> = {
|
|
|
229
253
|
*/
|
|
230
254
|
export type TransformOptions = Visitor & {
|
|
231
255
|
/**
|
|
232
|
-
* Traversal depth
|
|
256
|
+
* Traversal depth.
|
|
233
257
|
* @default 'deep'
|
|
234
258
|
*/
|
|
235
259
|
depth?: VisitorDepth
|
|
@@ -249,7 +273,7 @@ export type TransformOptions = Visitor & {
|
|
|
249
273
|
*/
|
|
250
274
|
export type WalkOptions = AsyncVisitor & {
|
|
251
275
|
/**
|
|
252
|
-
* Traversal depth
|
|
276
|
+
* Traversal depth.
|
|
253
277
|
* @default 'deep'
|
|
254
278
|
*/
|
|
255
279
|
depth?: VisitorDepth
|
|
@@ -270,7 +294,7 @@ export type WalkOptions = AsyncVisitor & {
|
|
|
270
294
|
*/
|
|
271
295
|
export type CollectOptions<T> = CollectVisitor<T> & {
|
|
272
296
|
/**
|
|
273
|
-
* Traversal depth
|
|
297
|
+
* Traversal depth.
|
|
274
298
|
* @default 'deep'
|
|
275
299
|
*/
|
|
276
300
|
depth?: VisitorDepth
|
|
@@ -318,9 +342,9 @@ function* getChildren(node: Node, recurse: boolean): Generator<Node, void, undef
|
|
|
318
342
|
}
|
|
319
343
|
|
|
320
344
|
/**
|
|
321
|
-
*
|
|
322
|
-
* context.
|
|
323
|
-
*
|
|
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.
|
|
324
348
|
*
|
|
325
349
|
* Shared by `walk`, `transform`, and `collectLazy` so node-kind dispatch lives
|
|
326
350
|
* in one place. `TResult` is the caller's expected return: the same node type
|
|
@@ -369,7 +393,7 @@ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit:
|
|
|
369
393
|
|
|
370
394
|
// Visit siblings concurrently and let the shared `limit` cap how many callbacks
|
|
371
395
|
// run at once. Awaiting each child sequentially here would serialize the whole
|
|
372
|
-
// traversal and make `concurrency` inert
|
|
396
|
+
// traversal and make `concurrency` inert. Every visitor callback would run one
|
|
373
397
|
// at a time regardless of the limit.
|
|
374
398
|
const children = Array.from(getChildren(node, recurse))
|
|
375
399
|
if (children.length === 0) return
|
|
@@ -378,11 +402,11 @@ async function _walk(node: Node, visitor: AsyncVisitor, recurse: boolean, limit:
|
|
|
378
402
|
}
|
|
379
403
|
|
|
380
404
|
/**
|
|
381
|
-
* Synchronous depth-first transform. Each visitor callback
|
|
382
|
-
*
|
|
405
|
+
* Synchronous depth-first transform. Each visitor callback can return a
|
|
406
|
+
* replacement node. Returning `undefined` keeps the original.
|
|
383
407
|
*
|
|
384
|
-
* The
|
|
385
|
-
*
|
|
408
|
+
* The original tree is never mutated, a new tree is returned. Pass
|
|
409
|
+
* `depth: 'shallow'` to skip recursion into children.
|
|
386
410
|
*
|
|
387
411
|
* @example Prefix every operationId
|
|
388
412
|
* ```ts
|
|
@@ -489,8 +513,8 @@ export function* collectLazy<T>(node: Node, options: CollectOptions<T>): Generat
|
|
|
489
513
|
}
|
|
490
514
|
|
|
491
515
|
/**
|
|
492
|
-
* Eager depth-first collection pass.
|
|
493
|
-
*
|
|
516
|
+
* Eager depth-first collection pass. Gathers every non-null value the visitor
|
|
517
|
+
* callbacks return into an array.
|
|
494
518
|
*
|
|
495
519
|
* @example Collect every operationId
|
|
496
520
|
* ```ts
|