@kubb/plugin-ts 5.0.0-alpha.8 → 5.0.0-beta.3

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.
Files changed (42) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +1 -3
  3. package/dist/index.cjs +1452 -4
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +574 -4
  6. package/dist/index.js +1418 -2
  7. package/dist/index.js.map +1 -0
  8. package/package.json +44 -64
  9. package/src/components/{v2/Enum.tsx → Enum.tsx} +33 -17
  10. package/src/components/Type.tsx +31 -161
  11. package/src/constants.ts +15 -5
  12. package/src/factory.ts +283 -35
  13. package/src/generators/typeGenerator.tsx +189 -424
  14. package/src/index.ts +9 -2
  15. package/src/plugin.ts +67 -205
  16. package/src/printers/functionPrinter.ts +197 -0
  17. package/src/printers/printerTs.ts +325 -0
  18. package/src/resolvers/resolverTs.ts +66 -0
  19. package/src/types.ts +238 -94
  20. package/src/utils.ts +130 -0
  21. package/dist/components-CRu8IKY3.js +0 -729
  22. package/dist/components-CRu8IKY3.js.map +0 -1
  23. package/dist/components-DeNDKlzf.cjs +0 -982
  24. package/dist/components-DeNDKlzf.cjs.map +0 -1
  25. package/dist/components.cjs +0 -3
  26. package/dist/components.d.ts +0 -36
  27. package/dist/components.js +0 -2
  28. package/dist/generators.cjs +0 -4
  29. package/dist/generators.d.ts +0 -480
  30. package/dist/generators.js +0 -2
  31. package/dist/plugin-D5NGPj0v.js +0 -1232
  32. package/dist/plugin-D5NGPj0v.js.map +0 -1
  33. package/dist/plugin-MLTxoa8p.cjs +0 -1279
  34. package/dist/plugin-MLTxoa8p.cjs.map +0 -1
  35. package/dist/types-CsvB6X5Y.d.ts +0 -167
  36. package/src/components/index.ts +0 -1
  37. package/src/components/v2/Type.tsx +0 -59
  38. package/src/generators/index.ts +0 -2
  39. package/src/generators/v2/typeGenerator.tsx +0 -171
  40. package/src/generators/v2/utils.ts +0 -140
  41. package/src/parser.ts +0 -389
  42. package/src/printer.ts +0 -368
package/src/factory.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  import { camelCase, pascalCase, screamingSnakeCase, snakeCase } from '@internals/utils'
2
+ import { ast } from '@kubb/core'
2
3
  import { isNumber, sortBy } from 'remeda'
3
4
  import ts from 'typescript'
5
+ import { OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
4
6
 
5
7
  const { SyntaxKind, factory } = ts
6
8
 
7
9
  // https://ts-ast-viewer.com/
8
10
 
11
+ /**
12
+ * TypeScript AST modifiers for common keywords (async, export, const, static).
13
+ */
9
14
  export const modifiers = {
10
15
  async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
11
16
  export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
@@ -13,22 +18,15 @@ export const modifiers = {
13
18
  static: factory.createModifier(ts.SyntaxKind.StaticKeyword),
14
19
  } as const
15
20
 
21
+ /**
22
+ * TypeScript syntax kind constants for union, literal, and string types.
23
+ */
16
24
  export const syntaxKind = {
17
25
  union: SyntaxKind.UnionType as 192,
18
26
  literalType: SyntaxKind.LiteralType,
19
27
  stringLiteral: SyntaxKind.StringLiteral,
20
28
  } as const
21
29
 
22
- export function getUnknownType(unknownType: 'any' | 'unknown' | 'void' | undefined) {
23
- if (unknownType === 'any') {
24
- return keywordTypeNodes.any
25
- }
26
- if (unknownType === 'void') {
27
- return keywordTypeNodes.void
28
- }
29
-
30
- return keywordTypeNodes.unknown
31
- }
32
30
  function isValidIdentifier(str: string): boolean {
33
31
  if (!str.length || str.trim() !== str) {
34
32
  return false
@@ -48,6 +46,10 @@ function propertyName(name: string | ts.PropertyName): ts.PropertyName {
48
46
 
49
47
  const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken)
50
48
 
49
+ /**
50
+ * Creates a question token for optional type annotations.
51
+ * Pass `true` to use the cached token, or provide a pre-created token.
52
+ */
51
53
  export function createQuestionToken(token?: boolean | ts.QuestionToken) {
52
54
  if (!token) {
53
55
  return undefined
@@ -58,6 +60,10 @@ export function createQuestionToken(token?: boolean | ts.QuestionToken) {
58
60
  return token
59
61
  }
60
62
 
63
+ /**
64
+ * Creates a TypeScript intersection type node from multiple type nodes.
65
+ * Returns the single node if only one is provided, or wraps in parentheses if requested.
66
+ */
61
67
  export function createIntersectionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
62
68
  if (!nodes.length) {
63
69
  return null
@@ -77,27 +83,15 @@ export function createIntersectionDeclaration({ nodes, withParentheses }: { node
77
83
  }
78
84
 
79
85
  /**
80
- * Minimum nodes length of 2
81
- * @example `string & number`
86
+ * Creates a TypeScript array type node.
87
+ * Use `arrayType: 'array'` for bracket syntax (`T[]`), or `'generic'` for `Array<T>`.
88
+ *
89
+ * @example Array bracket syntax
90
+ * `createArrayDeclaration({ nodes: [stringType], arrayType: 'array' }) // → string[]`
91
+ *
92
+ * @example Generic Array syntax
93
+ * `createArrayDeclaration({ nodes: [stringType], arrayType: 'generic' }) // → Array<string>`
82
94
  */
83
- export function createTupleDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
84
- if (!nodes.length) {
85
- return null
86
- }
87
-
88
- if (nodes.length === 1) {
89
- return nodes[0] || null
90
- }
91
-
92
- const node = factory.createTupleTypeNode(nodes)
93
-
94
- if (withParentheses) {
95
- return factory.createParenthesizedType(node)
96
- }
97
-
98
- return node
99
- }
100
-
101
95
  export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes: Array<ts.TypeNode>; arrayType?: 'array' | 'generic' }): ts.TypeNode | null {
102
96
  if (!nodes.length) {
103
97
  return factory.createTupleTypeNode([])
@@ -125,7 +119,8 @@ export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes:
125
119
 
126
120
  /**
127
121
  * Minimum nodes length of 2
128
- * @example `string | number`
122
+ * @example Union type example
123
+ * `string | number`
129
124
  */
130
125
  export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode {
131
126
  if (!nodes.length) {
@@ -145,6 +140,10 @@ export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Arra
145
140
  return node
146
141
  }
147
142
 
143
+ /**
144
+ * Creates a TypeScript property signature for object/interface members.
145
+ * Supports optional markers, readonly modifiers, and type annotations.
146
+ */
148
147
  export function createPropertySignature({
149
148
  readOnly,
150
149
  modifiers = [],
@@ -166,6 +165,9 @@ export function createPropertySignature({
166
165
  )
167
166
  }
168
167
 
168
+ /**
169
+ * Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
170
+ */
169
171
  export function createParameterSignature(
170
172
  name: string | ts.BindingName,
171
173
  {
@@ -186,6 +188,10 @@ export function createParameterSignature(
186
188
  return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer)
187
189
  }
188
190
 
191
+ /**
192
+ * Creates a JSDoc comment node from an array of comment strings.
193
+ * Returns null if no comments are provided.
194
+ */
189
195
  export function createJSDoc({ comments }: { comments: string[] }) {
190
196
  if (!comments.length) {
191
197
  return null
@@ -204,7 +210,10 @@ export function createJSDoc({ comments }: { comments: string[] }) {
204
210
  }
205
211
 
206
212
  /**
207
- * @link https://github.com/microsoft/TypeScript/issues/44151
213
+ * Attaches JSDoc comments to an AST node as synthetic leading comments.
214
+ * Filters out undefined comments before attaching.
215
+ *
216
+ * @see https://github.com/microsoft/TypeScript/issues/44151
208
217
  */
209
218
  export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { node: TNode; comments: Array<string | undefined> }) {
210
219
  const filteredComments = comments.filter(Boolean)
@@ -222,6 +231,10 @@ export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { n
222
231
  return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `${text || '*'}\n`, true)
223
232
  }
224
233
 
234
+ /**
235
+ * Creates a TypeScript index signature for dynamic property access.
236
+ * Defines the key type (default: `string`) and value type on an object.
237
+ */
225
238
  export function createIndexSignature(
226
239
  type: ts.TypeNode,
227
240
  {
@@ -238,6 +251,9 @@ export function createIndexSignature(
238
251
  return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type)
239
252
  }
240
253
 
254
+ /**
255
+ * Creates a TypeScript type alias declaration with optional modifiers and type parameters.
256
+ */
241
257
  export function createTypeAliasDeclaration({
242
258
  modifiers,
243
259
  name,
@@ -252,6 +268,9 @@ export function createTypeAliasDeclaration({
252
268
  return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type)
253
269
  }
254
270
 
271
+ /**
272
+ * Creates a TypeScript interface declaration with optional modifiers, type parameters, and members.
273
+ */
255
274
  export function createInterfaceDeclaration({
256
275
  modifiers,
257
276
  name,
@@ -266,6 +285,10 @@ export function createInterfaceDeclaration({
266
285
  return factory.createInterfaceDeclaration(modifiers, name, typeParameters, undefined, members)
267
286
  }
268
287
 
288
+ /**
289
+ * Creates a TypeScript type declaration as either a type alias or interface.
290
+ * Intelligently selects the syntax based on the type structure and attaches JSDoc comments.
291
+ */
269
292
  export function createTypeDeclaration({
270
293
  syntax,
271
294
  isExportable,
@@ -281,7 +304,7 @@ export function createTypeDeclaration({
281
304
  }) {
282
305
  if (syntax === 'interface' && 'members' in type) {
283
306
  const node = createInterfaceDeclaration({
284
- members: type.members as Array<ts.TypeElement>,
307
+ members: [...(type as ts.TypeLiteralNode).members],
285
308
  modifiers: isExportable ? [modifiers.export] : [],
286
309
  name,
287
310
  typeParameters: undefined,
@@ -306,6 +329,9 @@ export function createTypeDeclaration({
306
329
  })
307
330
  }
308
331
 
332
+ /**
333
+ * Creates a TypeScript namespace declaration (exported module).
334
+ */
309
335
  export function createNamespaceDeclaration({ statements, name }: { name: string; statements: ts.Statement[] }) {
310
336
  return factory.createModuleDeclaration(
311
337
  [factory.createToken(ts.SyntaxKind.ExportKeyword)],
@@ -316,8 +342,17 @@ export function createNamespaceDeclaration({ statements, name }: { name: string;
316
342
  }
317
343
 
318
344
  /**
319
- * In { propertyName: string; name?: string } is `name` being used to make the type more unique when multiple same names are used.
320
- * @example `import { Pet as Cat } from './Pet'`
345
+ * Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
346
+ * Optionally rename imported members with `propertyName` and `name` pairs.
347
+ *
348
+ * @example Default import
349
+ * `import Pet from './Pet'`
350
+ *
351
+ * @example Named imports with rename
352
+ * `import { Pet as Cat } from './Pet'`
353
+ *
354
+ * @example Namespace import
355
+ * `import * as Pet from './Pet'`
321
356
  */
322
357
  export function createImportDeclaration({
323
358
  name,
@@ -375,6 +410,10 @@ export function createImportDeclaration({
375
410
  )
376
411
  }
377
412
 
413
+ /**
414
+ * Creates an export declaration with support for named exports, namespace exports, and type-only exports.
415
+ * Sorts export names alphabetically for consistent output across platforms.
416
+ */
378
417
  export function createExportDeclaration({
379
418
  path,
380
419
  asAlias,
@@ -440,6 +479,20 @@ function applyEnumKeyCasing(key: string, casing: 'screamingSnakeCase' | 'snakeCa
440
479
  return key
441
480
  }
442
481
 
482
+ /**
483
+ * Creates a TypeScript enum declaration or equivalent construct in various formats.
484
+ * Returns a tuple of [name node, type node] - name node may be undefined for certain types.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * const [name, type] = createEnumDeclaration({
489
+ * type: 'enum',
490
+ * name: 'petType',
491
+ * typeName: 'PetType',
492
+ * enums: [['cat', 'cat'], ['dog', 'dog']],
493
+ * })
494
+ * ```
495
+ */
443
496
  export function createEnumDeclaration({
444
497
  type = 'enum',
445
498
  name,
@@ -626,6 +679,10 @@ export function createEnumDeclaration({
626
679
  ]
627
680
  }
628
681
 
682
+ /**
683
+ * Creates a TypeScript `Omit<T, Keys>` type reference node.
684
+ * Optionally wraps the type in `NonNullable<T>` if `nonNullable` is true.
685
+ */
629
686
  export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array<string> | string; type: ts.TypeNode; nonNullable?: boolean }) {
630
687
  const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier('NonNullable'), [type]) : type
631
688
 
@@ -643,6 +700,10 @@ export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array
643
700
  return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))])
644
701
  }
645
702
 
703
+ /**
704
+ * Pre-built TypeScript keyword type nodes for common primitive types.
705
+ * Use these to avoid repeatedly creating the same type nodes.
706
+ */
646
707
  export const keywordTypeNodes = {
647
708
  any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
648
709
  unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
@@ -701,26 +762,213 @@ export function createUrlTemplateType(path: string): ts.TypeNode {
701
762
  return ts.factory.createTemplateLiteralType(head, templateSpans)
702
763
  }
703
764
 
765
+ /**
766
+ * Creates a TypeScript type literal node (anonymous object type).
767
+ */
704
768
  export const createTypeLiteralNode = factory.createTypeLiteralNode
705
769
 
770
+ /**
771
+ * Creates a TypeScript type reference node (e.g., `Array<string>`, `Record<K, V>`).
772
+ */
706
773
  export const createTypeReferenceNode = factory.createTypeReferenceNode
774
+
775
+ /**
776
+ * Creates a numeric literal type node.
777
+ */
707
778
  export const createNumericLiteral = factory.createNumericLiteral
779
+
780
+ /**
781
+ * Creates a string literal type node.
782
+ */
708
783
  export const createStringLiteral = factory.createStringLiteral
709
784
 
785
+ /**
786
+ * Creates an array type node (e.g., `T[]`).
787
+ */
710
788
  export const createArrayTypeNode = factory.createArrayTypeNode
789
+
790
+ /**
791
+ * Creates a parenthesized type node to control operator precedence.
792
+ */
711
793
  export const createParenthesizedType = factory.createParenthesizedType
712
794
 
795
+ /**
796
+ * Creates a literal type node (e.g., `'hello'`, `42`, `true`).
797
+ */
713
798
  export const createLiteralTypeNode = factory.createLiteralTypeNode
799
+
800
+ /**
801
+ * Creates a null literal type node.
802
+ */
714
803
  export const createNull = factory.createNull
804
+
805
+ /**
806
+ * Creates an identifier node.
807
+ */
715
808
  export const createIdentifier = factory.createIdentifier
716
809
 
810
+ /**
811
+ * Creates an optional type node (e.g., `T | undefined`).
812
+ */
717
813
  export const createOptionalTypeNode = factory.createOptionalTypeNode
814
+
815
+ /**
816
+ * Creates a tuple type node (e.g., `[string, number]`).
817
+ */
718
818
  export const createTupleTypeNode = factory.createTupleTypeNode
819
+
820
+ /**
821
+ * Creates a rest type node for variadic tuple elements (e.g., `...T[]`).
822
+ */
719
823
  export const createRestTypeNode = factory.createRestTypeNode
824
+
825
+ /**
826
+ * Creates a boolean true literal type node.
827
+ */
720
828
  export const createTrue = factory.createTrue
829
+
830
+ /**
831
+ * Creates a boolean false literal type node.
832
+ */
721
833
  export const createFalse = factory.createFalse
834
+
835
+ /**
836
+ * Creates an indexed access type node (e.g., `T[K]`).
837
+ */
722
838
  export const createIndexedAccessTypeNode = factory.createIndexedAccessTypeNode
839
+
840
+ /**
841
+ * Creates a type operator node (e.g., `keyof T`, `readonly T[]`).
842
+ */
723
843
  export const createTypeOperatorNode = factory.createTypeOperatorNode
844
+
845
+ /**
846
+ * Creates a prefix unary expression (e.g., negative numbers, logical not).
847
+ */
724
848
  export const createPrefixUnaryExpression = factory.createPrefixUnaryExpression
725
849
 
850
+ /**
851
+ * Exports TypeScript SyntaxKind enum for AST node type checking.
852
+ */
726
853
  export { SyntaxKind }
854
+
855
+ // ─── Printer helpers ──────────────────────────────────────────────────────────
856
+
857
+ /**
858
+ * Converts a primitive const value to a TypeScript literal type node.
859
+ * Handles negative numbers via a prefix unary expression.
860
+ */
861
+ export function constToTypeNode(value: string | number | boolean, format: 'string' | 'number' | 'boolean'): ts.TypeNode | undefined {
862
+ if (format === 'boolean') {
863
+ return createLiteralTypeNode(value === true ? createTrue() : createFalse())
864
+ }
865
+ if (format === 'number' && typeof value === 'number') {
866
+ if (value < 0) {
867
+ return createLiteralTypeNode(createPrefixUnaryExpression(SyntaxKind.MinusToken, createNumericLiteral(Math.abs(value))))
868
+ }
869
+ return createLiteralTypeNode(createNumericLiteral(value))
870
+ }
871
+ return createLiteralTypeNode(createStringLiteral(String(value)))
872
+ }
873
+
874
+ /**
875
+ * Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
876
+ */
877
+ export function dateOrStringNode(node: { representation?: string }): ts.TypeNode {
878
+ return node.representation === 'date' ? createTypeReferenceNode(createIdentifier('Date')) : keywordTypeNodes.string
879
+ }
880
+
881
+ /**
882
+ * Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
883
+ */
884
+ export function buildMemberNodes(
885
+ members: Array<ast.SchemaNode> | undefined,
886
+ print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined,
887
+ ): Array<ts.TypeNode> {
888
+ return (members ?? []).map(print).filter(Boolean)
889
+ }
890
+
891
+ /**
892
+ * Builds a TypeScript tuple type node from an array schema's `items`,
893
+ * applying min/max slice and optional/rest element rules.
894
+ */
895
+ export function buildTupleNode(node: ast.ArraySchemaNode, print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined): ts.TypeNode | undefined {
896
+ let items = (node.items ?? []).map(print).filter(Boolean)
897
+
898
+ const restNode = node.rest ? (print(node.rest) ?? undefined) : undefined
899
+ const { min, max } = node
900
+
901
+ if (max !== undefined) {
902
+ items = items.slice(0, max)
903
+ if (items.length < max && restNode) {
904
+ items = [...items, ...Array(max - items.length).fill(restNode)]
905
+ }
906
+ }
907
+
908
+ if (min !== undefined) {
909
+ items = items.map((item, i) => (i >= min ? createOptionalTypeNode(item) : item))
910
+ }
911
+
912
+ if (max === undefined && restNode) {
913
+ items.push(createRestTypeNode(createArrayTypeNode(restNode)))
914
+ }
915
+
916
+ return createTupleTypeNode(items)
917
+ }
918
+
919
+ /**
920
+ * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
921
+ */
922
+ export function buildPropertyType(
923
+ schema: ast.SchemaNode,
924
+ baseType: ts.TypeNode,
925
+ optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined',
926
+ ): ts.TypeNode {
927
+ const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType)
928
+ const meta = ast.syncSchemaRef(schema)
929
+
930
+ let type = baseType
931
+
932
+ if (meta.nullable) {
933
+ type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] })
934
+ }
935
+
936
+ if ((meta.nullish || meta.optional) && addsUndefined) {
937
+ type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] })
938
+ }
939
+
940
+ return type
941
+ }
942
+
943
+ /**
944
+ * Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
945
+ */
946
+ export function buildIndexSignatures(
947
+ node: { additionalProperties?: ast.SchemaNode | boolean; patternProperties?: Record<string, ast.SchemaNode> },
948
+ propertyCount: number,
949
+ print: (node: ast.SchemaNode) => ts.TypeNode | null | undefined,
950
+ ): Array<ts.TypeElement> {
951
+ const elements: Array<ts.TypeElement> = []
952
+
953
+ if (node.additionalProperties && node.additionalProperties !== true) {
954
+ const additionalType = print(node.additionalProperties) ?? keywordTypeNodes.unknown
955
+
956
+ elements.push(createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : additionalType))
957
+ } else if (node.additionalProperties === true) {
958
+ elements.push(createIndexSignature(keywordTypeNodes.unknown))
959
+ }
960
+
961
+ if (node.patternProperties) {
962
+ const first = Object.values(node.patternProperties)[0]
963
+ if (first) {
964
+ let patternType = print(first) ?? keywordTypeNodes.unknown
965
+
966
+ if (first.nullable) {
967
+ patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] })
968
+ }
969
+ elements.push(createIndexSignature(patternType))
970
+ }
971
+ }
972
+
973
+ return elements
974
+ }