@kubb/ast 5.0.0-alpha.9 → 5.0.0-beta.75

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/src/factory.ts CHANGED
@@ -1,24 +1,138 @@
1
- import type { ObjectSchemaNode, OperationNode, ParameterNode, PropertyNode, ResponseNode, RootNode, SchemaNode } from './nodes/index.ts'
1
+ import { createHash } from 'node:crypto'
2
+ import path from 'node:path'
3
+ import { trimExtName } from '@internals/utils'
4
+ import type { InferSchemaNode } from './infer.ts'
5
+ import type {
6
+ ArrowFunctionNode,
7
+ BreakNode,
8
+ ConstNode,
9
+ ExportNode,
10
+ FileNode,
11
+ FunctionNode,
12
+ FunctionParameterNode,
13
+ FunctionParametersNode,
14
+ ImportNode,
15
+ InputNode,
16
+ JsxNode,
17
+ ObjectSchemaNode,
18
+ OperationNode,
19
+ OutputNode,
20
+ ParameterGroupNode,
21
+ ParameterNode,
22
+ ParamsTypeNode,
23
+ PrimitiveSchemaType,
24
+ PropertyNode,
25
+ ResponseNode,
26
+ SchemaNode,
27
+ SourceNode,
28
+ TextNode,
29
+ TypeNode,
30
+ } from './nodes/index.ts'
31
+ import { combineExports, combineImports, combineSources, extractStringsFromNodes } from './utils.ts'
2
32
 
3
33
  /**
4
- * Distributive variant of `Omit` that preserves union members.
34
+ * Syncs property/parameter schema optionality flags from `required` and `schema.nullable`.
35
+ *
36
+ * - `optional` is set for non-required, non-nullable schemas.
37
+ * - `nullish` is set for non-required, nullable schemas.
38
+ */
39
+ export function syncOptionality(schema: SchemaNode, required: boolean): SchemaNode {
40
+ const nullable = schema.nullable ?? false
41
+
42
+ return {
43
+ ...schema,
44
+ optional: !required && !nullable ? true : undefined,
45
+ nullish: !required && nullable ? true : undefined,
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Distributive `Omit` that preserves each member of a union.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * type A = { kind: 'a'; keep: string; drop: number }
55
+ * type B = { kind: 'b'; keep: boolean; drop: number }
56
+ * type Result = DistributiveOmit<A | B, 'drop'>
57
+ * // -> { kind: 'a'; keep: string } | { kind: 'b'; keep: boolean }
58
+ * ```
5
59
  */
6
60
  export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never
7
61
 
62
+ type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties' | 'primitive'> & { properties?: Array<PropertyNode>; primitive?: 'object' }
63
+ type CreateSchemaInput = CreateSchemaObjectInput | DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>
64
+ type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & {
65
+ kind: 'Schema'
66
+ }
67
+
8
68
  /**
9
- * Creates a `RootNode`.
69
+ * Creates an `InputNode` with stable defaults for `schemas` and `operations`.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const input = createInput()
74
+ * // { kind: 'Input', schemas: [], operations: [] }
75
+ * ```
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const input = createInput({ schemas: [petSchema] })
80
+ * // keeps default operations: []
81
+ * ```
10
82
  */
11
- export function createRoot(overrides: Partial<Omit<RootNode, 'kind'>> = {}): RootNode {
83
+ export function createInput(overrides: Partial<Omit<InputNode, 'kind'>> = {}): InputNode {
12
84
  return {
13
85
  schemas: [],
14
86
  operations: [],
15
87
  ...overrides,
16
- kind: 'Root',
88
+ kind: 'Input',
17
89
  }
18
90
  }
19
91
 
20
92
  /**
21
- * Creates an `OperationNode`.
93
+ * Creates an `OutputNode` with a stable default for `files`.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const output = createOutput()
98
+ * // { kind: 'Output', files: [] }
99
+ * ```
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const output = createOutput({ files: [petFile] })
104
+ * ```
105
+ */
106
+ export function createOutput(overrides: Partial<Omit<OutputNode, 'kind'>> = {}): OutputNode {
107
+ return {
108
+ files: [],
109
+ ...overrides,
110
+ kind: 'Output',
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Creates an `OperationNode` with default empty arrays for `tags`, `parameters`, and `responses`.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const operation = createOperation({
120
+ * operationId: 'getPetById',
121
+ * method: 'GET',
122
+ * path: '/pet/{petId}',
123
+ * })
124
+ * // tags, parameters, and responses are []
125
+ * ```
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * const operation = createOperation({
130
+ * operationId: 'findPets',
131
+ * method: 'GET',
132
+ * path: '/pet/findByStatus',
133
+ * tags: ['pet'],
134
+ * })
135
+ * ```
22
136
  */
23
137
  export function createOperation(
24
138
  props: Pick<OperationNode, 'operationId' | 'method' | 'path'> & Partial<Omit<OperationNode, 'kind' | 'operationId' | 'method' | 'path'>>,
@@ -32,49 +146,172 @@ export function createOperation(
32
146
  }
33
147
  }
34
148
 
149
+ /**
150
+ * Maps schema `type` to its underlying `primitive`.
151
+ * Primitive types map to themselves; special string formats map to `'string'`.
152
+ * Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
153
+ */
154
+ const TYPE_TO_PRIMITIVE: Partial<Record<SchemaNode['type'], PrimitiveSchemaType>> = {
155
+ string: 'string',
156
+ number: 'number',
157
+ integer: 'integer',
158
+ bigint: 'bigint',
159
+ boolean: 'boolean',
160
+ null: 'null',
161
+ any: 'any',
162
+ unknown: 'unknown',
163
+ void: 'void',
164
+ never: 'never',
165
+ object: 'object',
166
+ array: 'array',
167
+ date: 'date',
168
+ uuid: 'string',
169
+ email: 'string',
170
+ url: 'string',
171
+ datetime: 'string',
172
+ time: 'string',
173
+ }
174
+
35
175
  /**
36
176
  * Creates a `SchemaNode`, narrowed to the variant of `props.type`.
37
- * For object schemas, `properties` defaults to `[]` when not provided.
38
- */
39
- export function createSchema<T extends Omit<ObjectSchemaNode, 'kind' | 'properties'> & { properties?: Array<PropertyNode> }>(
40
- props: T,
41
- ): Omit<T, 'properties'> & { properties: Array<PropertyNode>; kind: 'Schema' }
42
- export function createSchema<T extends DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>>(props: T): T & { kind: 'Schema' }
43
- export function createSchema(props: DistributiveOmit<SchemaNode, 'kind'>): SchemaNode
44
- export function createSchema(props: Record<string, unknown>): Record<string, unknown> {
177
+ * For object schemas, `properties` defaults to an empty array.
178
+ * `primitive` is automatically inferred from `type` when not explicitly provided.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * const scalar = createSchema({ type: 'string' })
183
+ * // { kind: 'Schema', type: 'string', primitive: 'string' }
184
+ * ```
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * const uuid = createSchema({ type: 'uuid' })
189
+ * // { kind: 'Schema', type: 'uuid', primitive: 'string' }
190
+ * ```
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const object = createSchema({ type: 'object' })
195
+ * // { kind: 'Schema', type: 'object', primitive: 'object', properties: [] }
196
+ * ```
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const enumSchema = createSchema({
201
+ * type: 'enum',
202
+ * primitive: 'string',
203
+ * enumValues: ['available', 'pending'],
204
+ * })
205
+ * ```
206
+ */
207
+ export function createSchema<T extends CreateSchemaInput>(props: T): CreateSchemaOutput<T>
208
+ export function createSchema(props: CreateSchemaInput): SchemaNode
209
+ export function createSchema(props: CreateSchemaInput): SchemaNode {
210
+ const inferredPrimitive = TYPE_TO_PRIMITIVE[props.type as keyof typeof TYPE_TO_PRIMITIVE]
211
+
45
212
  if (props['type'] === 'object') {
46
- return { properties: [], ...props, kind: 'Schema' }
213
+ return {
214
+ properties: [],
215
+ primitive: 'object',
216
+ ...props,
217
+ kind: 'Schema',
218
+ } as CreateSchemaOutput<typeof props>
47
219
  }
48
220
 
49
- return { ...props, kind: 'Schema' }
221
+ return {
222
+ primitive: inferredPrimitive,
223
+ ...props,
224
+ kind: 'Schema',
225
+ } as CreateSchemaOutput<typeof props>
50
226
  }
51
227
 
228
+ type UserPropertyNode = Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>
229
+
52
230
  /**
53
- * Creates a `PropertyNode`. `required` defaults to `false`.
231
+ * Creates a `PropertyNode`.
232
+ *
233
+ * `required` defaults to `false`.
234
+ * `schema.optional` and `schema.nullish` are derived from `required` and `schema.nullable`.
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * const property = createProperty({
239
+ * name: 'status',
240
+ * schema: createSchema({ type: 'string' }),
241
+ * })
242
+ * // required=false, schema.optional=true
243
+ * ```
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * const property = createProperty({
248
+ * name: 'status',
249
+ * required: true,
250
+ * schema: createSchema({ type: 'string', nullable: true }),
251
+ * })
252
+ * // required=true, no optional/nullish
253
+ * ```
54
254
  */
55
- export function createProperty(props: Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>): PropertyNode {
255
+ export function createProperty(props: UserPropertyNode): PropertyNode {
256
+ const required = props.required ?? false
257
+
56
258
  return {
57
- required: false,
58
259
  ...props,
59
260
  kind: 'Property',
261
+ required,
262
+ schema: syncOptionality(props.schema, required),
60
263
  }
61
264
  }
62
265
 
63
266
  /**
64
- * Creates a `ParameterNode`. `required` defaults to `false`.
267
+ * Creates a `ParameterNode`.
268
+ *
269
+ * `required` defaults to `false`.
270
+ * Nested schema flags are set from `required` and `schema.nullable`.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * const param = createParameter({
275
+ * name: 'petId',
276
+ * in: 'path',
277
+ * required: true,
278
+ * schema: createSchema({ type: 'string' }),
279
+ * })
280
+ * ```
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * const param = createParameter({
285
+ * name: 'status',
286
+ * in: 'query',
287
+ * schema: createSchema({ type: 'string', nullable: true }),
288
+ * })
289
+ * // required=false, schema.nullish=true
290
+ * ```
65
291
  */
66
292
  export function createParameter(
67
293
  props: Pick<ParameterNode, 'name' | 'in' | 'schema'> & Partial<Omit<ParameterNode, 'kind' | 'name' | 'in' | 'schema'>>,
68
294
  ): ParameterNode {
295
+ const required = props.required ?? false
69
296
  return {
70
- required: false,
71
297
  ...props,
72
298
  kind: 'Parameter',
299
+ required,
300
+ schema: syncOptionality(props.schema, required),
73
301
  }
74
302
  }
75
303
 
76
304
  /**
77
305
  * Creates a `ResponseNode`.
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const response = createResponse({
310
+ * statusCode: '200',
311
+ * description: 'Success',
312
+ * schema: createSchema({ type: 'object', properties: [] }),
313
+ * })
314
+ * ```
78
315
  */
79
316
  export function createResponse(
80
317
  props: Pick<ResponseNode, 'statusCode' | 'schema'> & Partial<Omit<ResponseNode, 'kind' | 'statusCode' | 'schema'>>,
@@ -84,3 +321,422 @@ export function createResponse(
84
321
  kind: 'Response',
85
322
  }
86
323
  }
324
+
325
+ /**
326
+ * Creates a `FunctionParameterNode`.
327
+ *
328
+ * `optional` defaults to `false`.
329
+ *
330
+ * @example Required typed param
331
+ * ```ts
332
+ * createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }) })
333
+ * // → petId: string
334
+ * ```
335
+ *
336
+ * @example Optional param
337
+ * ```ts
338
+ * createFunctionParameter({ name: 'params', type: createParamsType({ variant: 'reference', name: 'QueryParams' }), optional: true })
339
+ * // → params?: QueryParams
340
+ * ```
341
+ *
342
+ * @example Param with default (implicitly optional; cannot combine with `optional: true`)
343
+ * ```ts
344
+ * createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), default: '{}' })
345
+ * // → config: RequestConfig = {}
346
+ * ```
347
+ */
348
+ export function createFunctionParameter(
349
+ props: { name: string; type?: ParamsTypeNode; rest?: boolean } & ({ optional: true; default?: never } | { optional?: false; default?: string }),
350
+ ): FunctionParameterNode {
351
+ return {
352
+ optional: false,
353
+ ...props,
354
+ kind: 'FunctionParameter',
355
+ } as FunctionParameterNode
356
+ }
357
+
358
+ /**
359
+ * Creates a {@link TypeNode} representing a language-agnostic structured type expression.
360
+ *
361
+ * Use `variant: 'struct'` for inline anonymous types and `variant: 'member'` for a single
362
+ * named field accessed from a group type. Each language's printer renders the variant
363
+ * into its own syntax (TypeScript, Python, C#, Kotlin, …).
364
+ *
365
+ * @example Reference type (TypeScript: `QueryParams`)
366
+ * ```ts
367
+ * createParamsType({ variant: 'reference', name: 'QueryParams' })
368
+ * ```
369
+ *
370
+ * @example Struct type (TypeScript: `{ petId: string }`)
371
+ * ```ts
372
+ * createParamsType({ variant: 'struct', properties: [{ name: 'petId', optional: false, type: createParamsType({ variant: 'reference', name: 'string' }) }] })
373
+ * ```
374
+ *
375
+ * @example Member type (TypeScript: `DeletePetPathParams['petId']`)
376
+ * ```ts
377
+ * createParamsType({ variant: 'member', base: 'DeletePetPathParams', key: 'petId' })
378
+ * ```
379
+ */
380
+ export function createParamsType(
381
+ props:
382
+ | { variant: 'reference'; name: string }
383
+ | {
384
+ variant: 'struct'
385
+ properties: Array<{
386
+ name: string
387
+ optional: boolean
388
+ type: ParamsTypeNode
389
+ }>
390
+ }
391
+ | { variant: 'member'; base: string; key: string },
392
+ ): ParamsTypeNode {
393
+ return { ...props, kind: 'ParamsType' } as ParamsTypeNode
394
+ }
395
+
396
+ /**
397
+ * Creates a `ParameterGroupNode` representing a group of related parameters treated as a unit.
398
+ *
399
+ * @example Grouped param (TypeScript declaration)
400
+ * ```ts
401
+ * createParameterGroup({
402
+ * properties: [
403
+ * createFunctionParameter({ name: 'id', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
404
+ * createFunctionParameter({ name: 'name', type: createParamsType({ variant: 'reference', name: 'string' }), optional: true }),
405
+ * ],
406
+ * default: '{}',
407
+ * })
408
+ * // declaration → { id, name? }: { id: string; name?: string } = {}
409
+ * // call → { id, name }
410
+ * ```
411
+ *
412
+ * @example Inline (spread) — children emitted as individual top-level parameters
413
+ * ```ts
414
+ * createParameterGroup({
415
+ * properties: [createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false })],
416
+ * inline: true,
417
+ * })
418
+ * // declaration → petId: string
419
+ * // call → petId
420
+ * ```
421
+ */
422
+ export function createParameterGroup(
423
+ props: Pick<ParameterGroupNode, 'properties'> & Partial<Omit<ParameterGroupNode, 'kind' | 'properties'>>,
424
+ ): ParameterGroupNode {
425
+ return {
426
+ ...props,
427
+ kind: 'ParameterGroup',
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Creates a `FunctionParametersNode` from an ordered list of parameters.
433
+ *
434
+ * @example
435
+ * ```ts
436
+ * createFunctionParameters({
437
+ * params: [
438
+ * createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
439
+ * createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), optional: false, default: '{}' }),
440
+ * ],
441
+ * })
442
+ * ```
443
+ *
444
+ * @example
445
+ * ```ts
446
+ * const empty = createFunctionParameters()
447
+ * // { kind: 'FunctionParameters', params: [] }
448
+ * ```
449
+ */
450
+ export function createFunctionParameters(props: Partial<Omit<FunctionParametersNode, 'kind'>> = {}): FunctionParametersNode {
451
+ return {
452
+ params: [],
453
+ ...props,
454
+ kind: 'FunctionParameters',
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
460
+ *
461
+ * @example Named import
462
+ * ```ts
463
+ * createImport({ name: ['useState'], path: 'react' })
464
+ * // import { useState } from 'react'
465
+ * ```
466
+ *
467
+ * @example Type-only import
468
+ * ```ts
469
+ * createImport({ name: ['FC'], path: 'react', isTypeOnly: true })
470
+ * // import type { FC } from 'react'
471
+ * ```
472
+ */
473
+ export function createImport(props: Omit<ImportNode, 'kind'>): ImportNode {
474
+ return { ...props, kind: 'Import' }
475
+ }
476
+
477
+ /**
478
+ * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
479
+ *
480
+ * @example Named export
481
+ * ```ts
482
+ * createExport({ name: ['Pet'], path: './Pet' })
483
+ * // export { Pet } from './Pet'
484
+ * ```
485
+ *
486
+ * @example Wildcard export
487
+ * ```ts
488
+ * createExport({ path: './utils' })
489
+ * // export * from './utils'
490
+ * ```
491
+ */
492
+ export function createExport(props: Omit<ExportNode, 'kind'>): ExportNode {
493
+ return { ...props, kind: 'Export' }
494
+ }
495
+
496
+ /**
497
+ * Creates a `SourceNode` representing a fragment of source code within a file.
498
+ *
499
+ * @example
500
+ * ```ts
501
+ * createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
502
+ * ```
503
+ */
504
+ export function createSource(props: Omit<SourceNode, 'kind'>): SourceNode {
505
+ return { ...props, kind: 'Source' }
506
+ }
507
+
508
+ export type UserFileNode<TMeta extends object = object> = Omit<FileNode<TMeta>, 'kind' | 'id' | 'name' | 'extname' | 'imports' | 'exports' | 'sources'> &
509
+ Pick<Partial<FileNode<TMeta>>, 'imports' | 'exports' | 'sources'>
510
+
511
+ /**
512
+ * Creates a fully resolved `FileNode` from a file input descriptor.
513
+ *
514
+ * Computes:
515
+ * - `id` — SHA256 hash of the file path
516
+ * - `name` — `baseName` without extension
517
+ * - `extname` — extension extracted from `baseName`
518
+ *
519
+ * Deduplicates:
520
+ * - `sources` via `combineSources`
521
+ * - `exports` via `combineExports`
522
+ * - `imports` via `combineImports` (also filters unused imports)
523
+ *
524
+ * @throws {Error} when `baseName` has no extension.
525
+ *
526
+ * @example
527
+ * ```ts
528
+ * const file = createFile({
529
+ * baseName: 'petStore.ts',
530
+ * path: 'src/models/petStore.ts',
531
+ * sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
532
+ * imports: [createImport({ name: ['z'], path: 'zod' })],
533
+ * exports: [createExport({ name: ['Pet'], path: './petStore' })],
534
+ * })
535
+ * // file.id = SHA256 hash of 'src/models/petStore.ts'
536
+ * // file.name = 'petStore'
537
+ * // file.extname = '.ts'
538
+ * ```
539
+ */
540
+ export function createFile<TMeta extends object = object>(input: UserFileNode<TMeta>): FileNode<TMeta> {
541
+ const rawExtname = path.extname(input.baseName)
542
+ // Handle dotfile basename like '.ts' where path.extname returns ''
543
+ const extname = (rawExtname || (input.baseName.startsWith('.') ? input.baseName : '')) as `.${string}`
544
+ if (!extname) {
545
+ throw new Error(`No extname found for ${input.baseName}`)
546
+ }
547
+
548
+ const source = (input.sources ?? [])
549
+ .flatMap((item) => item.nodes ?? [])
550
+ .map((node) => extractStringsFromNodes([node]))
551
+ .filter(Boolean)
552
+ .join('\n\n')
553
+ const resolvedExports = input.exports?.length ? combineExports(input.exports) : []
554
+ const resolvedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || undefined) : []
555
+ const resolvedSources = input.sources?.length ? combineSources(input.sources) : []
556
+
557
+ return {
558
+ kind: 'File',
559
+ ...input,
560
+ id: createHash('sha256').update(input.path).digest('hex'),
561
+ name: trimExtName(input.baseName),
562
+ extname,
563
+ imports: resolvedImports,
564
+ exports: resolvedExports,
565
+ sources: resolvedSources,
566
+ meta: input.meta ?? ({} as TMeta),
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Creates a `ConstNode` representing a TypeScript `const` declaration.
572
+ *
573
+ * Mirrors the `Const` component from `@kubb/renderer-jsx`.
574
+ * The component's `children` are represented as `nodes`.
575
+ *
576
+ * @example Simple constant
577
+ * ```ts
578
+ * createConst({ name: 'pet' })
579
+ * // const pet = ...
580
+ * ```
581
+ *
582
+ * @example Exported constant with type and `as const`
583
+ * ```ts
584
+ * createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true })
585
+ * // export const pets: Pet[] = ... as const
586
+ * ```
587
+ *
588
+ * @example With JSDoc and child nodes
589
+ * ```ts
590
+ * createConst({
591
+ * name: 'config',
592
+ * export: true,
593
+ * JSDoc: { comments: ['@description App configuration'] },
594
+ * nodes: [],
595
+ * })
596
+ * ```
597
+ */
598
+ export function createConst(props: Omit<ConstNode, 'kind'>): ConstNode {
599
+ return { ...props, kind: 'Const' }
600
+ }
601
+
602
+ /**
603
+ * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
604
+ *
605
+ * Mirrors the `Type` component from `@kubb/renderer-jsx`.
606
+ * The component's `children` are represented as `nodes`.
607
+ *
608
+ * @example Simple type alias
609
+ * ```ts
610
+ * createType({ name: 'Pet' })
611
+ * // type Pet = ...
612
+ * ```
613
+ *
614
+ * @example Exported type with JSDoc
615
+ * ```ts
616
+ * createType({
617
+ * name: 'PetStatus',
618
+ * export: true,
619
+ * JSDoc: { comments: ['@description Status of a pet'] },
620
+ * })
621
+ * // export type PetStatus = ...
622
+ * ```
623
+ */
624
+ export function createType(props: Omit<TypeNode, 'kind'>): TypeNode {
625
+ return { ...props, kind: 'Type' }
626
+ }
627
+
628
+ /**
629
+ * Creates a `FunctionNode` representing a TypeScript `function` declaration.
630
+ *
631
+ * Mirrors the `Function` component from `@kubb/renderer-jsx`.
632
+ * The component's `children` are represented as `nodes`.
633
+ *
634
+ * @example Simple function
635
+ * ```ts
636
+ * createFunction({ name: 'getPet' })
637
+ * // function getPet() { ... }
638
+ * ```
639
+ *
640
+ * @example Exported async function with return type
641
+ * ```ts
642
+ * createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
643
+ * // export async function fetchPet(): Promise<Pet> { ... }
644
+ * ```
645
+ *
646
+ * @example Function with generics and params
647
+ * ```ts
648
+ * createFunction({
649
+ * name: 'identity',
650
+ * export: true,
651
+ * generics: ['T'],
652
+ * params: 'value: T',
653
+ * returnType: 'T',
654
+ * })
655
+ * // export function identity<T>(value: T): T { ... }
656
+ * ```
657
+ */
658
+ export function createFunction(props: Omit<FunctionNode, 'kind'>): FunctionNode {
659
+ return { ...props, kind: 'Function' }
660
+ }
661
+
662
+ /**
663
+ * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
664
+ *
665
+ * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
666
+ * The component's `children` are represented as `nodes`.
667
+ *
668
+ * @example Simple arrow function
669
+ * ```ts
670
+ * createArrowFunction({ name: 'getPet' })
671
+ * // const getPet = () => { ... }
672
+ * ```
673
+ *
674
+ * @example Single-line exported arrow function
675
+ * ```ts
676
+ * createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
677
+ * // export const double = (n: number) => ...
678
+ * ```
679
+ *
680
+ * @example Async arrow function with generics
681
+ * ```ts
682
+ * createArrowFunction({
683
+ * name: 'fetchPet',
684
+ * export: true,
685
+ * async: true,
686
+ * generics: ['T'],
687
+ * params: 'id: string',
688
+ * returnType: 'T',
689
+ * })
690
+ * // export const fetchPet = async <T>(id: string): Promise<T> => { ... }
691
+ * ```
692
+ */
693
+ export function createArrowFunction(props: Omit<ArrowFunctionNode, 'kind'>): ArrowFunctionNode {
694
+ return { ...props, kind: 'ArrowFunction' }
695
+ }
696
+
697
+ /**
698
+ * Creates a {@link TextNode} representing a raw string fragment in the source output.
699
+ *
700
+ * Use this instead of bare strings when building `nodes` arrays so that every
701
+ * entry in the array is a typed {@link CodeNode}.
702
+ *
703
+ * @example
704
+ * ```ts
705
+ * createText('return fetch(id)')
706
+ * // { kind: 'Text', value: 'return fetch(id)' }
707
+ * ```
708
+ */
709
+ export function createText(value: string): TextNode {
710
+ return { value, kind: 'Text' }
711
+ }
712
+
713
+ /**
714
+ * Creates a {@link BreakNode} representing a line break in the source output.
715
+ *
716
+ * Corresponds to `<br/>` in JSX components. Prints as an empty string which,
717
+ * when joined with `\n` by `printNodes`, produces a blank line.
718
+ *
719
+ * @example
720
+ * ```ts
721
+ * createBreak()
722
+ * // { kind: 'Break' }
723
+ * ```
724
+ */
725
+ export function createBreak(): BreakNode {
726
+ return { kind: 'Break' }
727
+ }
728
+
729
+ /**
730
+ * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
731
+ *
732
+ * Use this to embed JSX markup (including fragments `<>…</>`) directly in generated code.
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * createJsx('<>\n <a href={href}>Open</a>\n</>')
737
+ * // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
738
+ * ```
739
+ */
740
+ export function createJsx(value: string): JsxNode {
741
+ return { value, kind: 'Jsx' }
742
+ }