@kubb/plugin-ts 5.0.0-alpha.23 → 5.0.0-alpha.25
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/dist/index.cjs +480 -473
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +105 -37
- package/dist/index.js +486 -479
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/Enum.tsx +2 -7
- package/src/components/Type.tsx +8 -0
- package/src/constants.ts +10 -0
- package/src/factory.ts +122 -1
- package/src/generators/typeGenerator.tsx +119 -96
- package/src/generators/typeGeneratorLegacy.tsx +85 -80
- package/src/index.ts +0 -5
- package/src/plugin.ts +19 -49
- package/src/printers/functionPrinter.ts +6 -5
- package/src/printers/printerTs.ts +65 -181
- package/src/resolvers/resolverTs.ts +2 -2
- package/src/types.ts +93 -36
- package/src/utils.ts +41 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-ts",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.25",
|
|
4
4
|
"description": "TypeScript code generation plugin for Kubb, transforming OpenAPI schemas into TypeScript interfaces, types, and utility functions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@kubb/fabric-core": "0.15.1",
|
|
53
53
|
"@kubb/react-fabric": "0.15.1",
|
|
54
|
-
"remeda": "^2.33.
|
|
54
|
+
"remeda": "^2.33.7",
|
|
55
55
|
"typescript": "5.9.3",
|
|
56
|
-
"@kubb/ast": "5.0.0-alpha.
|
|
57
|
-
"@kubb/core": "5.0.0-alpha.
|
|
56
|
+
"@kubb/ast": "5.0.0-alpha.25",
|
|
57
|
+
"@kubb/core": "5.0.0-alpha.25"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@kubb/react-fabric": "0.15.1"
|
package/src/components/Enum.tsx
CHANGED
|
@@ -35,17 +35,12 @@ export function getEnumNames({
|
|
|
35
35
|
}): {
|
|
36
36
|
enumName: string
|
|
37
37
|
typeName: string
|
|
38
|
-
/**
|
|
39
|
-
* The PascalCase name that `$ref` importers will use to reference this enum type.
|
|
40
|
-
* For `asConst`/`asPascalConst` this differs from `typeName` (which has a `Key` suffix).
|
|
41
|
-
*/
|
|
42
|
-
refName: string
|
|
43
38
|
} {
|
|
44
39
|
const resolved = resolver.default(node.name!, 'type')
|
|
45
40
|
const enumName = enumType === 'asPascalConst' ? resolved : camelCase(node.name!)
|
|
46
41
|
const typeName = ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
|
|
47
42
|
|
|
48
|
-
return { enumName, typeName
|
|
43
|
+
return { enumName, typeName }
|
|
49
44
|
}
|
|
50
45
|
|
|
51
46
|
/**
|
|
@@ -67,7 +62,7 @@ export function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }
|
|
|
67
62
|
typeName,
|
|
68
63
|
enums: (node.namedEnumValues?.map((v) => [trimQuotes(v.name.toString()), v.value]) ??
|
|
69
64
|
node.enumValues?.filter((v): v is NonNullable<typeof v> => v !== null && v !== undefined).map((v) => [trimQuotes(v.toString()), v]) ??
|
|
70
|
-
[]) as
|
|
65
|
+
[]) as Array<[string | number, string | number | boolean]>,
|
|
71
66
|
type: enumType,
|
|
72
67
|
enumKeyCasing,
|
|
73
68
|
})
|
package/src/components/Type.tsx
CHANGED
|
@@ -18,6 +18,12 @@ type Props = {
|
|
|
18
18
|
resolver: PluginTs['resolver']
|
|
19
19
|
description?: string
|
|
20
20
|
keysToOmit?: string[]
|
|
21
|
+
/**
|
|
22
|
+
* Names of top-level schemas that are enums.
|
|
23
|
+
* Used so the printer's `ref` handler can use the suffixed type name (e.g. `StatusKey`)
|
|
24
|
+
* instead of the plain PascalCase name (e.g. `Status`) when resolving `$ref` enum targets.
|
|
25
|
+
*/
|
|
26
|
+
enumSchemaNames?: Set<string>
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export function Type({
|
|
@@ -32,6 +38,7 @@ export function Type({
|
|
|
32
38
|
enumKeyCasing,
|
|
33
39
|
description,
|
|
34
40
|
resolver,
|
|
41
|
+
enumSchemaNames,
|
|
35
42
|
}: Props): FabricReactNode {
|
|
36
43
|
const resolvedDescription = description || node?.description
|
|
37
44
|
const enumSchemaNodes = collect<EnumSchemaNode>(node, {
|
|
@@ -51,6 +58,7 @@ export function Type({
|
|
|
51
58
|
description: resolvedDescription,
|
|
52
59
|
keysToOmit,
|
|
53
60
|
resolver,
|
|
61
|
+
enumSchemaNames,
|
|
54
62
|
})
|
|
55
63
|
const output = printer.print(node)
|
|
56
64
|
|
package/src/constants.ts
CHANGED
|
@@ -27,3 +27,13 @@ export const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set<EnumType | undefined>(['enu
|
|
|
27
27
|
* `enumType` values whose type declaration is type-only (no runtime value emitted for the type alias).
|
|
28
28
|
*/
|
|
29
29
|
export const ENUM_TYPES_WITH_TYPE_ONLY = new Set<EnumType | undefined>(['asConst', 'asPascalConst', 'literal', undefined] as const)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Ordering priority for function parameters: lower = sorted earlier.
|
|
33
|
+
*/
|
|
34
|
+
export const PARAM_RANK = {
|
|
35
|
+
required: 0,
|
|
36
|
+
optional: 1,
|
|
37
|
+
withDefault: 2,
|
|
38
|
+
rest: 3,
|
|
39
|
+
} as const
|
package/src/factory.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { camelCase, pascalCase, screamingSnakeCase, snakeCase } from '@internals/utils'
|
|
2
|
+
import { syncSchemaRef } from '@kubb/ast'
|
|
3
|
+
import type { ArraySchemaNode, SchemaNode } from '@kubb/ast/types'
|
|
2
4
|
import { isNumber, sortBy } from 'remeda'
|
|
3
5
|
import ts from 'typescript'
|
|
6
|
+
import { OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
|
|
4
7
|
|
|
5
8
|
const { SyntaxKind, factory } = ts
|
|
6
9
|
|
|
@@ -249,7 +252,7 @@ export function createTypeDeclaration({
|
|
|
249
252
|
}) {
|
|
250
253
|
if (syntax === 'interface' && 'members' in type) {
|
|
251
254
|
const node = createInterfaceDeclaration({
|
|
252
|
-
members: type
|
|
255
|
+
members: [...(type as ts.TypeLiteralNode).members],
|
|
253
256
|
modifiers: isExportable ? [modifiers.export] : [],
|
|
254
257
|
name,
|
|
255
258
|
typeParameters: undefined,
|
|
@@ -692,3 +695,121 @@ export const createTypeOperatorNode = factory.createTypeOperatorNode
|
|
|
692
695
|
export const createPrefixUnaryExpression = factory.createPrefixUnaryExpression
|
|
693
696
|
|
|
694
697
|
export { SyntaxKind }
|
|
698
|
+
|
|
699
|
+
// ─── Printer helpers ──────────────────────────────────────────────────────────
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Converts a primitive const value to a TypeScript literal type node.
|
|
703
|
+
* Handles negative numbers via a prefix unary expression.
|
|
704
|
+
*/
|
|
705
|
+
export function constToTypeNode(value: string | number | boolean, format: 'string' | 'number' | 'boolean'): ts.TypeNode | undefined {
|
|
706
|
+
if (format === 'boolean') {
|
|
707
|
+
return createLiteralTypeNode(value === true ? createTrue() : createFalse())
|
|
708
|
+
}
|
|
709
|
+
if (format === 'number' && typeof value === 'number') {
|
|
710
|
+
if (value < 0) {
|
|
711
|
+
return createLiteralTypeNode(createPrefixUnaryExpression(SyntaxKind.MinusToken, createNumericLiteral(Math.abs(value))))
|
|
712
|
+
}
|
|
713
|
+
return createLiteralTypeNode(createNumericLiteral(value))
|
|
714
|
+
}
|
|
715
|
+
return createLiteralTypeNode(createStringLiteral(String(value)))
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
|
|
720
|
+
*/
|
|
721
|
+
export function dateOrStringNode(node: { representation?: string }): ts.TypeNode {
|
|
722
|
+
return node.representation === 'date' ? createTypeReferenceNode(createIdentifier('Date')) : keywordTypeNodes.string
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
|
|
727
|
+
*/
|
|
728
|
+
export function buildMemberNodes(members: Array<SchemaNode> | undefined, print: (node: SchemaNode) => ts.TypeNode | null | undefined): Array<ts.TypeNode> {
|
|
729
|
+
return (members ?? []).map(print).filter(Boolean)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Builds a TypeScript tuple type node from an array schema's `items`,
|
|
734
|
+
* applying min/max slice and optional/rest element rules.
|
|
735
|
+
*/
|
|
736
|
+
export function buildTupleNode(node: ArraySchemaNode, print: (node: SchemaNode) => ts.TypeNode | null | undefined): ts.TypeNode | undefined {
|
|
737
|
+
let items = (node.items ?? []).map(print).filter(Boolean)
|
|
738
|
+
|
|
739
|
+
const restNode = node.rest ? (print(node.rest) ?? undefined) : undefined
|
|
740
|
+
const { min, max } = node
|
|
741
|
+
|
|
742
|
+
if (max !== undefined) {
|
|
743
|
+
items = items.slice(0, max)
|
|
744
|
+
if (items.length < max && restNode) {
|
|
745
|
+
items = [...items, ...Array(max - items.length).fill(restNode)]
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (min !== undefined) {
|
|
750
|
+
items = items.map((item, i) => (i >= min ? createOptionalTypeNode(item) : item))
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (max === undefined && restNode) {
|
|
754
|
+
items.push(createRestTypeNode(createArrayTypeNode(restNode)))
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return createTupleTypeNode(items)
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
|
|
762
|
+
*/
|
|
763
|
+
export function buildPropertyType(
|
|
764
|
+
schema: SchemaNode,
|
|
765
|
+
baseType: ts.TypeNode,
|
|
766
|
+
optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined',
|
|
767
|
+
): ts.TypeNode {
|
|
768
|
+
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType)
|
|
769
|
+
const meta = syncSchemaRef(schema)
|
|
770
|
+
|
|
771
|
+
let type = baseType
|
|
772
|
+
|
|
773
|
+
if (meta.nullable) {
|
|
774
|
+
type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] })
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if ((meta.nullish || meta.optional) && addsUndefined) {
|
|
778
|
+
type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] })
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return type
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
|
|
786
|
+
*/
|
|
787
|
+
export function buildIndexSignatures(
|
|
788
|
+
node: { additionalProperties?: SchemaNode | boolean; patternProperties?: Record<string, SchemaNode> },
|
|
789
|
+
propertyCount: number,
|
|
790
|
+
print: (node: SchemaNode) => ts.TypeNode | null | undefined,
|
|
791
|
+
): Array<ts.TypeElement> {
|
|
792
|
+
const elements: Array<ts.TypeElement> = []
|
|
793
|
+
|
|
794
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
795
|
+
const additionalType = print(node.additionalProperties) ?? keywordTypeNodes.unknown
|
|
796
|
+
|
|
797
|
+
elements.push(createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : additionalType))
|
|
798
|
+
} else if (node.additionalProperties === true) {
|
|
799
|
+
elements.push(createIndexSignature(keywordTypeNodes.unknown))
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (node.patternProperties) {
|
|
803
|
+
const first = Object.values(node.patternProperties)[0]
|
|
804
|
+
if (first) {
|
|
805
|
+
let patternType = print(first) ?? keywordTypeNodes.unknown
|
|
806
|
+
|
|
807
|
+
if (first.nullable) {
|
|
808
|
+
patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] })
|
|
809
|
+
}
|
|
810
|
+
elements.push(createIndexSignature(patternType))
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return elements
|
|
815
|
+
}
|
|
@@ -11,46 +11,115 @@ import { buildData, buildResponses, buildResponseUnion } from '../utils.ts'
|
|
|
11
11
|
export const typeGenerator = defineGenerator<PluginTs>({
|
|
12
12
|
name: 'typescript',
|
|
13
13
|
type: 'react',
|
|
14
|
+
Schema({ node, adapter, options, config, resolver }) {
|
|
15
|
+
const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, transformers = [] } = options
|
|
16
|
+
|
|
17
|
+
const transformedNode = transform(node, composeTransformers(...transformers))
|
|
18
|
+
|
|
19
|
+
if (!transformedNode.name) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const root = path.resolve(config.root, config.output.path)
|
|
24
|
+
const mode = getMode(path.resolve(root, output.path))
|
|
25
|
+
// Build a set of schema names that are enums so the ref handler and getImports
|
|
26
|
+
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
27
|
+
const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name!))
|
|
28
|
+
|
|
29
|
+
function resolveImportName(schemaName: string): string {
|
|
30
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
|
|
31
|
+
return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix)
|
|
32
|
+
}
|
|
33
|
+
return resolver.default(schemaName, 'type')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const imports = adapter.getImports(transformedNode, (schemaName) => ({
|
|
37
|
+
name: resolveImportName(schemaName),
|
|
38
|
+
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
const isEnumSchema = !!narrowSchema(transformedNode, schemaTypes.enum)
|
|
42
|
+
|
|
43
|
+
const meta = {
|
|
44
|
+
name:
|
|
45
|
+
ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema
|
|
46
|
+
? resolver.resolveEnumKeyName(transformedNode, enumTypeSuffix)
|
|
47
|
+
: resolver.resolveName(transformedNode.name),
|
|
48
|
+
file: resolver.resolveFile({ name: transformedNode.name, extname: '.ts' }, { root, output, group }),
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<File
|
|
53
|
+
baseName={meta.file.baseName}
|
|
54
|
+
path={meta.file.path}
|
|
55
|
+
meta={meta.file.meta}
|
|
56
|
+
banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
|
|
57
|
+
footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
|
|
58
|
+
>
|
|
59
|
+
{mode === 'split' &&
|
|
60
|
+
imports.map((imp) => (
|
|
61
|
+
<File.Import key={[transformedNode.name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
62
|
+
))}
|
|
63
|
+
<Type
|
|
64
|
+
name={meta.name}
|
|
65
|
+
node={transformedNode}
|
|
66
|
+
enumType={enumType}
|
|
67
|
+
enumTypeSuffix={enumTypeSuffix}
|
|
68
|
+
enumKeyCasing={enumKeyCasing}
|
|
69
|
+
optionalType={optionalType}
|
|
70
|
+
arrayType={arrayType}
|
|
71
|
+
syntaxType={syntaxType}
|
|
72
|
+
resolver={resolver}
|
|
73
|
+
enumSchemaNames={enumSchemaNames}
|
|
74
|
+
/>
|
|
75
|
+
</File>
|
|
76
|
+
)
|
|
77
|
+
},
|
|
14
78
|
Operation({ node, adapter, options, config, resolver }) {
|
|
15
79
|
const { enumType, enumTypeSuffix, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, transformers = [] } = options
|
|
16
80
|
|
|
81
|
+
const transformedNode = transform(node, composeTransformers(...transformers))
|
|
82
|
+
|
|
17
83
|
const root = path.resolve(config.root, config.output.path)
|
|
18
84
|
const mode = getMode(path.resolve(root, output.path))
|
|
19
85
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return null
|
|
86
|
+
const params = caseParams(transformedNode.parameters, paramsCasing)
|
|
87
|
+
|
|
88
|
+
const meta = {
|
|
89
|
+
file: resolver.resolveFile(
|
|
90
|
+
{ name: transformedNode.operationId, extname: '.ts', tag: transformedNode.tags[0] ?? 'default', path: transformedNode.path },
|
|
91
|
+
{ root, output, group },
|
|
92
|
+
),
|
|
93
|
+
} as const
|
|
94
|
+
|
|
95
|
+
// Build a set of schema names that are enums so the ref handler and getImports
|
|
96
|
+
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
97
|
+
const enumSchemaNames = new Set((adapter.rootNode?.schemas ?? []).filter((s) => narrowSchema(s, schemaTypes.enum) && s.name).map((s) => s.name!))
|
|
98
|
+
|
|
99
|
+
function resolveImportName(schemaName: string): string {
|
|
100
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
|
|
101
|
+
return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix)
|
|
37
102
|
}
|
|
103
|
+
return resolver.default(schemaName, 'type')
|
|
104
|
+
}
|
|
38
105
|
|
|
39
|
-
|
|
106
|
+
function renderSchemaType({ schema, name, keysToOmit }: { schema: SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
|
|
107
|
+
if (!schema) return null
|
|
40
108
|
|
|
41
|
-
const imports = adapter.getImports(
|
|
42
|
-
name:
|
|
109
|
+
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
110
|
+
name: resolveImportName(schemaName),
|
|
43
111
|
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
44
112
|
}))
|
|
45
113
|
|
|
46
114
|
return (
|
|
47
115
|
<>
|
|
48
116
|
{mode === 'split' &&
|
|
49
|
-
imports.map((imp) =>
|
|
117
|
+
imports.map((imp) => (
|
|
118
|
+
<File.Import key={[name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
119
|
+
))}
|
|
50
120
|
<Type
|
|
51
121
|
name={name}
|
|
52
|
-
node={
|
|
53
|
-
description={description}
|
|
122
|
+
node={schema}
|
|
54
123
|
enumType={enumType}
|
|
55
124
|
enumTypeSuffix={enumTypeSuffix}
|
|
56
125
|
enumKeyCasing={enumKeyCasing}
|
|
@@ -59,6 +128,7 @@ export const typeGenerator = defineGenerator<PluginTs>({
|
|
|
59
128
|
syntaxType={syntaxType}
|
|
60
129
|
resolver={resolver}
|
|
61
130
|
keysToOmit={keysToOmit}
|
|
131
|
+
enumSchemaNames={enumSchemaNames}
|
|
62
132
|
/>
|
|
63
133
|
</>
|
|
64
134
|
)
|
|
@@ -66,50 +136,55 @@ export const typeGenerator = defineGenerator<PluginTs>({
|
|
|
66
136
|
|
|
67
137
|
const paramTypes = params.map((param) =>
|
|
68
138
|
renderSchemaType({
|
|
69
|
-
|
|
70
|
-
name: resolver.resolveParamName(
|
|
139
|
+
schema: param.schema,
|
|
140
|
+
name: resolver.resolveParamName(transformedNode, param),
|
|
71
141
|
}),
|
|
72
142
|
)
|
|
73
143
|
|
|
74
|
-
const requestType =
|
|
144
|
+
const requestType = transformedNode.requestBody?.schema
|
|
75
145
|
? renderSchemaType({
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
146
|
+
schema: {
|
|
147
|
+
...transformedNode.requestBody.schema,
|
|
148
|
+
description: transformedNode.requestBody.description ?? transformedNode.requestBody.schema.description,
|
|
149
|
+
},
|
|
150
|
+
name: resolver.resolveDataName(transformedNode),
|
|
151
|
+
keysToOmit: transformedNode.requestBody.keysToOmit,
|
|
80
152
|
})
|
|
81
153
|
: null
|
|
82
154
|
|
|
83
|
-
const responseTypes =
|
|
155
|
+
const responseTypes = transformedNode.responses.map((res) =>
|
|
84
156
|
renderSchemaType({
|
|
85
|
-
|
|
86
|
-
name: resolver.resolveResponseStatusName(
|
|
87
|
-
description: res.description,
|
|
157
|
+
schema: res.schema,
|
|
158
|
+
name: resolver.resolveResponseStatusName(transformedNode, res.statusCode),
|
|
88
159
|
keysToOmit: res.keysToOmit,
|
|
89
160
|
}),
|
|
90
161
|
)
|
|
91
162
|
|
|
92
163
|
const dataType = renderSchemaType({
|
|
93
|
-
|
|
94
|
-
name: resolver.resolveRequestConfigName(
|
|
164
|
+
schema: buildData({ ...transformedNode, parameters: params }, { resolver }),
|
|
165
|
+
name: resolver.resolveRequestConfigName(transformedNode),
|
|
95
166
|
})
|
|
96
167
|
|
|
97
168
|
const responsesType = renderSchemaType({
|
|
98
|
-
|
|
99
|
-
name: resolver.resolveResponsesName(
|
|
169
|
+
schema: buildResponses(transformedNode, { resolver }),
|
|
170
|
+
name: resolver.resolveResponsesName(transformedNode),
|
|
100
171
|
})
|
|
101
172
|
|
|
102
173
|
const responseType = renderSchemaType({
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
174
|
+
schema: transformedNode.responses.some((res) => res.schema)
|
|
175
|
+
? {
|
|
176
|
+
...buildResponseUnion(transformedNode, { resolver })!,
|
|
177
|
+
description: 'Union of all possible responses',
|
|
178
|
+
}
|
|
179
|
+
: null,
|
|
180
|
+
name: resolver.resolveResponseName(transformedNode),
|
|
106
181
|
})
|
|
107
182
|
|
|
108
183
|
return (
|
|
109
184
|
<File
|
|
110
|
-
baseName={file.baseName}
|
|
111
|
-
path={file.path}
|
|
112
|
-
meta={file.meta}
|
|
185
|
+
baseName={meta.file.baseName}
|
|
186
|
+
path={meta.file.path}
|
|
187
|
+
meta={meta.file.meta}
|
|
113
188
|
banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
|
|
114
189
|
footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
|
|
115
190
|
>
|
|
@@ -122,56 +197,4 @@ export const typeGenerator = defineGenerator<PluginTs>({
|
|
|
122
197
|
</File>
|
|
123
198
|
)
|
|
124
199
|
},
|
|
125
|
-
Schema({ node, adapter, options, config, resolver }) {
|
|
126
|
-
const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, transformers = [] } = options
|
|
127
|
-
|
|
128
|
-
const root = path.resolve(config.root, config.output.path)
|
|
129
|
-
const mode = getMode(path.resolve(root, output.path))
|
|
130
|
-
|
|
131
|
-
if (!node.name) {
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const transformedNode = transform(node, composeTransformers(...transformers))
|
|
136
|
-
|
|
137
|
-
const imports = adapter.getImports(transformedNode, (schemaName) => ({
|
|
138
|
-
name: resolver.default(schemaName, 'type'),
|
|
139
|
-
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
140
|
-
}))
|
|
141
|
-
|
|
142
|
-
const isEnumSchema = !!narrowSchema(node, schemaTypes.enum)
|
|
143
|
-
|
|
144
|
-
const name = ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolver.resolveName(node.name)
|
|
145
|
-
|
|
146
|
-
const type = {
|
|
147
|
-
name,
|
|
148
|
-
file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
|
|
149
|
-
} as const
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<File
|
|
153
|
-
baseName={type.file.baseName}
|
|
154
|
-
path={type.file.path}
|
|
155
|
-
meta={type.file.meta}
|
|
156
|
-
banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
|
|
157
|
-
footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
|
|
158
|
-
>
|
|
159
|
-
{mode === 'split' &&
|
|
160
|
-
imports.map((imp) => (
|
|
161
|
-
<File.Import key={[node.name, imp.path, imp.isTypeOnly].join('-')} root={type.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
162
|
-
))}
|
|
163
|
-
<Type
|
|
164
|
-
name={type.name}
|
|
165
|
-
node={transformedNode}
|
|
166
|
-
enumType={enumType}
|
|
167
|
-
enumTypeSuffix={enumTypeSuffix}
|
|
168
|
-
enumKeyCasing={enumKeyCasing}
|
|
169
|
-
optionalType={optionalType}
|
|
170
|
-
arrayType={arrayType}
|
|
171
|
-
syntaxType={syntaxType}
|
|
172
|
-
resolver={resolver}
|
|
173
|
-
/>
|
|
174
|
-
</File>
|
|
175
|
-
)
|
|
176
|
-
},
|
|
177
200
|
})
|