@kubb/plugin-zod 0.0.0-canary-20241104172400

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/chunk-37OP7TSO.cjs +354 -0
  4. package/dist/chunk-37OP7TSO.cjs.map +1 -0
  5. package/dist/chunk-OOAUU32I.js +235 -0
  6. package/dist/chunk-OOAUU32I.js.map +1 -0
  7. package/dist/chunk-QEYWJ6VH.cjs +244 -0
  8. package/dist/chunk-QEYWJ6VH.cjs.map +1 -0
  9. package/dist/chunk-SSOO3TXR.js +347 -0
  10. package/dist/chunk-SSOO3TXR.js.map +1 -0
  11. package/dist/components.cjs +16 -0
  12. package/dist/components.cjs.map +1 -0
  13. package/dist/components.d.cts +29 -0
  14. package/dist/components.d.ts +29 -0
  15. package/dist/components.js +3 -0
  16. package/dist/components.js.map +1 -0
  17. package/dist/generators.cjs +17 -0
  18. package/dist/generators.cjs.map +1 -0
  19. package/dist/generators.d.cts +10 -0
  20. package/dist/generators.d.ts +10 -0
  21. package/dist/generators.js +4 -0
  22. package/dist/generators.js.map +1 -0
  23. package/dist/index.cjs +17 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +9 -0
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/types-L1fXCZAs.d.cts +100 -0
  30. package/dist/types-L1fXCZAs.d.ts +100 -0
  31. package/package.json +95 -0
  32. package/src/components/Operations.tsx +49 -0
  33. package/src/components/Zod.tsx +68 -0
  34. package/src/components/index.ts +2 -0
  35. package/src/generators/__snapshots__/anyof.ts +3 -0
  36. package/src/generators/__snapshots__/coercion.ts +3 -0
  37. package/src/generators/__snapshots__/coercionDates.ts +3 -0
  38. package/src/generators/__snapshots__/coercionNumbers.ts +3 -0
  39. package/src/generators/__snapshots__/coercionStrings.ts +3 -0
  40. package/src/generators/__snapshots__/createPet.ts +15 -0
  41. package/src/generators/__snapshots__/createPetWithUnknownTypeAny.ts +13 -0
  42. package/src/generators/__snapshots__/createPetWithUnknownTypeUnknown.ts +15 -0
  43. package/src/generators/__snapshots__/deletePet.ts +3 -0
  44. package/src/generators/__snapshots__/discriminator.ts +3 -0
  45. package/src/generators/__snapshots__/enumBooleanLiteral.ts +3 -0
  46. package/src/generators/__snapshots__/enumNamesType.ts +3 -0
  47. package/src/generators/__snapshots__/enumNullable.ts +3 -0
  48. package/src/generators/__snapshots__/enumSingleLiteral.ts +3 -0
  49. package/src/generators/__snapshots__/enumVarNamesType.ts +3 -0
  50. package/src/generators/__snapshots__/example.ts +3 -0
  51. package/src/generators/__snapshots__/getPets.ts +15 -0
  52. package/src/generators/__snapshots__/mixedValueTypeConst.ts +6 -0
  53. package/src/generators/__snapshots__/nullableString.ts +3 -0
  54. package/src/generators/__snapshots__/nullableStringUuid.ts +3 -0
  55. package/src/generators/__snapshots__/nullableStringWithAnyOf.ts +3 -0
  56. package/src/generators/__snapshots__/numberValueConst.ts +6 -0
  57. package/src/generators/__snapshots__/oneof.ts +3 -0
  58. package/src/generators/__snapshots__/operations.ts +43 -0
  59. package/src/generators/__snapshots__/optionalPetInfer.ts +5 -0
  60. package/src/generators/__snapshots__/optionalPetTyped.ts +3 -0
  61. package/src/generators/__snapshots__/order.ts +3 -0
  62. package/src/generators/__snapshots__/orderDateTyeString.ts +10 -0
  63. package/src/generators/__snapshots__/orderDateTypeFalse.ts +3 -0
  64. package/src/generators/__snapshots__/orderDateTypeString.ts +3 -0
  65. package/src/generators/__snapshots__/pet.ts +3 -0
  66. package/src/generators/__snapshots__/petArray.ts +6 -0
  67. package/src/generators/__snapshots__/petCoercion.ts +3 -0
  68. package/src/generators/__snapshots__/petTupleObject.ts +6 -0
  69. package/src/generators/__snapshots__/petWithMapper.ts +3 -0
  70. package/src/generators/__snapshots__/pets.ts +3 -0
  71. package/src/generators/__snapshots__/recursive.ts +3 -0
  72. package/src/generators/__snapshots__/showPetById.ts +15 -0
  73. package/src/generators/__snapshots__/stringValueConst.ts +6 -0
  74. package/src/generators/__snapshots__/uuidSchema.ts +3 -0
  75. package/src/generators/index.ts +2 -0
  76. package/src/generators/operationsGenerator.tsx +39 -0
  77. package/src/generators/zodGenerator.tsx +121 -0
  78. package/src/index.ts +2 -0
  79. package/src/parser/index.ts +381 -0
  80. package/src/plugin.ts +139 -0
  81. package/src/types.ts +106 -0
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const order = z.object({ "id": z.number().int().optional(), "petId": z.number().int().optional(), "quantity": z.number().int().optional(), "shipDate": z.string().optional(), "status": z.enum(["placed", "approved", "delivered"]).describe("Order Status").optional(), "complete": z.boolean().optional() });
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const order = z.object({ "id": z.number().int().optional(), "petId": z.number().int().optional(), "quantity": z.number().int().optional(), "shipDate": z.string().datetime().optional(), "status": z.enum(["placed", "approved", "delivered"]).describe("Order Status").optional(), "complete": z.boolean().optional() });
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const pet = z.object({ "id": z.number().int(), "name": z.string(), "date": z.date().optional(), "tag": z.string().min(5).max(100).optional() });
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * @description List of Pet object
5
+ */
6
+ export const petArray = z.array(z.lazy(() => pet)).min(1).max(3).describe("List of Pet object");
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const pet = z.object({ "id": z.coerce.number().int(), "name": z.coerce.string(), "date": z.coerce.date().optional(), "tag": z.coerce.string().min(5).max(100).optional() });
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * @description Tuple of exact length 2 nested in an object
5
+ */
6
+ export const petTupleObject = z.object({ "tupleProperty": z.tuple([z.string(), z.string()]).optional() }).describe("Tuple of exact length 2 nested in an object");
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const pet = z.object({ "id": z.number().int(), "name": z.string().email(), "date": z.date().optional(), "tag": z.string().min(5).max(100).optional() });
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const pets = z.array(z.object({ "id": z.number().int(), "name": z.string(), "tag": z.string().optional() })) as z.ZodType<Pets>;
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const example = z.object({ "name": z.string(), "children": z.array(z.lazy(() => example)) });
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+
3
+ export const showPetByIdPathParams = z.object({ "petId": z.string().describe("The id of the pet to retrieve"), "testId": z.string().describe("The id of the pet to retrieve") });
4
+
5
+ /**
6
+ * @description Expected response to a valid request
7
+ */
8
+ export const showPetById200 = z.lazy(() => pet);
9
+
10
+ /**
11
+ * @description unexpected error
12
+ */
13
+ export const showPetByIdError = z.lazy(() => error);
14
+
15
+ export const showPetByIdQueryResponse = z.lazy(() => showPetById200);
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * @description its value is equal to the value of the keyword
5
+ */
6
+ export const stringValueConst = z.object({ "foobar": z.literal("foobar") }).describe("its value is equal to the value of the keyword");
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+
3
+ export const uuidSchema = z.string().uuid();
@@ -0,0 +1,2 @@
1
+ export { zodGenerator } from './zodGenerator.tsx'
2
+ export { operationsGenerator } from './operationsGenerator.tsx'
@@ -0,0 +1,39 @@
1
+ import { createReactGenerator } from '@kubb/plugin-oas'
2
+ import { useOperationManager } from '@kubb/plugin-oas/hooks'
3
+ import { File, useApp } from '@kubb/react'
4
+ import { Operations } from '../components/Operations'
5
+ import { pluginZodName } from '../plugin.ts'
6
+ import type { PluginZod } from '../types'
7
+
8
+ export const operationsGenerator = createReactGenerator<PluginZod>({
9
+ name: 'operations',
10
+ Operations({ operations }) {
11
+ const {
12
+ pluginManager,
13
+ plugin: {
14
+ options: { output },
15
+ },
16
+ } = useApp<PluginZod>()
17
+ const { getFile, groupSchemasByName } = useOperationManager()
18
+
19
+ const name = 'operations'
20
+ const file = pluginManager.getFile({ name, extname: '.ts', pluginKey: [pluginZodName] })
21
+
22
+ const transformedOperations = operations.map((operation) => ({ operation, data: groupSchemasByName(operation, { type: 'function' }) }))
23
+
24
+ const imports = Object.entries(transformedOperations)
25
+ .map(([key, { data, operation }]) => {
26
+ const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean)
27
+
28
+ return <File.Import key={key} name={names} root={file.path} path={getFile(operation).path} />
29
+ })
30
+ .filter(Boolean)
31
+
32
+ return (
33
+ <File baseName={file.baseName} path={file.path} meta={file.meta} banner={output?.banner} footer={output?.footer}>
34
+ {imports}
35
+ <Operations name={name} operations={transformedOperations} />
36
+ </File>
37
+ )
38
+ },
39
+ })
@@ -0,0 +1,121 @@
1
+ import { type OperationSchema as OperationSchemaType, SchemaGenerator, createReactGenerator, schemaKeywords } from '@kubb/plugin-oas'
2
+ import { Oas } from '@kubb/plugin-oas/components'
3
+ import { useOas, useOperationManager, useSchemaManager } from '@kubb/plugin-oas/hooks'
4
+ import { pluginTsName } from '@kubb/plugin-ts'
5
+ import { File, useApp } from '@kubb/react'
6
+ import { Zod } from '../components'
7
+ import type { PluginZod } from '../types'
8
+
9
+ export const zodGenerator = createReactGenerator<PluginZod>({
10
+ name: 'zod',
11
+ Operation({ operation, options }) {
12
+ const { coercion, inferred, typed, mapper } = options
13
+
14
+ const { plugin, pluginManager, mode } = useApp<PluginZod>()
15
+ const oas = useOas()
16
+ const { getSchemas, getFile } = useOperationManager()
17
+ const schemaManager = useSchemaManager()
18
+
19
+ const file = getFile(operation)
20
+ const schemas = getSchemas(operation)
21
+ const schemaGenerator = new SchemaGenerator(options, {
22
+ oas,
23
+ plugin,
24
+ pluginManager,
25
+ mode,
26
+ override: options.override,
27
+ })
28
+
29
+ const operationSchemas = [schemas.pathParams, schemas.queryParams, schemas.headerParams, schemas.statusCodes, schemas.request, schemas.response]
30
+ .flat()
31
+ .filter(Boolean)
32
+
33
+ const mapOperationSchema = ({ name, schema, description, keysToOmit, ...options }: OperationSchemaType, i: number) => {
34
+ // hack so Params can be optional when needed
35
+ const required = Array.isArray(schema?.required) ? !!schema.required.length : !!schema?.required
36
+ const optional = !required && !!name.includes('Params')
37
+ const tree = [...schemaGenerator.parse({ schema, name }), optional ? { keyword: schemaKeywords.optional } : undefined].filter(Boolean)
38
+ const imports = schemaManager.getImports(tree)
39
+
40
+ const zod = {
41
+ name: schemaManager.getName(name, { type: 'function' }),
42
+ inferTypeName: schemaManager.getName(name, { type: 'type' }),
43
+ file: schemaManager.getFile(name),
44
+ }
45
+
46
+ const type = {
47
+ name: schemaManager.getName(name, { type: 'type', pluginKey: [pluginTsName] }),
48
+ file: schemaManager.getFile(options.operationName || name, { pluginKey: [pluginTsName], tag: options.operation?.getTags()[0]?.name }),
49
+ }
50
+
51
+ return (
52
+ <Oas.Schema key={i} name={name} value={schema} tree={tree}>
53
+ {typed && <File.Import isTypeOnly root={file.path} path={type.file.path} name={[type.name]} />}
54
+ {imports.map((imp, index) => (
55
+ <File.Import key={index} root={file.path} path={imp.path} name={imp.name} />
56
+ ))}
57
+ <Zod
58
+ name={zod.name}
59
+ typeName={typed ? type.name : undefined}
60
+ inferTypeName={inferred ? zod.inferTypeName : undefined}
61
+ description={description}
62
+ tree={tree}
63
+ mapper={mapper}
64
+ coercion={coercion}
65
+ keysToOmit={keysToOmit}
66
+ />
67
+ </Oas.Schema>
68
+ )
69
+ }
70
+
71
+ return (
72
+ <File baseName={file.baseName} path={file.path} meta={file.meta} banner={plugin.options.output?.banner} footer={plugin.options.output?.footer}>
73
+ <File.Import name={['z']} path={plugin.options.importPath} />
74
+ {operationSchemas.map(mapOperationSchema)}
75
+ </File>
76
+ )
77
+ },
78
+ Schema({ schema, options }) {
79
+ const { coercion, inferred, typed, mapper, importPath } = options
80
+
81
+ const { getName, getFile, getImports } = useSchemaManager()
82
+ const {
83
+ plugin: {
84
+ options: { output },
85
+ },
86
+ } = useApp<PluginZod>()
87
+
88
+ const imports = getImports(schema.tree)
89
+
90
+ const zod = {
91
+ name: getName(schema.name, { type: 'function' }),
92
+ inferTypeName: getName(schema.name, { type: 'type' }),
93
+ file: getFile(schema.name),
94
+ }
95
+
96
+ const type = {
97
+ name: getName(schema.name, { type: 'type', pluginKey: [pluginTsName] }),
98
+ file: getFile(schema.name, { pluginKey: [pluginTsName] }),
99
+ }
100
+
101
+ return (
102
+ <File baseName={zod.file.baseName} path={zod.file.path} meta={zod.file.meta} banner={output?.banner} footer={output?.footer}>
103
+ <File.Import name={['z']} path={importPath} />
104
+ {typed && <File.Import isTypeOnly root={zod.file.path} path={type.file.path} name={[type.name]} />}
105
+ {imports.map((imp, index) => (
106
+ <File.Import key={index} root={zod.file.path} path={imp.path} name={imp.name} />
107
+ ))}
108
+
109
+ <Zod
110
+ name={zod.name}
111
+ typeName={typed ? type.name : undefined}
112
+ inferTypeName={inferred ? zod.inferTypeName : undefined}
113
+ description={schema.value.description}
114
+ tree={schema.tree}
115
+ mapper={mapper}
116
+ coercion={coercion}
117
+ />
118
+ </File>
119
+ )
120
+ },
121
+ })
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { pluginZod, pluginZodName } from './plugin.ts'
2
+ export type { PluginZod } from './types.ts'
@@ -0,0 +1,381 @@
1
+ import transformers, { createJSDocBlockText } from '@kubb/core/transformers'
2
+ import { type SchemaKeywordMapper, type SchemaTree, isKeyword, schemaKeywords } from '@kubb/plugin-oas'
3
+
4
+ import type { Schema, SchemaKeywordBase, SchemaMapper } from '@kubb/plugin-oas'
5
+
6
+ const zodKeywordMapper = {
7
+ any: () => 'z.any()',
8
+ unknown: () => 'z.unknown()',
9
+ number: (coercion?: boolean, min?: number, max?: number) => {
10
+ return [coercion ? 'z.coerce.number()' : 'z.number()', min !== undefined ? `.min(${min})` : undefined, max !== undefined ? `.max(${max})` : undefined]
11
+ .filter(Boolean)
12
+ .join('')
13
+ },
14
+ integer: (coercion?: boolean, min?: number, max?: number) => {
15
+ return [
16
+ coercion ? 'z.coerce.number().int()' : 'z.number().int()',
17
+ min !== undefined ? `.min(${min})` : undefined,
18
+ max !== undefined ? `.max(${max})` : undefined,
19
+ ]
20
+ .filter(Boolean)
21
+ .join('')
22
+ },
23
+ object: (value?: string) => `z.object({${value}})`,
24
+ string: (coercion?: boolean, min?: number, max?: number) => {
25
+ return [coercion ? 'z.coerce.string()' : 'z.string()', min !== undefined ? `.min(${min})` : undefined, max !== undefined ? `.max(${max})` : undefined]
26
+ .filter(Boolean)
27
+ .join('')
28
+ },
29
+ boolean: () => 'z.boolean()',
30
+ undefined: () => 'z.undefined()',
31
+ nullable: () => '.nullable()',
32
+ null: () => 'z.null()',
33
+ nullish: () => '.nullish()',
34
+ array: (items: string[] = [], min?: number, max?: number) => {
35
+ return [`z.array(${items?.join('')})`, min !== undefined ? `.min(${min})` : undefined, max !== undefined ? `.max(${max})` : undefined]
36
+ .filter(Boolean)
37
+ .join('')
38
+ },
39
+ tuple: (items: string[] = []) => `z.tuple([${items?.join(', ')}])`,
40
+ enum: (items: string[] = []) => `z.enum([${items?.join(', ')}])`,
41
+ union: (items: string[] = []) => `z.union([${items?.join(', ')}])`,
42
+ const: (value?: string | number | boolean) => `z.literal(${value ?? ''})`,
43
+ /**
44
+ * ISO 8601
45
+ */
46
+ datetime: (offset = false, local = false) => {
47
+ if (offset) {
48
+ return `z.string().datetime({ offset: ${offset} })`
49
+ }
50
+
51
+ if (local) {
52
+ return `z.string().datetime({ local: ${local} })`
53
+ }
54
+
55
+ return 'z.string().datetime()'
56
+ },
57
+ /**
58
+ * Type `'date'` Date
59
+ * Type `'string'` ISO date format (YYYY-MM-DD)
60
+ * @default ISO date format (YYYY-MM-DD)
61
+ */
62
+ date: (type: 'date' | 'string' = 'string', coercion?: boolean) => {
63
+ if (type === 'string') {
64
+ return 'z.string().date()'
65
+ }
66
+
67
+ if (coercion) {
68
+ return 'z.coerce.date()'
69
+ }
70
+
71
+ return 'z.date()'
72
+ },
73
+ /**
74
+ * Type `'date'` Date
75
+ * Type `'string'` ISO time format (HH:mm:ss[.SSSSSS])
76
+ * @default ISO time format (HH:mm:ss[.SSSSSS])
77
+ */
78
+ time: (type: 'date' | 'string' = 'string', coercion?: boolean) => {
79
+ if (type === 'string') {
80
+ return 'z.string().time()'
81
+ }
82
+
83
+ if (coercion) {
84
+ return 'z.coerce.date()'
85
+ }
86
+
87
+ return 'z.date()'
88
+ },
89
+ uuid: () => '.uuid()',
90
+ url: () => '.url()',
91
+ strict: () => '.strict()',
92
+ default: (value?: string | number | true) => `.default(${value ?? ''})`,
93
+ and: (items: string[] = []) => items?.map((item) => `.and(${item})`).join(''),
94
+ describe: (value = '') => `.describe(${value})`,
95
+ min: (value?: number) => `.min(${value ?? ''})`,
96
+ max: (value?: number) => `.max(${value ?? ''})`,
97
+ optional: () => '.optional()',
98
+ matches: (value = '') => `.regex(${value})`,
99
+ email: () => '.email()',
100
+ firstName: undefined,
101
+ lastName: undefined,
102
+ password: undefined,
103
+ phone: undefined,
104
+ readOnly: undefined,
105
+ writeOnly: undefined,
106
+ ref: (value?: string) => (value ? `z.lazy(() => ${value})` : undefined),
107
+ blob: () => 'z.string()',
108
+ deprecated: undefined,
109
+ example: undefined,
110
+ schema: undefined,
111
+ catchall: (value?: string) => (value ? `.catchall(${value})` : undefined),
112
+ name: undefined,
113
+ } satisfies SchemaMapper<string | null | undefined>
114
+
115
+ /**
116
+ * @link based on https://github.com/cellular/oazapfts/blob/7ba226ebb15374e8483cc53e7532f1663179a22c/src/codegen/generate.ts#L398
117
+ */
118
+
119
+ export function sort(items?: Schema[]): Schema[] {
120
+ const order: string[] = [
121
+ schemaKeywords.string,
122
+ schemaKeywords.datetime,
123
+ schemaKeywords.date,
124
+ schemaKeywords.time,
125
+ schemaKeywords.tuple,
126
+ schemaKeywords.number,
127
+ schemaKeywords.object,
128
+ schemaKeywords.enum,
129
+ schemaKeywords.url,
130
+ schemaKeywords.email,
131
+ schemaKeywords.firstName,
132
+ schemaKeywords.lastName,
133
+ schemaKeywords.password,
134
+ schemaKeywords.matches,
135
+ schemaKeywords.uuid,
136
+ schemaKeywords.min,
137
+ schemaKeywords.max,
138
+ schemaKeywords.default,
139
+ schemaKeywords.describe,
140
+ schemaKeywords.optional,
141
+ schemaKeywords.nullable,
142
+ schemaKeywords.nullish,
143
+ schemaKeywords.null,
144
+ ]
145
+
146
+ if (!items) {
147
+ return []
148
+ }
149
+
150
+ return transformers.orderBy(items, [(v) => order.indexOf(v.keyword)], ['asc'])
151
+ }
152
+
153
+ const shouldCoerce = (coercion: ParserOptions['coercion'] | undefined, type: 'dates' | 'strings' | 'numbers'): boolean => {
154
+ if (coercion === undefined) {
155
+ return false
156
+ }
157
+ if (typeof coercion === 'boolean') {
158
+ return coercion
159
+ }
160
+
161
+ return !!coercion[type]
162
+ }
163
+
164
+ type ParserOptions = {
165
+ name: string
166
+ typeName?: string
167
+ description?: string
168
+ keysToOmit?: string[]
169
+ mapper?: Record<string, string>
170
+ coercion?: boolean | { dates?: boolean; strings?: boolean; numbers?: boolean }
171
+ }
172
+
173
+ export function parse({ parent, current, siblings }: SchemaTree, options: ParserOptions): string | undefined {
174
+ const value = zodKeywordMapper[current.keyword as keyof typeof zodKeywordMapper]
175
+
176
+ if (!value) {
177
+ return undefined
178
+ }
179
+
180
+ if (isKeyword(current, schemaKeywords.union)) {
181
+ // zod union type needs at least 2 items
182
+ if (Array.isArray(current.args) && current.args.length === 1) {
183
+ return parse({ parent, current: current.args[0] as Schema, siblings }, options)
184
+ }
185
+ if (Array.isArray(current.args) && !current.args.length) {
186
+ return ''
187
+ }
188
+
189
+ return zodKeywordMapper.union(
190
+ sort(current.args)
191
+ .map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
192
+ .filter(Boolean),
193
+ )
194
+ }
195
+
196
+ if (isKeyword(current, schemaKeywords.and)) {
197
+ const items = sort(current.args)
198
+ .filter((schema: Schema) => {
199
+ return ![schemaKeywords.optional, schemaKeywords.describe].includes(schema.keyword as typeof schemaKeywords.describe)
200
+ })
201
+ .map((schema: Schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
202
+ .filter(Boolean)
203
+
204
+ return `${items.slice(0, 1)}${zodKeywordMapper.and(items.slice(1))}`
205
+ }
206
+
207
+ if (isKeyword(current, schemaKeywords.array)) {
208
+ return zodKeywordMapper.array(
209
+ sort(current.args.items)
210
+ .map((schemas, _index, siblings) => parse({ parent: current, current: schemas, siblings }, options))
211
+ .filter(Boolean),
212
+ current.args.min,
213
+ current.args.max,
214
+ )
215
+ }
216
+
217
+ if (isKeyword(current, schemaKeywords.enum)) {
218
+ if (current.args.asConst) {
219
+ if (current.args.items.length === 1) {
220
+ const child = {
221
+ keyword: schemaKeywords.const,
222
+ args: current.args.items[0],
223
+ }
224
+ return parse({ parent: current, current: child, siblings: [child] }, options)
225
+ }
226
+
227
+ return zodKeywordMapper.union(
228
+ current.args.items
229
+ .map((schema) => ({
230
+ keyword: schemaKeywords.const,
231
+ args: schema,
232
+ }))
233
+ .map((schema, _index, siblings) => {
234
+ return parse({ parent: current, current: schema, siblings }, options)
235
+ })
236
+ .filter(Boolean),
237
+ )
238
+ }
239
+
240
+ return zodKeywordMapper.enum(
241
+ current.args.items.map((schema) => {
242
+ if (schema.format === 'boolean') {
243
+ return transformers.stringify(schema.value)
244
+ }
245
+
246
+ if (schema.format === 'number') {
247
+ return transformers.stringify(schema.value)
248
+ }
249
+ return transformers.stringify(schema.value)
250
+ }),
251
+ )
252
+ }
253
+
254
+ if (isKeyword(current, schemaKeywords.ref)) {
255
+ return zodKeywordMapper.ref(current.args?.name)
256
+ }
257
+
258
+ if (isKeyword(current, schemaKeywords.object)) {
259
+ const properties = Object.entries(current.args?.properties || {})
260
+ .filter((item) => {
261
+ const schema = item[1]
262
+ return schema && typeof schema.map === 'function'
263
+ })
264
+ .map(([name, schemas]) => {
265
+ const nameSchema = schemas.find((schema) => schema.keyword === schemaKeywords.name) as SchemaKeywordMapper['name']
266
+ const mappedName = nameSchema?.args || name
267
+
268
+ // custom mapper(pluginOptions)
269
+ if (options.mapper?.[mappedName]) {
270
+ return `"${name}": ${options.mapper?.[mappedName]}`
271
+ }
272
+
273
+ return `"${name}": ${sort(schemas)
274
+ .map((schema, array, siblings) => {
275
+ return parse({ parent: current, current: schema, siblings }, options)
276
+ })
277
+ .filter(Boolean)
278
+ .join('')}`
279
+ })
280
+ .join(',')
281
+
282
+ const additionalProperties = current.args?.additionalProperties?.length
283
+ ? current.args.additionalProperties
284
+ .map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
285
+ .filter(Boolean)
286
+ .join('')
287
+ : undefined
288
+
289
+ const text = [
290
+ zodKeywordMapper.object(properties),
291
+ current.args?.strict ? zodKeywordMapper.strict() : undefined,
292
+ additionalProperties ? zodKeywordMapper.catchall(additionalProperties) : undefined,
293
+ ].filter(Boolean)
294
+
295
+ return text.join('')
296
+ }
297
+
298
+ if (isKeyword(current, schemaKeywords.tuple)) {
299
+ return zodKeywordMapper.tuple(
300
+ current.args.items.map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options)).filter(Boolean),
301
+ )
302
+ }
303
+
304
+ if (isKeyword(current, schemaKeywords.const)) {
305
+ if (current.args.format === 'number' && current.args.value !== undefined) {
306
+ return zodKeywordMapper.const(Number.parseInt(current.args.value?.toString()))
307
+ }
308
+
309
+ if (current.args.format === 'boolean' && current.args.value !== undefined) {
310
+ return zodKeywordMapper.const(current.args.value)
311
+ }
312
+ return zodKeywordMapper.const(transformers.stringify(current.args.value))
313
+ }
314
+
315
+ if (isKeyword(current, schemaKeywords.matches)) {
316
+ if (current.args) {
317
+ return zodKeywordMapper.matches(transformers.toRegExpString(current.args))
318
+ }
319
+ }
320
+
321
+ if (isKeyword(current, schemaKeywords.default)) {
322
+ if (current.args) {
323
+ return zodKeywordMapper.default(current.args)
324
+ }
325
+ }
326
+
327
+ if (isKeyword(current, schemaKeywords.describe)) {
328
+ if (current.args) {
329
+ return zodKeywordMapper.describe(transformers.stringify(current.args.toString()))
330
+ }
331
+ }
332
+
333
+ if (isKeyword(current, schemaKeywords.string)) {
334
+ return zodKeywordMapper.string(shouldCoerce(options.coercion, 'strings'))
335
+ }
336
+
337
+ if (isKeyword(current, schemaKeywords.number)) {
338
+ return zodKeywordMapper.number(shouldCoerce(options.coercion, 'numbers'))
339
+ }
340
+
341
+ if (isKeyword(current, schemaKeywords.integer)) {
342
+ return zodKeywordMapper.integer(shouldCoerce(options.coercion, 'numbers'))
343
+ }
344
+
345
+ if (isKeyword(current, schemaKeywords.min)) {
346
+ return zodKeywordMapper.min(current.args)
347
+ }
348
+ if (isKeyword(current, schemaKeywords.max)) {
349
+ return zodKeywordMapper.max(current.args)
350
+ }
351
+
352
+ if (isKeyword(current, schemaKeywords.datetime)) {
353
+ return zodKeywordMapper.datetime(current.args.offset, current.args.local)
354
+ }
355
+
356
+ if (isKeyword(current, schemaKeywords.date)) {
357
+ return zodKeywordMapper.date(current.args.type, shouldCoerce(options.coercion, 'dates'))
358
+ }
359
+
360
+ if (isKeyword(current, schemaKeywords.time)) {
361
+ return zodKeywordMapper.time(current.args.type, shouldCoerce(options.coercion, 'dates'))
362
+ }
363
+
364
+ if (current.keyword in zodKeywordMapper && 'args' in current) {
365
+ const value = zodKeywordMapper[current.keyword as keyof typeof zodKeywordMapper] as (typeof zodKeywordMapper)['const']
366
+
367
+ return value((current as SchemaKeywordBase<unknown>).args as any)
368
+ }
369
+
370
+ if (isKeyword(current, schemaKeywords.optional)) {
371
+ if (siblings.some((schema) => isKeyword(schema, schemaKeywords.default))) return ''
372
+
373
+ return value()
374
+ }
375
+
376
+ if (current.keyword in zodKeywordMapper) {
377
+ return value()
378
+ }
379
+
380
+ return undefined
381
+ }