@kubb/plugin-zod 5.0.0-beta.10 → 5.0.0-beta.15

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.
@@ -1,20 +1,27 @@
1
1
  import type { Adapter } from '@kubb/core'
2
2
  import { ast, defineGenerator } from '@kubb/core'
3
3
  import type { AdapterOas } from '@kubb/adapter-oas'
4
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
4
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
5
5
  import { Operations } from '../components/Operations.tsx'
6
6
  import { Zod } from '../components/Zod.tsx'
7
7
  import { ZOD_NAMESPACE_IMPORTS } from '../constants.ts'
8
8
  import { printerZod } from '../printers/printerZod.ts'
9
9
  import { printerZodMini } from '../printers/printerZodMini.ts'
10
- import type { PluginZod } from '../types'
10
+ import type { PluginZod, ResolverZod } from '../types'
11
11
  import { buildSchemaNames } from '../utils.ts'
12
12
 
13
+ type ZodPrinterEntry = { printer: ReturnType<typeof printerZod>; coercion: unknown; guidType: unknown; dateType: unknown }
14
+ type ZodMiniPrinterEntry = { printer: ReturnType<typeof printerZodMini>; guidType: unknown }
15
+
16
+ // Per-build caches: keyed on resolver (unique per plugin instance per build, GC'd when released)
17
+ const zodPrinterCache = new WeakMap<ResolverZod, ZodPrinterEntry>()
18
+ const zodMiniPrinterCache = new WeakMap<ResolverZod, ZodMiniPrinterEntry>()
19
+
13
20
  export const zodGenerator = defineGenerator<PluginZod>({
14
21
  name: 'zod',
15
- renderer: jsxRenderer,
22
+ renderer: jsxRendererSync,
16
23
  schema(node, ctx) {
17
- const { adapter, config, resolver, root } = ctx
24
+ const { adapter, config, resolver, root, inputNode } = ctx
18
25
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options
19
26
  const dateType = (adapter as Adapter<AdapterOas>).options.dateType
20
27
 
@@ -37,19 +44,35 @@ export const zodGenerator = defineGenerator<PluginZod>({
37
44
 
38
45
  const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : undefined
39
46
 
40
- const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : undefined
47
+ const cyclicSchemas = ast.findCircularSchemas(inputNode.schemas)
41
48
 
42
- const schemaPrinter = mini
43
- ? printerZodMini({ guidType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
44
- : printerZod({ coercion, guidType, dateType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
49
+ const schemaPrinter = mini ? getCachedMiniPrinter() : getCachedStdPrinter()
50
+ function getCachedStdPrinter() {
51
+ const cached = zodPrinterCache.get(resolver)
52
+ if (cached && cached.coercion === coercion && cached.guidType === guidType && cached.dateType === dateType) {
53
+ return cached.printer
54
+ }
55
+ const p = printerZod({ coercion, guidType, dateType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
56
+ zodPrinterCache.set(resolver, { printer: p, coercion, guidType, dateType })
57
+ return p
58
+ }
59
+ function getCachedMiniPrinter() {
60
+ const cached = zodMiniPrinterCache.get(resolver)
61
+ if (cached && cached.guidType === guidType) {
62
+ return cached.printer
63
+ }
64
+ const p = printerZodMini({ guidType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
65
+ zodMiniPrinterCache.set(resolver, { printer: p, guidType })
66
+ return p
67
+ }
45
68
 
46
69
  return (
47
70
  <File
48
71
  baseName={meta.file.baseName}
49
72
  path={meta.file.path}
50
73
  meta={meta.file.meta}
51
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
52
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
74
+ banner={resolver.resolveBanner(inputNode, { output, config })}
75
+ footer={resolver.resolveFooter(inputNode, { output, config })}
53
76
  >
54
77
  <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
55
78
  {mode === 'split' && imports.map((imp) => <File.Import key={[node.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
@@ -59,7 +82,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
59
82
  )
60
83
  },
61
84
  operation(node, ctx) {
62
- const { adapter, config, resolver, root } = ctx
85
+ const { adapter, config, resolver, root, inputNode } = ctx
63
86
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options
64
87
  const dateType = (adapter as Adapter<AdapterOas>).options.dateType
65
88
 
@@ -72,7 +95,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
72
95
  file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
73
96
  } as const
74
97
 
75
- const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : undefined
98
+ const cyclicSchemas = ast.findCircularSchemas(inputNode.schemas)
76
99
 
77
100
  function renderSchemaEntry({ schema, name, keysToOmit }: { schema: ast.SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
78
101
  if (!schema) return null
@@ -84,9 +107,19 @@ export const zodGenerator = defineGenerator<PluginZod>({
84
107
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
85
108
  }))
86
109
 
110
+ const cachedStd = zodPrinterCache.get(resolver)
111
+ const cachedMini = zodMiniPrinterCache.get(resolver)
87
112
  const schemaPrinter = mini
88
- ? printerZodMini({ guidType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
89
- : printerZod({ coercion, guidType, dateType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
113
+ ? keysToOmit?.length
114
+ ? printerZodMini({ guidType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
115
+ : cachedMini?.guidType === guidType
116
+ ? cachedMini.printer
117
+ : printerZodMini({ guidType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
118
+ : keysToOmit?.length
119
+ ? printerZod({ coercion, guidType, dateType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
120
+ : cachedStd?.coercion === coercion && cachedStd?.guidType === guidType && cachedStd?.dateType === dateType
121
+ ? cachedStd.printer
122
+ : printerZod({ coercion, guidType, dateType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
90
123
 
91
124
  return (
92
125
  <>
@@ -159,8 +192,8 @@ export const zodGenerator = defineGenerator<PluginZod>({
159
192
  baseName={meta.file.baseName}
160
193
  path={meta.file.path}
161
194
  meta={meta.file.meta}
162
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
163
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
195
+ banner={resolver.resolveBanner(inputNode, { output, config })}
196
+ footer={resolver.resolveFooter(inputNode, { output, config })}
164
197
  >
165
198
  <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
166
199
  {paramSchemas}
@@ -171,7 +204,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
171
204
  )
172
205
  },
173
206
  operations(nodes, ctx) {
174
- const { adapter, config, resolver, root } = ctx
207
+ const { config, resolver, root, inputNode } = ctx
175
208
  const { output, importPath, group, operations, paramsCasing } = ctx.options
176
209
 
177
210
  if (!operations) {
@@ -204,8 +237,8 @@ export const zodGenerator = defineGenerator<PluginZod>({
204
237
  baseName={meta.file.baseName}
205
238
  path={meta.file.path}
206
239
  meta={meta.file.meta}
207
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
208
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
240
+ banner={resolver.resolveBanner(inputNode, { output, config })}
241
+ footer={resolver.resolveFooter(inputNode, { output, config })}
209
242
  >
210
243
  <File.Import isTypeOnly name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
211
244
  {imports}
@@ -208,9 +208,12 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
208
208
  const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
209
209
  // Inside a getter the getter itself defers evaluation, so suppress
210
210
  // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
211
+ // Save before clearing: this.options === options (same reference via definePrinter),
212
+ // so reading options.cyclicSchemas after mutation would return undefined.
213
+ const savedCyclicSchemas = this.options.cyclicSchemas
211
214
  if (hasSelfRef) this.options.cyclicSchemas = undefined
212
215
  const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
213
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas
216
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
214
217
 
215
218
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
216
219
 
@@ -177,9 +177,12 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
177
177
  const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
178
178
  // Inside a getter the getter itself defers evaluation, so suppress
179
179
  // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
180
+ // Save before clearing: this.options === options (same reference via definePrinter),
181
+ // so reading options.cyclicSchemas after mutation would return undefined.
182
+ const savedCyclicSchemas = this.options.cyclicSchemas
180
183
  if (hasSelfRef) this.options.cyclicSchemas = undefined
181
184
  const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
182
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas
185
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
183
186
 
184
187
  const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
185
188