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