11import { z } from 'zod'
2- import { escape , escapeJsDoc, parseKey } from '@traversable/registry'
2+ import { escapeCharCodes , escapeJsDoc, parseKey } from '@traversable/registry'
33import { hasTypeName, tagged, F, hasOptional, Invariant } from '@traversable/zod-types'
44import { Json } from '@traversable/json'
55
6+ const GRAVE_CHAR_CODE = 96
7+
68export type WithOptionalTypeName = {
79 /**
810 * ## {@link WithOptionalTypeName `toType.Options.typeName`}
@@ -145,7 +147,7 @@ function preserveJsDocsEnabled(ix: F.CompilerIndex) {
145147}
146148
147149function stringifyLiteral(value: unknown) {
148- return typeof value === 'string' ? `"${escape (value)}"` : typeof value === 'bigint' ? `${value}n` : `${value}`
150+ return typeof value === 'string' ? `"${escapeCharCodes (value, GRAVE_CHAR_CODE )}"` : typeof value === 'bigint' ? `${value}n` : `${value}`
149151}
150152
151153const stringifyExample = Json.fold<string>((x) => {
@@ -163,7 +165,7 @@ const stringifyExample = Json.fold<string>((x) => {
163165 }
164166})
165167
166- const readonly = (x: F.Z.Readonly<string>, ix: F.CompilerIndex, input: z.ZodReadonly): string => {
168+ function readonly(x: F.Z.Readonly<string>, ix: F.CompilerIndex, input: z.ZodReadonly): string {
167169 const { innerType } = input._zod.def
168170 if (tagged('file', innerType)) return `Readonly<File>`
169171 else if (tagged('unknown', innerType)) return `Readonly<unknown>`
@@ -186,25 +188,57 @@ const readonly = (x: F.Z.Readonly<string>, ix: F.CompilerIndex, input: z.ZodRead
186188 else return x._zod.def.innerType
187189}
188190
189- function templateLiteralParts(parts: unknown[]): string[][] {
190- let out = [Array.of<string>()]
191+ function templateLiteralParts(parts: unknown[], out: string[][] = [Array.of<string>()]): string[][] {
191192 let x = parts[0]
192193 for (let ix = 0, len = parts.length; ix < len; (void ix++, x = parts[ix])) {
193194 switch (true) {
194195 case x === undefined: out.forEach((xs) => xs.push('')); break
195196 case x === null: out.forEach((xs) => xs.push('null')); break
196- case typeof x === 'string': out.forEach((xs) => xs.push(escape (String(x)))); break
197+ case typeof x === 'string': out.forEach((xs) => xs.push(escapeCharCodes (String(x), GRAVE_CHAR_CODE ))); break
197198 case tagged('null', x): out.forEach((xs) => xs.push('null')); break
198199 case tagged('undefined', x): out.forEach((xs) => xs.push('')); break
199200 case tagged('number', x): out.forEach((xs) => xs.push('${number}')); break
200201 case tagged('string', x): out.forEach((xs) => xs.push('${string}')); break
201202 case tagged('bigint', x): out.forEach((xs) => xs.push('${bigint}')); break
202203 case tagged('boolean', x): out = out.flatMap((xs) => [[...xs, 'true'], [...xs, 'false']]); break
203204 case tagged('literal', x): {
204- const values = x._zod.def.values.map((_) => _ === undefined ? '' : escape (String(_)))
205+ const values = x._zod.def.values.map((_) => _ === undefined ? '' : escapeCharCodes (String(_), GRAVE_CHAR_CODE ))
205206 out = out.flatMap((xs) => values.map((value) => [...xs, value]))
206207 break
207208 }
209+ case tagged('nullable', x): {
210+ const { innerType } = x._zod.def
211+ if (tagged('boolean', innerType)) {
212+ out = out.flatMap((xs) => [[...xs, 'true'], [...xs, 'false'], [...xs, 'null']])
213+ break
214+ } else if (tagged('string', innerType) && ix === 0) {
215+ out.forEach((xs) => xs.push('${string}'))
216+ break
217+ } else {
218+ out = out.flatMap((xs) => [[...xs, ...templateLiteralParts([innerType]).flat()], [...xs, 'null']])
219+ break
220+ }
221+ }
222+ case tagged('optional', x): {
223+ const { innerType } = x._zod.def
224+ if (tagged('boolean', innerType)) {
225+ out = out.flatMap((xs) => [[...xs, 'true'], [...xs, 'false'], [...xs, '']])
226+ break
227+ }
228+ else if (tagged('string', innerType)) {
229+ out.forEach((xs) => xs.push('${string}'))
230+ break
231+ }
232+ else {
233+ out = out.flatMap((xs) => [[...xs, ...templateLiteralParts([innerType]).flat()], [...xs, '']])
234+ break
235+ }
236+ }
237+ case tagged('enum', x): {
238+ const values: (string | number)[] = Object.values(x._zod.def.entries)
239+ out = out.flatMap((xs) => values.map((value) => [...xs, String(value)]))
240+ break
241+ }
208242 default: out.forEach((xs) => xs.push(String(x))); break
209243 }
210244 }
0 commit comments