@kubb/ast 5.0.0-beta.56 → 5.0.0-beta.58
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 +13 -9
- package/dist/{types-BL7RpQAE.d.ts → ast-ClnJg9BN.d.ts} +1630 -2443
- package/dist/chunk-CNktS9qV.js +17 -0
- package/dist/extractStringsFromNodes-Bn9cOos9.d.ts +14 -0
- package/dist/factory-C5gHvtLU.js +138 -0
- package/dist/factory-C5gHvtLU.js.map +1 -0
- package/dist/factory-JN-Ylfl6.cjs +155 -0
- package/dist/factory-JN-Ylfl6.cjs.map +1 -0
- package/dist/factory.cjs +31 -0
- package/dist/factory.d.ts +62 -0
- package/dist/factory.js +3 -0
- package/dist/index.cjs +56 -1751
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -47
- package/dist/index.js +7 -1697
- package/dist/index.js.map +1 -1
- package/dist/types-CB2oY8Dw.d.ts +769 -0
- package/dist/types.d.ts +4 -2
- package/dist/utils-C8bWAzhv.cjs +2696 -0
- package/dist/utils-C8bWAzhv.cjs.map +1 -0
- package/dist/utils-DN4XLVqz.js +2099 -0
- package/dist/utils-DN4XLVqz.js.map +1 -0
- package/dist/utils.cjs +12 -1
- package/dist/utils.d.ts +21 -2
- package/dist/utils.js +2 -2
- package/package.json +5 -1
- package/src/dedupe.ts +1 -1
- package/src/factory.ts +22 -764
- package/src/guards.ts +1 -53
- package/src/index.ts +20 -39
- package/src/mocks.ts +6 -1
- package/src/node.ts +128 -0
- package/src/nodes/base.ts +3 -12
- package/src/nodes/code.ts +115 -0
- package/src/nodes/content.ts +19 -0
- package/src/nodes/file.ts +54 -0
- package/src/nodes/function.ts +222 -147
- package/src/nodes/index.ts +11 -3
- package/src/nodes/input.ts +37 -0
- package/src/nodes/operation.ts +59 -1
- package/src/nodes/output.ts +23 -0
- package/src/nodes/parameter.ts +33 -0
- package/src/nodes/property.ts +36 -0
- package/src/nodes/requestBody.ts +23 -1
- package/src/nodes/response.ts +39 -1
- package/src/nodes/schema.ts +72 -0
- package/src/printer.ts +3 -3
- package/src/registry.ts +70 -0
- package/src/transformers.ts +2 -2
- package/src/types.ts +6 -4
- package/src/utils/ast.ts +103 -243
- package/src/utils/extractStringsFromNodes.ts +34 -0
- package/src/utils/index.ts +44 -0
- package/src/visitor.ts +3 -47
- 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
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { camelCase, isValidVarName, memoize } from '@internals/utils'
|
|
2
2
|
|
|
3
|
-
import { createFunctionParameter, createFunctionParameters, createParameterGroup, createParamsType, createProperty, createSchema } from '../factory.ts'
|
|
4
3
|
import { narrowSchema } from '../guards.ts'
|
|
4
|
+
import { createFunctionParameter, createFunctionParameters, createIndexedAccessType, createTypeLiteral } from '../nodes/function.ts'
|
|
5
|
+
import { createProperty } from '../nodes/property.ts'
|
|
6
|
+
import { createSchema } from '../nodes/schema.ts'
|
|
5
7
|
import type {
|
|
6
|
-
CodeNode,
|
|
7
8
|
ExportNode,
|
|
8
9
|
FunctionParameterNode,
|
|
9
10
|
FunctionParametersNode,
|
|
10
11
|
ImportNode,
|
|
11
12
|
OperationNode,
|
|
12
|
-
ParameterGroupNode,
|
|
13
13
|
ParameterNode,
|
|
14
|
-
ParamsTypeNode,
|
|
15
14
|
SchemaNode,
|
|
16
15
|
SourceNode,
|
|
16
|
+
TypeExpression,
|
|
17
|
+
TypeLiteralNode,
|
|
17
18
|
} from '../nodes/index.ts'
|
|
18
19
|
import type { SchemaType } from '../nodes/schema.ts'
|
|
19
|
-
import { extractRefName } from './index.ts'
|
|
20
|
+
import { extractRefName, extractStringsFromNodes, resolveGroupType } from './index.ts'
|
|
20
21
|
import { collect, collectLazy } from '../visitor.ts'
|
|
21
22
|
|
|
22
23
|
const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'] as const)
|
|
@@ -118,17 +119,26 @@ export function createDiscriminantNode({ propertyName, value }: { propertyName:
|
|
|
118
119
|
/**
|
|
119
120
|
* Named type for a group of parameters (query or header) emitted as a single typed parameter.
|
|
120
121
|
*/
|
|
121
|
-
type ParamGroupType = {
|
|
122
|
+
export type ParamGroupType = {
|
|
122
123
|
/**
|
|
123
|
-
*
|
|
124
|
+
* Type expression for the group, a plain group-name reference.
|
|
124
125
|
*/
|
|
125
|
-
type:
|
|
126
|
+
type: TypeExpression
|
|
126
127
|
/**
|
|
127
128
|
* Whether the parameter group is optional.
|
|
128
129
|
*/
|
|
129
130
|
optional: boolean
|
|
130
131
|
}
|
|
131
132
|
|
|
133
|
+
/**
|
|
134
|
+
* A single member of a destructured parameter group, fed to `createFunctionParameter({ properties })`.
|
|
135
|
+
*/
|
|
136
|
+
type GroupProperty = {
|
|
137
|
+
name: string
|
|
138
|
+
type: TypeExpression
|
|
139
|
+
optional?: boolean
|
|
140
|
+
}
|
|
141
|
+
|
|
132
142
|
/**
|
|
133
143
|
* Resolver interface for {@link createOperationParams}.
|
|
134
144
|
*
|
|
@@ -215,7 +225,7 @@ export type CreateOperationParamsOptions = {
|
|
|
215
225
|
* extraParams: [createFunctionParameter({ name: 'options', type: 'Partial<RequestOptions>', default: '{}' })]
|
|
216
226
|
* ```
|
|
217
227
|
*/
|
|
218
|
-
extraParams?: Array<FunctionParameterNode
|
|
228
|
+
extraParams?: Array<FunctionParameterNode>
|
|
219
229
|
/**
|
|
220
230
|
* Override the default parameter names used for body, query, header, and rest-path groups.
|
|
221
231
|
*
|
|
@@ -255,7 +265,14 @@ export type CreateOperationParamsOptions = {
|
|
|
255
265
|
typeWrapper?: (type: string) => string
|
|
256
266
|
}
|
|
257
267
|
|
|
258
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Resolves the {@link TypeExpression} for an individual parameter.
|
|
270
|
+
*
|
|
271
|
+
* Without a resolver, falls back to the schema primitive (a plain type-name string).
|
|
272
|
+
* When the parameter belongs to a named group, emits an {@link IndexedAccessTypeNode}
|
|
273
|
+
* (`GroupParams['petId']`); otherwise the resolved individual name as a plain string.
|
|
274
|
+
*/
|
|
275
|
+
export function resolveParamType({
|
|
259
276
|
node,
|
|
260
277
|
param,
|
|
261
278
|
resolver,
|
|
@@ -263,12 +280,9 @@ function resolveParamsType({
|
|
|
263
280
|
node: OperationNode
|
|
264
281
|
param: ParameterNode
|
|
265
282
|
resolver: OperationParamsResolver | undefined
|
|
266
|
-
}):
|
|
283
|
+
}): TypeExpression {
|
|
267
284
|
if (!resolver) {
|
|
268
|
-
return
|
|
269
|
-
variant: 'reference',
|
|
270
|
-
name: param.schema.primitive ?? 'unknown',
|
|
271
|
-
})
|
|
285
|
+
return param.schema.primitive ?? 'unknown'
|
|
272
286
|
}
|
|
273
287
|
|
|
274
288
|
const individualName = resolver.resolveParamName(node, param)
|
|
@@ -284,23 +298,19 @@ function resolveParamsType({
|
|
|
284
298
|
const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : undefined
|
|
285
299
|
|
|
286
300
|
if (groupName && groupName !== individualName) {
|
|
287
|
-
return
|
|
288
|
-
variant: 'member',
|
|
289
|
-
base: groupName,
|
|
290
|
-
key: param.name,
|
|
291
|
-
})
|
|
301
|
+
return createIndexedAccessType({ objectType: groupName, indexType: param.name })
|
|
292
302
|
}
|
|
293
303
|
|
|
294
|
-
return
|
|
304
|
+
return individualName
|
|
295
305
|
}
|
|
296
306
|
|
|
297
307
|
/**
|
|
298
308
|
* Converts an `OperationNode` into function parameters for code generation.
|
|
299
309
|
*
|
|
300
|
-
* Centralizes parameter grouping logic for all plugins.
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* and `
|
|
310
|
+
* Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
|
|
311
|
+
* destructured object parameter (`object`) and separate top-level parameters (`inline`), while
|
|
312
|
+
* `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
|
|
313
|
+
* name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
|
|
304
314
|
*/
|
|
305
315
|
export function createOperationParams(node: OperationNode, options: CreateOperationParamsOptions): FunctionParametersNode {
|
|
306
316
|
const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options
|
|
@@ -310,147 +320,58 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
|
|
|
310
320
|
const headersName = paramNames?.headers ?? 'headers'
|
|
311
321
|
const pathName = paramNames?.path ?? 'pathParams'
|
|
312
322
|
|
|
313
|
-
const wrapType = (type: string):
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
})
|
|
318
|
-
// Only reference-variant TypeNodes are wrapped, they hold a plain type name string that needs casing applied.
|
|
319
|
-
// Member and struct TypeNodes are pre-resolved structured expressions and are passed through unchanged.
|
|
320
|
-
const wrapTypeNode = (type: ParamsTypeNode): ParamsTypeNode => (type.kind === 'ParamsType' && type.variant === 'reference' ? wrapType(type.name) : type)
|
|
323
|
+
const wrapType = (type: string): string => (typeWrapper ? typeWrapper(type) : type)
|
|
324
|
+
// Only plain type-name references are wrapped, they need casing applied.
|
|
325
|
+
// TypeLiteral and IndexedAccessType expressions are pre-resolved and pass through unchanged.
|
|
326
|
+
const wrapTypeExpression = (type: TypeExpression): TypeExpression => (typeof type === 'string' ? wrapType(type) : type)
|
|
321
327
|
|
|
322
328
|
const casedParams = caseParams(node.parameters, paramsCasing)
|
|
323
329
|
const pathParams = casedParams.filter((p) => p.in === 'path')
|
|
324
330
|
const queryParams = casedParams.filter((p) => p.in === 'query')
|
|
325
331
|
const headerParams = casedParams.filter((p) => p.in === 'header')
|
|
326
332
|
|
|
333
|
+
const toProperty = (param: ParameterNode): GroupProperty => ({
|
|
334
|
+
name: param.name,
|
|
335
|
+
type: wrapTypeExpression(resolveParamType({ node, param, resolver })),
|
|
336
|
+
optional: !param.required,
|
|
337
|
+
})
|
|
338
|
+
const emptyObjectDefault = (props: Array<GroupProperty>): string | undefined => (props.every((p) => p.optional) ? '{}' : undefined)
|
|
339
|
+
|
|
327
340
|
const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? 'unknown') : undefined
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
params: headerParams,
|
|
342
|
-
groupMethod: resolver.resolveHeaderParamsName,
|
|
343
|
-
resolver,
|
|
344
|
-
})
|
|
345
|
-
: undefined
|
|
346
|
-
|
|
347
|
-
const params: Array<FunctionParameterNode | ParameterGroupNode> = []
|
|
341
|
+
const bodyProperty: Array<GroupProperty> = bodyType ? [{ name: dataName, type: bodyType, optional: !(node.requestBody?.required ?? false) }] : []
|
|
342
|
+
|
|
343
|
+
const trailingGroups: Array<BuildGroupArgs> = [
|
|
344
|
+
{ name: paramsName, node, params: queryParams, groupType: resolveGroupType({ node, params: queryParams, group: 'query', resolver }), resolver, wrapType },
|
|
345
|
+
{
|
|
346
|
+
name: headersName,
|
|
347
|
+
node,
|
|
348
|
+
params: headerParams,
|
|
349
|
+
groupType: resolveGroupType({ node, params: headerParams, group: 'header', resolver }),
|
|
350
|
+
resolver,
|
|
351
|
+
wrapType,
|
|
352
|
+
},
|
|
353
|
+
]
|
|
348
354
|
|
|
349
|
-
|
|
350
|
-
const children: Array<FunctionParameterNode> = [
|
|
351
|
-
...pathParams.map((p) => {
|
|
352
|
-
const type = resolveParamsType({ node, param: p, resolver })
|
|
353
|
-
return createFunctionParameter({
|
|
354
|
-
name: p.name,
|
|
355
|
-
type: wrapTypeNode(type),
|
|
356
|
-
optional: !p.required,
|
|
357
|
-
})
|
|
358
|
-
}),
|
|
359
|
-
...(bodyType
|
|
360
|
-
? [
|
|
361
|
-
createFunctionParameter({
|
|
362
|
-
name: dataName,
|
|
363
|
-
type: bodyType,
|
|
364
|
-
optional: !bodyRequired,
|
|
365
|
-
}),
|
|
366
|
-
]
|
|
367
|
-
: []),
|
|
368
|
-
...buildGroupParam({
|
|
369
|
-
name: paramsName,
|
|
370
|
-
node,
|
|
371
|
-
params: queryParams,
|
|
372
|
-
groupType: queryGroupType,
|
|
373
|
-
resolver,
|
|
374
|
-
wrapType,
|
|
375
|
-
}),
|
|
376
|
-
...buildGroupParam({
|
|
377
|
-
name: headersName,
|
|
378
|
-
node,
|
|
379
|
-
params: headerParams,
|
|
380
|
-
groupType: headerGroupType,
|
|
381
|
-
resolver,
|
|
382
|
-
wrapType,
|
|
383
|
-
}),
|
|
384
|
-
]
|
|
355
|
+
const params: Array<FunctionParameterNode> = []
|
|
385
356
|
|
|
357
|
+
if (paramsType === 'object') {
|
|
358
|
+
const children = [...pathParams.map(toProperty), ...bodyProperty, ...trailingGroups.flatMap(buildGroupProperty)]
|
|
386
359
|
if (children.length) {
|
|
387
|
-
params.push(
|
|
388
|
-
createParameterGroup({
|
|
389
|
-
properties: children,
|
|
390
|
-
default: children.every((c) => c.optional) ? '{}' : undefined,
|
|
391
|
-
}),
|
|
392
|
-
)
|
|
360
|
+
params.push(createFunctionParameter({ properties: children, default: emptyObjectDefault(children) }))
|
|
393
361
|
}
|
|
394
362
|
} else {
|
|
395
|
-
if (pathParams.length) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}),
|
|
404
|
-
)
|
|
405
|
-
} else {
|
|
406
|
-
const pathChildren = pathParams.map((p) => {
|
|
407
|
-
const type = resolveParamsType({ node, param: p, resolver })
|
|
408
|
-
return createFunctionParameter({
|
|
409
|
-
name: p.name,
|
|
410
|
-
type: wrapTypeNode(type),
|
|
411
|
-
optional: !p.required,
|
|
412
|
-
})
|
|
413
|
-
})
|
|
414
|
-
params.push(
|
|
415
|
-
createParameterGroup({
|
|
416
|
-
properties: pathChildren,
|
|
417
|
-
inline: pathParamsType === 'inline',
|
|
418
|
-
default: pathParamsDefault ?? (pathChildren.every((c) => c.optional) ? '{}' : undefined),
|
|
419
|
-
}),
|
|
420
|
-
)
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (bodyType) {
|
|
425
|
-
params.push(
|
|
426
|
-
createFunctionParameter({
|
|
427
|
-
name: dataName,
|
|
428
|
-
type: bodyType,
|
|
429
|
-
optional: !bodyRequired,
|
|
430
|
-
}),
|
|
431
|
-
)
|
|
363
|
+
if (pathParamsType === 'inlineSpread' && pathParams.length) {
|
|
364
|
+
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]!)
|
|
365
|
+
params.push(createFunctionParameter({ name: pathName, type: spreadType ? wrapType(spreadType) : undefined, rest: true }))
|
|
366
|
+
} else if (pathParamsType === 'inline') {
|
|
367
|
+
params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))))
|
|
368
|
+
} else if (pathParams.length) {
|
|
369
|
+
const pathChildren = pathParams.map(toProperty)
|
|
370
|
+
params.push(createFunctionParameter({ properties: pathChildren, default: pathParamsDefault ?? emptyObjectDefault(pathChildren) }))
|
|
432
371
|
}
|
|
433
372
|
|
|
434
|
-
params.push(
|
|
435
|
-
|
|
436
|
-
name: paramsName,
|
|
437
|
-
node,
|
|
438
|
-
params: queryParams,
|
|
439
|
-
groupType: queryGroupType,
|
|
440
|
-
resolver,
|
|
441
|
-
wrapType,
|
|
442
|
-
}),
|
|
443
|
-
)
|
|
444
|
-
params.push(
|
|
445
|
-
...buildGroupParam({
|
|
446
|
-
name: headersName,
|
|
447
|
-
node,
|
|
448
|
-
params: headerParams,
|
|
449
|
-
groupType: headerGroupType,
|
|
450
|
-
resolver,
|
|
451
|
-
wrapType,
|
|
452
|
-
}),
|
|
453
|
-
)
|
|
373
|
+
params.push(...bodyProperty.map((p) => createFunctionParameter(p)))
|
|
374
|
+
params.push(...trailingGroups.flatMap(buildGroupParam))
|
|
454
375
|
}
|
|
455
376
|
|
|
456
377
|
params.push(...extraParams)
|
|
@@ -459,80 +380,53 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
|
|
|
459
380
|
}
|
|
460
381
|
|
|
461
382
|
/**
|
|
462
|
-
*
|
|
463
|
-
* Returns an empty array when there are no params to emit.
|
|
464
|
-
*
|
|
465
|
-
* If a pre-resolved `groupType` is provided it emits `name: GroupType`.
|
|
466
|
-
* Otherwise, it builds an inline struct from the individual params.
|
|
383
|
+
* Shared arguments for building a query or header parameter group.
|
|
467
384
|
*/
|
|
468
|
-
|
|
469
|
-
name,
|
|
470
|
-
node,
|
|
471
|
-
params,
|
|
472
|
-
groupType,
|
|
473
|
-
resolver,
|
|
474
|
-
wrapType,
|
|
475
|
-
}: {
|
|
385
|
+
export type BuildGroupArgs = {
|
|
476
386
|
name: string
|
|
477
387
|
node: OperationNode
|
|
478
388
|
params: Array<ParameterNode>
|
|
479
|
-
groupType: ParamGroupType | null
|
|
389
|
+
groupType: ParamGroupType | null
|
|
480
390
|
resolver: OperationParamsResolver | undefined
|
|
481
|
-
wrapType: (type: string) =>
|
|
482
|
-
}
|
|
391
|
+
wrapType: (type: string) => string
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Builds the property descriptor for a query or header group.
|
|
396
|
+
* Returns an empty array when there are no params to emit.
|
|
397
|
+
*
|
|
398
|
+
* A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
|
|
399
|
+
* {@link TypeLiteralNode} from the individual params.
|
|
400
|
+
*/
|
|
401
|
+
function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }: BuildGroupArgs): Array<GroupProperty> {
|
|
483
402
|
if (groupType) {
|
|
484
|
-
const type =
|
|
485
|
-
return [
|
|
403
|
+
const type = typeof groupType.type === 'string' ? wrapType(groupType.type) : groupType.type
|
|
404
|
+
return [{ name, type, optional: groupType.optional }]
|
|
486
405
|
}
|
|
487
406
|
if (params.length) {
|
|
488
|
-
return [
|
|
489
|
-
createFunctionParameter({
|
|
490
|
-
name,
|
|
491
|
-
type: toStructType({ node, params, resolver }),
|
|
492
|
-
optional: params.every((p) => !p.required),
|
|
493
|
-
}),
|
|
494
|
-
]
|
|
407
|
+
return [{ name, type: buildTypeLiteral({ node, params, resolver }), optional: params.every((p) => !p.required) }]
|
|
495
408
|
}
|
|
496
409
|
return []
|
|
497
410
|
}
|
|
498
411
|
|
|
499
412
|
/**
|
|
500
|
-
*
|
|
501
|
-
* Returns
|
|
413
|
+
* Builds a single {@link FunctionParameterNode} for a query or header group.
|
|
414
|
+
* Returns an empty array when there are no params to emit.
|
|
415
|
+
*
|
|
416
|
+
* A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
|
|
417
|
+
* {@link TypeLiteralNode} from the individual params.
|
|
502
418
|
*/
|
|
503
|
-
function
|
|
504
|
-
|
|
505
|
-
params,
|
|
506
|
-
groupMethod,
|
|
507
|
-
resolver,
|
|
508
|
-
}: {
|
|
509
|
-
node: OperationNode
|
|
510
|
-
params: Array<ParameterNode>
|
|
511
|
-
groupMethod: (_node: OperationNode, _param: ParameterNode) => string
|
|
512
|
-
resolver: OperationParamsResolver
|
|
513
|
-
}): ParamGroupType | null {
|
|
514
|
-
if (!params.length) {
|
|
515
|
-
return null
|
|
516
|
-
}
|
|
517
|
-
const firstParam = params[0]!
|
|
518
|
-
const groupName = groupMethod.call(resolver, node, firstParam)
|
|
519
|
-
if (groupName === resolver.resolveParamName(node, firstParam)) {
|
|
520
|
-
return null
|
|
521
|
-
}
|
|
522
|
-
const allOptional = params.every((p) => !p.required)
|
|
523
|
-
return {
|
|
524
|
-
type: createParamsType({ variant: 'reference', name: groupName }),
|
|
525
|
-
optional: allOptional,
|
|
526
|
-
}
|
|
419
|
+
export function buildGroupParam(args: BuildGroupArgs): Array<FunctionParameterNode> {
|
|
420
|
+
return buildGroupProperty(args).map((p) => createFunctionParameter(p))
|
|
527
421
|
}
|
|
528
422
|
|
|
529
423
|
/**
|
|
530
|
-
* Builds a {@link
|
|
424
|
+
* Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
|
|
531
425
|
*
|
|
532
426
|
* Used when query or header parameters have no dedicated group type name.
|
|
533
427
|
* Each language printer renders this appropriately (TypeScript: `{ petId: string; name?: string }`).
|
|
534
428
|
*/
|
|
535
|
-
function
|
|
429
|
+
export function buildTypeLiteral({
|
|
536
430
|
node,
|
|
537
431
|
params,
|
|
538
432
|
resolver,
|
|
@@ -540,13 +434,12 @@ function toStructType({
|
|
|
540
434
|
node: OperationNode
|
|
541
435
|
params: Array<ParameterNode>
|
|
542
436
|
resolver: OperationParamsResolver | undefined
|
|
543
|
-
}):
|
|
544
|
-
return
|
|
545
|
-
|
|
546
|
-
properties: params.map((p) => ({
|
|
437
|
+
}): TypeLiteralNode {
|
|
438
|
+
return createTypeLiteral({
|
|
439
|
+
members: params.map((p) => ({
|
|
547
440
|
name: p.name,
|
|
441
|
+
type: resolveParamType({ node, param: p, resolver }),
|
|
548
442
|
optional: !p.required,
|
|
549
|
-
type: resolveParamsType({ node, param: p, resolver }),
|
|
550
443
|
})),
|
|
551
444
|
})
|
|
552
445
|
}
|
|
@@ -728,39 +621,6 @@ export function combineImports(imports: Array<ImportNode>, exports: Array<Export
|
|
|
728
621
|
return result
|
|
729
622
|
}
|
|
730
623
|
|
|
731
|
-
/**
|
|
732
|
-
* Extracts all string content from a `CodeNode` tree recursively.
|
|
733
|
-
*
|
|
734
|
-
* Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
|
|
735
|
-
* and nested node content. Used internally to build the full source string for import filtering.
|
|
736
|
-
*/
|
|
737
|
-
export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): string {
|
|
738
|
-
if (!nodes?.length) return ''
|
|
739
|
-
return nodes
|
|
740
|
-
.map((node) => {
|
|
741
|
-
// Backward-compat: compiled plugins may still pass bare strings at runtime
|
|
742
|
-
if (typeof node === 'string') return node as string
|
|
743
|
-
if (node.kind === 'Text') return node.value
|
|
744
|
-
if (node.kind === 'Break') return ''
|
|
745
|
-
if (node.kind === 'Jsx') return node.value
|
|
746
|
-
|
|
747
|
-
const parts: Array<string> = []
|
|
748
|
-
|
|
749
|
-
if ('params' in node && node.params) parts.push(node.params)
|
|
750
|
-
if ('generics' in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(', ') : node.generics)
|
|
751
|
-
if ('returnType' in node && node.returnType) parts.push(node.returnType)
|
|
752
|
-
if ('type' in node && typeof node.type === 'string') parts.push(node.type)
|
|
753
|
-
|
|
754
|
-
const nested = extractStringsFromNodes(node.nodes)
|
|
755
|
-
|
|
756
|
-
if (nested) parts.push(nested)
|
|
757
|
-
|
|
758
|
-
return parts.join('\n')
|
|
759
|
-
})
|
|
760
|
-
.filter(Boolean)
|
|
761
|
-
.join('\n')
|
|
762
|
-
}
|
|
763
|
-
|
|
764
624
|
/**
|
|
765
625
|
* Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
|
|
766
626
|
*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CodeNode } from '../nodes/code.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts all string content from a `CodeNode` tree recursively.
|
|
5
|
+
*
|
|
6
|
+
* Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
|
|
7
|
+
* and nested node content. Used to build the full source string for import filtering.
|
|
8
|
+
*/
|
|
9
|
+
export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): string {
|
|
10
|
+
if (!nodes?.length) return ''
|
|
11
|
+
return nodes
|
|
12
|
+
.map((node) => {
|
|
13
|
+
// Backward-compat: compiled plugins may still pass bare strings at runtime
|
|
14
|
+
if (typeof node === 'string') return node as string
|
|
15
|
+
if (node.kind === 'Text') return node.value
|
|
16
|
+
if (node.kind === 'Break') return ''
|
|
17
|
+
if (node.kind === 'Jsx') return node.value
|
|
18
|
+
|
|
19
|
+
const parts: Array<string> = []
|
|
20
|
+
|
|
21
|
+
if ('params' in node && node.params) parts.push(node.params)
|
|
22
|
+
if ('generics' in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(', ') : node.generics)
|
|
23
|
+
if ('returnType' in node && node.returnType) parts.push(node.returnType)
|
|
24
|
+
if ('type' in node && typeof node.type === 'string') parts.push(node.type)
|
|
25
|
+
|
|
26
|
+
const nested = extractStringsFromNodes(node.nodes)
|
|
27
|
+
|
|
28
|
+
if (nested) parts.push(nested)
|
|
29
|
+
|
|
30
|
+
return parts.join('\n')
|
|
31
|
+
})
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join('\n')
|
|
34
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { isIdentifier, pascalCase, singleQuote } from '@internals/utils'
|
|
2
2
|
import { INDENT } from '../constants.ts'
|
|
3
|
+
import type { OperationNode, ParameterNode } from '../nodes/index.ts'
|
|
4
|
+
import type { OperationParamsResolver, ParamGroupType } from './ast.ts'
|
|
3
5
|
|
|
4
6
|
export { isValidVarName } from '@internals/utils'
|
|
7
|
+
export { extractStringsFromNodes } from './extractStringsFromNodes.ts'
|
|
8
|
+
export {
|
|
9
|
+
buildGroupParam,
|
|
10
|
+
buildTypeLiteral,
|
|
11
|
+
caseParams,
|
|
12
|
+
collectUsedSchemaNames,
|
|
13
|
+
containsCircularRef,
|
|
14
|
+
findCircularSchemas,
|
|
15
|
+
isStringType,
|
|
16
|
+
resolveParamType,
|
|
17
|
+
syncSchemaRef,
|
|
18
|
+
} from './ast.ts'
|
|
19
|
+
export type { BuildGroupArgs, ParamGroupType } from './ast.ts'
|
|
5
20
|
|
|
6
21
|
/**
|
|
7
22
|
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
@@ -295,3 +310,32 @@ export function findDiscriminator(mapping: Record<string, string> | undefined, r
|
|
|
295
310
|
if (!mapping || !ref) return null
|
|
296
311
|
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null
|
|
297
312
|
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Derives a {@link ParamGroupType} for a query or header group from the resolver.
|
|
316
|
+
*
|
|
317
|
+
* Returns `null` when there is no resolver, no params, or the group name equals the
|
|
318
|
+
* individual param name (so there is no real group to emit).
|
|
319
|
+
*/
|
|
320
|
+
export function resolveGroupType({
|
|
321
|
+
node,
|
|
322
|
+
params,
|
|
323
|
+
group,
|
|
324
|
+
resolver,
|
|
325
|
+
}: {
|
|
326
|
+
node: OperationNode
|
|
327
|
+
params: Array<ParameterNode>
|
|
328
|
+
group: 'query' | 'header'
|
|
329
|
+
resolver: OperationParamsResolver | undefined
|
|
330
|
+
}): ParamGroupType | null {
|
|
331
|
+
if (!resolver || !params.length) {
|
|
332
|
+
return null
|
|
333
|
+
}
|
|
334
|
+
const firstParam = params[0]!
|
|
335
|
+
const groupMethod = group === 'query' ? resolver.resolveQueryParamsName : resolver.resolveHeaderParamsName
|
|
336
|
+
const groupName = groupMethod.call(resolver, node, firstParam)
|
|
337
|
+
if (groupName === resolver.resolveParamName(node, firstParam)) {
|
|
338
|
+
return null
|
|
339
|
+
}
|
|
340
|
+
return { type: groupName, optional: params.every((p) => !p.required) }
|
|
341
|
+
}
|
package/src/visitor.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { VisitorDepth } from './constants.ts'
|
|
2
2
|
import { visitorDepths, WALK_CONCURRENCY } from './constants.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { nodeRebuilders, VISITOR_KEY_BY_KIND, VISITOR_KEYS } from './registry.ts'
|
|
4
4
|
import type {
|
|
5
5
|
ContentNode,
|
|
6
6
|
InputNode,
|
|
7
7
|
Node,
|
|
8
|
-
NodeKind,
|
|
9
8
|
OperationNode,
|
|
10
9
|
OutputNode,
|
|
11
10
|
ParameterNode,
|
|
@@ -281,24 +280,6 @@ export type CollectOptions<T> = CollectVisitor<T> & {
|
|
|
281
280
|
parent?: Node
|
|
282
281
|
}
|
|
283
282
|
|
|
284
|
-
/**
|
|
285
|
-
* Child node fields per node kind, in traversal order (Babel's `VISITOR_KEYS`).
|
|
286
|
-
*
|
|
287
|
-
* Each listed property holds a child node, an array of child nodes, or, for
|
|
288
|
-
* `additionalProperties` a node or the literal `true` (skipped). Every value
|
|
289
|
-
* in a child slot is a node, so one table drives both `getChildren` and `transform`.
|
|
290
|
-
*/
|
|
291
|
-
const VISITOR_KEYS = {
|
|
292
|
-
Input: ['schemas', 'operations'],
|
|
293
|
-
Operation: ['parameters', 'requestBody', 'responses'],
|
|
294
|
-
RequestBody: ['content'],
|
|
295
|
-
Content: ['schema'],
|
|
296
|
-
Response: ['content'],
|
|
297
|
-
Schema: ['properties', 'items', 'members', 'additionalProperties'],
|
|
298
|
-
Property: ['schema'],
|
|
299
|
-
Parameter: ['schema'],
|
|
300
|
-
} as const satisfies Partial<Record<NodeKind, ReadonlyArray<string>>>
|
|
301
|
-
|
|
302
283
|
const visitorKeysByKind = VISITOR_KEYS as Record<string, ReadonlyArray<string> | undefined>
|
|
303
284
|
|
|
304
285
|
/**
|
|
@@ -336,21 +317,6 @@ function* getChildren(node: Node, recurse: boolean): Generator<Node, void, undef
|
|
|
336
317
|
}
|
|
337
318
|
}
|
|
338
319
|
|
|
339
|
-
/**
|
|
340
|
-
* Maps a node `kind` to the matching visitor callback name. Only the seven
|
|
341
|
-
* traversable node kinds have an entry. Every other kind resolves to
|
|
342
|
-
* `undefined` and is skipped.
|
|
343
|
-
*/
|
|
344
|
-
const VISITOR_KEY_BY_KIND: Partial<Record<NodeKind, keyof Visitor>> = {
|
|
345
|
-
Input: 'input',
|
|
346
|
-
Output: 'output',
|
|
347
|
-
Operation: 'operation',
|
|
348
|
-
Schema: 'schema',
|
|
349
|
-
Property: 'property',
|
|
350
|
-
Parameter: 'parameter',
|
|
351
|
-
Response: 'response',
|
|
352
|
-
}
|
|
353
|
-
|
|
354
320
|
/**
|
|
355
321
|
* Invokes the visitor callback that matches `node.kind`, passing the traversal
|
|
356
322
|
* context. Returns the callback's result (a replacement node, a collected
|
|
@@ -455,18 +421,8 @@ export function transform(node: Node, options: TransformOptions): Node {
|
|
|
455
421
|
// changed" by identity and ancestors can avoid reallocating.
|
|
456
422
|
if (rebuilt === node) return node
|
|
457
423
|
|
|
458
|
-
const
|
|
459
|
-
return
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Per-kind builders rerun after children are rebuilt. `Property`/`Parameter`
|
|
464
|
-
* resync schema optionality against their `required` flag once the schema may
|
|
465
|
-
* have changed.
|
|
466
|
-
*/
|
|
467
|
-
const nodeFinalizers: Partial<Record<NodeKind, (node: Node) => Node>> = {
|
|
468
|
-
Property: (node) => createProperty(node as PropertyNode),
|
|
469
|
-
Parameter: (node) => createParameter(node as ParameterNode),
|
|
424
|
+
const rebuild = nodeRebuilders[rebuilt.kind]
|
|
425
|
+
return rebuild ? rebuild(rebuilt) : rebuilt
|
|
470
426
|
}
|
|
471
427
|
|
|
472
428
|
/**
|