UNPKG

typescript-runtime-schemas

Version:

A TypeScript schema generation tool that extracts Zod schemas from TypeScript source files with runtime validation support. Generate validation schemas directly from your existing TypeScript types with support for computed types and constraint-based valid

328 lines 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaFileWriter = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); /** * SchemaFileWriter - Handles writing Zod schemas to disk */ class SchemaFileWriter { /** * Write schemas to disk based on output options */ static async writeSchemas(schemas, options = {}, inputDir) { const outputDir = options.outputDir || "generated"; const preserveStructure = options.preserveStructure || false; // Ensure output directory exists await this.ensureDirectoryExists(outputDir); if (preserveStructure) { return this.writeStructuredSchemas(schemas, outputDir, inputDir); } else { return this.writeSingleSchemaFile(schemas, outputDir); } } /** * Write all schemas to a single schemas.zod.ts file */ static async writeSingleSchemaFile(schemas, outputDir) { const outputPath = path.join(outputDir, "schemas.zod.ts"); const content = this.generateSingleFileContent(schemas); await fs.promises.writeFile(outputPath, content, "utf-8"); return [outputPath]; } /** * Write schemas preserving directory structure */ static async writeStructuredSchemas(schemas, outputDir, inputDir) { // Group schemas by source file const schemasByFile = this.groupSchemasBySourceFile(schemas); const writtenFiles = []; for (const [sourceFile, fileSchemas] of Object.entries(schemasByFile)) { const outputPath = this.getStructuredOutputPath(sourceFile, outputDir, inputDir); // Ensure the directory exists await this.ensureDirectoryExists(path.dirname(outputPath)); const content = this.generateFileContent(fileSchemas, sourceFile); await fs.promises.writeFile(outputPath, content, "utf-8"); writtenFiles.push(outputPath); } return writtenFiles; } /** * Group schemas by their source file */ static groupSchemasBySourceFile(schemas) { const grouped = {}; for (const schema of schemas) { if (schema.sourceInfo?.filePath) { const filePath = schema.sourceInfo.filePath; if (!grouped[filePath]) { grouped[filePath] = []; } grouped[filePath].push(schema); } else { // Handle schemas without source info const unknownFile = "unknown.ts"; if (!grouped[unknownFile]) { grouped[unknownFile] = []; } grouped[unknownFile].push(schema); } } return grouped; } /** * Get the output path for a structured file */ static getStructuredOutputPath(sourceFilePath, outputDir, inputDir) { // Get the base name without extension const baseName = path.basename(sourceFilePath, path.extname(sourceFilePath)); // Try to preserve directory structure relative to current working directory const relativePath = path.relative(process.cwd(), sourceFilePath); let relativeDir = path.dirname(relativePath); // If we have an input directory, strip it from the relative path if (inputDir) { const inputRelative = path.relative(process.cwd(), inputDir); if (relativeDir.startsWith(inputRelative)) { // Remove the input directory from the path relativeDir = path.relative(inputRelative, relativeDir); } } // Create output filename const outputFileName = `${baseName}.zod.ts`; // If the relative directory is meaningful (not just '.'), preserve it if (relativeDir && relativeDir !== "." && !relativeDir.startsWith("..")) { return path.join(outputDir, relativeDir, outputFileName); } else { return path.join(outputDir, outputFileName); } } /** * Generate content for a single consolidated file */ static generateSingleFileContent(schemas) { const lines = []; // Add import lines.push("import { z } from 'zod';"); lines.push(""); // Group schemas by source file for organization const schemasByFile = this.groupSchemasBySourceFile(schemas); for (const [sourceFile, fileSchemas] of Object.entries(schemasByFile)) { if (sourceFile !== "unknown.ts") { lines.push(`// From ${sourceFile}`); } for (const schema of fileSchemas) { lines.push(this.generateSchemaExport(schema)); lines.push(""); } } return lines.join("\n"); } /** * Generate content for a single file */ static generateFileContent(schemas, sourceFile) { const lines = []; // Add import lines.push("import { z } from 'zod';"); lines.push(""); if (sourceFile && sourceFile !== "unknown.ts") { lines.push(`// Generated from ${sourceFile}`); lines.push(""); } for (const schema of schemas) { lines.push(this.generateSchemaExport(schema)); lines.push(""); } return lines.join("\n"); } /** * Generate the export statement for a schema */ static generateSchemaExport(schema) { const zodSchemaString = this.zodSchemaToString(schema.zodSchema); return `export const ${schema.typeName} = ${zodSchemaString};`; } /** * Convert a Zod schema to its string representation */ static zodSchemaToString(zodSchema) { // This is a simplified version - we'll need to implement proper serialization // For now, we'll use a basic approach that handles common cases if (zodSchema._def) { return this.serializeZodSchema(zodSchema); } // Fallback return "z.unknown()"; } /** * Serialize a Zod schema to string representation */ static serializeZodSchema(schema) { const def = schema._def; switch (def.typeName) { case "ZodObject": return this.serializeZodObject(schema); case "ZodString": return this.serializeZodString(schema); case "ZodNumber": return this.serializeZodNumber(schema); case "ZodBoolean": return "z.boolean()"; case "ZodArray": return this.serializeZodArray(schema); case "ZodOptional": return `${this.serializeZodSchema(def.innerType)}.optional()`; case "ZodUnknown": return "z.unknown()"; default: console.warn(`Unsupported Zod type: ${def.typeName}`); return "z.unknown()"; } } /** * Serialize a ZodObject */ static serializeZodObject(schema) { const shape = schema._def.shape(); const properties = []; for (const [key, value] of Object.entries(shape)) { const serializedValue = this.serializeZodSchema(value); properties.push(` ${key}: ${serializedValue}`); } if (properties.length === 0) { return "z.object({})"; } return `z.object({\n${properties.join(",\n")}\n})`; } /** * Serialize a ZodString with constraints */ static serializeZodString(schema) { let result = "z.string()"; const checks = schema._def.checks || []; for (const check of checks) { switch (check.kind) { case "min": result += `.min(${check.value})`; break; case "max": result += `.max(${check.value})`; break; case "email": result += ".email()"; break; case "uuid": result += ".uuid()"; break; case "url": result += ".url()"; break; case "regex": result += `.regex(${check.regex})`; break; case "datetime": result += ".datetime()"; break; case "ip": result += ".ip()"; break; } } return result; } /** * Serialize a ZodNumber with constraints */ static serializeZodNumber(schema) { let result = "z.number()"; const checks = schema._def.checks || []; for (const check of checks) { switch (check.kind) { case "min": result += `.min(${check.value})`; break; case "max": result += `.max(${check.value})`; break; case "int": result += ".int()"; break; case "positive": result += ".positive()"; break; case "negative": result += ".negative()"; break; } } return result; } /** * Serialize a ZodArray */ static serializeZodArray(schema) { const elementType = this.serializeZodSchema(schema._def.type); let result = `z.array(${elementType})`; const checks = schema._def.checks || []; for (const check of checks) { switch (check.kind) { case "min": result += `.min(${check.value})`; break; case "max": result += `.max(${check.value})`; break; } } return result; } /** * Ensure a directory exists, creating it if necessary */ static async ensureDirectoryExists(dirPath) { try { await fs.promises.access(dirPath); } catch (error) { await fs.promises.mkdir(dirPath, { recursive: true }); } } } exports.SchemaFileWriter = SchemaFileWriter; //# sourceMappingURL=schema-file-writer.js.map