@pesto-io/zod-reify
Version:
Pesto module to intantiate zod schema from Typescript source code as string.
235 lines (234 loc) • 7.31 kB
TypeScript
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;
}