UNPKG

@pesto-io/zod-reify

Version:

Pesto module to intantiate zod schema from Typescript source code as string.

235 lines (234 loc) 7.31 kB
import { Node, ts } from "ts-morph"; export interface Reifier<X> { reify(): X; } /** * The {@ZodSchemaReifier } class will parse a string assumed to be a zod schema source code, and will instantiate the Zod Schema. */ export declare class ZodSchemaReifier implements Reifier<any> { protected zodSchemaAsString: string; /** * The unique ID of * this {@ZodSchemaReifier } instance. * * This uuid is used to generate a unique * name for the sourceFile * */ private unique_id; /** * The ts-morph API Project instance which * will used to compile/parse the typescript code which is a zodSchema */ private project; private tsConfigRootdir; /** * The filename of the file in which the source * code to process will be saved to. */ private filename; /** * The source file obeject used by the TS compiler API */ private sourceFile; /** * Represents a variable Declaration Statement * which is assigned as value, a zodSchema. * * E.g.: * * const doesntMatter = z.object({ * title: z.string(), * tags: z.array(z.string()), * image: z.string().optional(), * }) */ /** * The ts-morph / TypeScript Compiler API * Variable Declaration of the Zod Schema, in the * built ts-morph Project * * Represents a variable Declaration Statement * which is assigned as value, a zodSchema. * * E.g.: * * <code> * import { z } from "zod"; * * //// * // Below that's the variable declaration represented: * //// * const weDontCare = z.object({ * // ... * }) * </code> */ private zodSchemaVarDeclaration; /** * The ts-morph / TypeScript Compiler API type checker */ private typeChecker; /** * Represents the full statement importing zod: * * <code> * // the zodImport is the full below line. * import { z } from "zod"; * </code> * * The {@ZodSchemaReifier } will always use * 'import { z } from "zod";' as the zod import, * since it is not provided by the user of the {@ZodSchemaReifier } class. * * Why? because we don't care what is * the zod import, as long as the zod is imported, we * care about instiating the Zod Schema. * */ private zodImport; /** * The name of the zod import. * * E.g.: * * <code> * // the name of the zod import is 'z' * import { z } from "zod"; * </code> * * <code> * // the name of the zod import is 'myZod' * import { z as myZod } from "zod"; * </code> * * The {@ZodSchemaReifier } will always use 'z' as * the name of the zod import, since it is not * provided by the user of the {@ZodSchemaReifier } class. * * Why? because we don't care what is * the name of the zod import, we * care about instiating the Zod Schema. * */ private nameOfTheZodImport; /** * * Example values of <pre>zodSchemaAsString</pre> : * --- * * Example 1: * ---------- * * <code> * z.object({ * title: z.string(), * subtitle: z.string(), * summary: z.string(), * category: z.array(z.string()), * image: z.string(), * tags: z.array(z.string()), * }) * </code> * * Example 2: * ---------- * * <code> * z.object({ * title: z.string(), * hereAnother: z.object({ * reseau: z.object({ * cesar: z.array(z.string()), * }), * imLackingIdea: z.boolean().optional(), * itsForATest: z.boolean(), * }), * tags: z.array(z.string( ) ), * another: z.boolean().nullish(), * exampleCategory: z.array(z.array(z.string().nullable())).optional(), * example2Category: z.nullable(z.array(z.boolean())).optional(), * example3Category: z.optional(z.number()).array(), * example4Category: z.array(z.number()).optional(), * image: z.string().optional(), * somethingElseNested: z.object({ * firstname: z.string().array(), * lastname: z.string(), * color: z.string(), * two: z.boolean().optional(), * three: z.number().array().optional(), * four: z.array(z.number()).optional(), * }), * department: z.object({ * divisionName: z.string(), * secrecyTags: z.array(z.string()).optional(), * }), * }) * </code> * * @param zodSchemaAsString the text of the zod schema, without any variable declaration, just the zod schema alone. see above example. * @param p_tsConfigRootdir the path to the folder used to set the <pre>rootDir</pre> TypeScript compiler configuration property value. (typically found in any <pre>tsconfig.json</pre> file) */ constructor(zodSchemaAsString: string, p_tsConfigRootdir?: string); /** * Represents the zod instance in the source file: * * Eg. if in the source file, we have: * * <code> * import z form 'zod'; * * const weDontCare = z.array(z.string()).optional(); * </code> * * then, <pre>this.zodExpressionNode</pre> represents * the object returned by <pre>z.array(z.string()).optional()</pre> */ private zodExpressionNode; private initZodExpressionNode; /** * This method will be a full reccurence: * * @param aZodExpressionNode A node in the terminology of the ts-morph / TypeScript Compiler API * @returns the reified typescript object, returned by the zod expression */ reify(aZodExpressionNode?: Node<ts.Node>): any; /** * Reifies a new expression node. * @param className The name of the class to reify. Supported classes are: Date, Set, Array (oters in the future ... ?) * @param constructorArg * @returns The reified object returned by the call of the constructor of the <code>className</code> Class. */ private reifyZodNewExpressionWithLessOneOrZeroArg; private reifyZodFunctionCallWithTwoArgs; private reifyArrayLiteralExpression; /** * Ok i could determine it is this function which has a bug, especially when a property is itself an object literal * @param processedNode * @returns */ private reifyObjectLiteralExpression; private reifyZodFunctionCallWithOneArg; /** * This method reifies the call of a zod method called without arguments * * <pre>caller.calledFunctionName()</pre> * * @param caller * @param calledFunctionName the zod function name, a * @returns the object returned by the function call */ private reifyNoArgsZodFunctionCall; /** * This method validates that the * source code in the source file built * based on the constructor-provided * string <pre>zodSchemaAsString</pre>, * assumed to be a zod schema, sucessfully * compiles with the TypeScript Compiler. * - * https://ts-morph.com/setup/diagnostics * @throws an Error if the source code does not compile as TypeScript source code */ private validate; }