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

400 lines 16.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaExtractor = void 0; exports.extractSchemasFromSource = extractSchemasFromSource; exports.extractSchemasFromFile = extractSchemasFromFile; exports.extractSchemasFromDirectory = extractSchemasFromDirectory; exports.extractSchemasFromGlob = extractSchemasFromGlob; exports.extractWithMetadata = extractWithMetadata; exports.extractAndWriteZodSchemasFromSource = extractAndWriteZodSchemasFromSource; exports.extractAndWriteZodSchemasFromFile = extractAndWriteZodSchemasFromFile; exports.extractAndWriteZodSchemasFromDirectory = extractAndWriteZodSchemasFromDirectory; exports.extractAndWriteZodSchemasFromGlob = extractAndWriteZodSchemasFromGlob; const fs_1 = require("fs"); const path_1 = require("path"); const source_loader_1 = require("./source-loader"); const type_resolver_1 = require("./type-resolver"); const type_parser_1 = require("./type-parser"); const supports_runtime_validation_checker_1 = require("./supports-runtime-validation-checker"); const zod_schema_generator_1 = require("./zod-schema-generator"); const schema_file_writer_1 = require("./schema-file-writer"); /** * SchemaExtractor - Integrated pipeline for extracting constraint schemas * from types that extend SupportsRuntimeValidation */ class SchemaExtractor { constructor(project, sourceFiles) { this.project = project; this.sourceFiles = sourceFiles; this.typeResolver = type_resolver_1.TypeResolver.fromProject(project, sourceFiles); // Create a combined source for the type parser const combinedSource = this.createCombinedSource(); this.typeParser = new type_parser_1.TypeParser(combinedSource); } /** * Main extraction method - extracts schemas from types that extend SupportsRuntimeValidation */ static async extract(input, options = {}) { const startTime = Date.now(); const extractor = await this.createExtractor(input, options); const typeDeclarations = extractor.discoverAllTypes(); const totalTypesFound = typeDeclarations.length; const validTypes = typeDeclarations.filter((t) => t.extendsSupportsRuntimeValidation); const typesWithSupportsRuntimeValidation = validTypes.length; const schemas = await extractor.extractSchemasFromTypes(validTypes, options.includeSourceInfo); const processingTime = Date.now() - startTime; return { schemas, totalTypesFound, typesWithSupportsRuntimeValidation, processingTime, }; } /** * Extract only the schemas without metadata */ static async extractSchemas(input, options = {}) { const result = await this.extract(input, options); return result.schemas; } /** * Extract schema for a single specific type */ static async extractSingleSchema(input, typeName, options = {}) { const extractor = await this.createExtractor(input, options); const typeDeclarations = extractor.discoverAllTypes(); const targetType = typeDeclarations.find((t) => t.name === typeName); if (!targetType) { return null; } if (!targetType.extendsSupportsRuntimeValidation) { return null; } const schemas = await extractor.extractSchemasFromTypes([targetType], options.includeSourceInfo); return schemas[0] || null; } /** * Get all types that extend SupportsRuntimeValidation without extracting schemas */ static async getValidationTypes(input, options = {}) { const extractor = await this.createExtractor(input, options); const typeDeclarations = extractor.discoverAllTypes(); return typeDeclarations .filter((t) => t.extendsSupportsRuntimeValidation) .map((t) => t.name); } /** * Extract and generate Zod schemas from types that extend SupportsRuntimeValidation */ static async extractZodSchemas(input, options = {}) { const extractedSchemas = await this.extractSchemas(input, options); return zod_schema_generator_1.ZodSchemaGenerator.generateSchemas(extractedSchemas, options.includeSourceInfo ?? true); } /** * Extract and generate a single Zod schema for a specific type */ static async extractSingleZodSchema(input, typeName, options = {}) { const extractedSchema = await this.extractSingleSchema(input, typeName, options); if (!extractedSchema) { return null; } return zod_schema_generator_1.ZodSchemaGenerator.generateSingle(extractedSchema, options.includeSourceInfo ?? true); } /** * Extract Zod schemas and write them to disk */ static async extractAndWriteZodSchemas(input, options = {}) { // Generate Zod schemas const zodSchemas = await this.extractZodSchemas(input, options); // Write to disk if output options are provided let writtenFiles = []; if (options.output?.writeToFile !== false) { writtenFiles = await schema_file_writer_1.SchemaFileWriter.writeSchemas(zodSchemas, options.output, input // Pass the input directory for structure preservation ); } return { schemas: zodSchemas, writtenFiles, }; } /** * Create an extractor instance from input */ static async createExtractor(input, options) { const { compilerOptions, ...fileOptions } = options; // Use SourceLoader to create project with proper file discovery const { project, sourceFiles } = await source_loader_1.SourceLoader.createProject(input, { ...fileOptions, compilerOptions, }); return new SchemaExtractor(project, sourceFiles); } /** * Discover all type declarations across all source files */ discoverAllTypes() { const typeDeclarations = []; for (const sourceFile of this.sourceFiles) { // Get type aliases const typeAliases = sourceFile.getTypeAliases(); for (const typeAlias of typeAliases) { const name = typeAlias.getName(); const extendsSupportsRuntimeValidation = (0, supports_runtime_validation_checker_1.isTopLevelIntersectionWithSupportsRuntimeValidation)(typeAlias); typeDeclarations.push({ name, declaration: typeAlias, sourceFile, extendsSupportsRuntimeValidation, }); } // Get interfaces const interfaces = sourceFile.getInterfaces(); for (const interfaceDecl of interfaces) { const name = interfaceDecl.getName(); const extendsSupportsRuntimeValidation = (0, supports_runtime_validation_checker_1.isTopLevelIntersectionWithSupportsRuntimeValidation)(interfaceDecl); typeDeclarations.push({ name, declaration: interfaceDecl, sourceFile, extendsSupportsRuntimeValidation, }); } } return typeDeclarations; } /** * Extract schemas from a list of type declarations */ async extractSchemasFromTypes(typeDeclarations, includeSourceInfo = false) { const schemas = []; for (const typeDecl of typeDeclarations) { try { // Use the type parser directly on the combined source to preserve constraints // The type parser can handle the original type definition with all constraints intact const schema = this.typeParser.parseType(typeDecl.name); // Check if schema is empty (which happens with computed types like Pick, Omit, etc.) const hasProperties = Object.keys(schema).length > 0; if (!hasProperties) { // Schema is empty, likely a computed type - fall back to type resolution throw new Error(`Empty schema for ${typeDecl.name}, falling back to type resolution`); } const extractedSchema = { typeName: typeDecl.name, schema, }; // Add source info if requested if (includeSourceInfo) { const sourceFile = typeDecl.sourceFile; const declaration = typeDecl.declaration; extractedSchema.sourceInfo = { filePath: sourceFile.getFilePath(), line: declaration.getStartLineNumber(), }; } schemas.push(extractedSchema); } catch (error) { // If direct parsing fails, try with type resolution for computed types try { const resolvedTypeString = this.typeResolver.getResolvedType(typeDecl.name); // Create a temporary source with the resolved type for parsing const tempSource = ` ${this.getImportsAndConstraintTypes()} type ${typeDecl.name} = ${resolvedTypeString}; `; // Parse the resolved type to extract schema const tempParser = new type_parser_1.TypeParser(tempSource); const schema = tempParser.parseType(typeDecl.name); const extractedSchema = { typeName: typeDecl.name, schema, }; // Add source info if requested if (includeSourceInfo) { const sourceFile = typeDecl.sourceFile; const declaration = typeDecl.declaration; extractedSchema.sourceInfo = { filePath: sourceFile.getFilePath(), line: declaration.getStartLineNumber(), }; } schemas.push(extractedSchema); } catch (fallbackError) { // Log error but continue processing other types console.warn(`Failed to extract schema for type ${typeDecl.name}:`, error, fallbackError); } } } return schemas; } /** * Create combined source code from all source files for the type parser */ createCombinedSource() { const sources = []; // Add constraint types first sources.push(this.getImportsAndConstraintTypes()); // Add content from all source files for (const sourceFile of this.sourceFiles) { const content = sourceFile.getFullText(); const filePath = sourceFile.getFilePath(); sources.push(`// File: ${filePath}\n${content}`); } return sources.join("\n\n"); } /** * Get the constraint types and imports needed for parsing * Dynamically loads from constraint-types.ts file */ getImportsAndConstraintTypes() { try { // Try to load constraint types from the constraint-types.ts file in src directory const constraintTypesPath = (0, path_1.join)(__dirname, "..", "src", "constraint-types.ts"); const constraintTypesContent = (0, fs_1.readFileSync)(constraintTypesPath, "utf-8"); // Add a comment header and return the content return ` // Constraint types loaded from constraint-types.ts ${constraintTypesContent} `; } catch (error) { // Try alternative path (same directory) try { const altPath = (0, path_1.join)(__dirname, "constraint-types.ts"); const constraintTypesContent = (0, fs_1.readFileSync)(altPath, "utf-8"); return ` // Constraint types loaded from constraint-types.ts ${constraintTypesContent} `; } catch (altError) { // Fallback to hardcoded constraint types if file cannot be loaded console.warn("Could not load constraint-types.ts from any location, using fallback constraint types"); return this.getFallbackConstraintTypes(); } } } /** * Get fallback constraint types when the file cannot be loaded */ getFallbackConstraintTypes() { return ` // Fallback constraint types type SupportsRuntimeValidation = {}; type Constraint<K extends any> = {}; type Min<N extends number> = Constraint<N>; type Max<N extends number> = Constraint<N>; type MinLength<N extends number> = Constraint<N>; type MaxLength<N extends number> = Constraint<N>; type Email = Constraint<string>; type UUID = Constraint<string>; type Regex<P extends string> = Constraint<P>; type Phone = Constraint<string>; type CreditCard = Constraint<string>; type URL = Constraint<string>; type Date = Constraint<string>; type JSON = Constraint<string>; type Base64 = Constraint<string>; type IPAddress = Constraint<string>; type Integer = Constraint<number>; type Positive = Constraint<number>; type Negative = Constraint<number>; type NonEmpty = Constraint<string>; type OneOf<T extends readonly string[]> = Constraint<T>; type Range<Min extends number, Max extends number> = Constraint<{ min: Min; max: Max }>; type FileSize<N extends number> = Constraint<N>; type MimeType<T extends string> = Constraint<T>; `; } /** * Get statistics about the types in the project */ async getTypeStatistics() { const typeDeclarations = this.discoverAllTypes(); const validationTypes = typeDeclarations.filter((t) => t.extendsSupportsRuntimeValidation); const typesByFile = {}; const validationTypesByFile = {}; for (const typeDecl of typeDeclarations) { const filePath = typeDecl.sourceFile.getFilePath(); typesByFile[filePath] = (typesByFile[filePath] || 0) + 1; } for (const typeDecl of validationTypes) { const filePath = typeDecl.sourceFile.getFilePath(); validationTypesByFile[filePath] = (validationTypesByFile[filePath] || 0) + 1; } return { totalTypes: typeDeclarations.length, typesWithSupportsRuntimeValidation: validationTypes.length, typesByFile, validationTypesByFile, }; } } exports.SchemaExtractor = SchemaExtractor; // Convenience functions for common use cases /** * Extract schemas from source code string */ async function extractSchemasFromSource(sourceCode, options) { return SchemaExtractor.extractSchemas(sourceCode, options); } /** * Extract schemas from a file */ async function extractSchemasFromFile(filePath, options) { return SchemaExtractor.extractSchemas(filePath, options); } /** * Extract schemas from a directory */ async function extractSchemasFromDirectory(dirPath, options) { return SchemaExtractor.extractSchemas(dirPath, options); } /** * Extract schemas using glob patterns */ async function extractSchemasFromGlob(dirPath, glob, exclude, options) { return SchemaExtractor.extractSchemas(dirPath, { ...options, glob, exclude, }); } /** * Get full extraction results with metadata */ async function extractWithMetadata(input, options) { return SchemaExtractor.extract(input, options); } // New convenience functions for writing schemas to disk /** * Extract and write Zod schemas from source code string */ async function extractAndWriteZodSchemasFromSource(sourceCode, options) { return SchemaExtractor.extractAndWriteZodSchemas(sourceCode, options); } /** * Extract and write Zod schemas from a file */ async function extractAndWriteZodSchemasFromFile(filePath, options) { return SchemaExtractor.extractAndWriteZodSchemas(filePath, options); } /** * Extract and write Zod schemas from a directory */ async function extractAndWriteZodSchemasFromDirectory(dirPath, options) { return SchemaExtractor.extractAndWriteZodSchemas(dirPath, options); } /** * Extract and write Zod schemas using glob patterns */ async function extractAndWriteZodSchemasFromGlob(dirPath, glob, exclude, options) { return SchemaExtractor.extractAndWriteZodSchemas(dirPath, { ...options, glob, exclude, }); } //# sourceMappingURL=schema-extractor.js.map