UNPKG

prisma-zod-generator

Version:

Prisma 2+ generator to emit Zod schemas from your Prisma schema

242 lines 9.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigParseError = void 0; exports.discoverConfigFile = discoverConfigFile; exports.validateFileExists = validateFileExists; exports.readConfigFile = readConfigFile; exports.parseJsonConfig = parseJsonConfig; exports.resolveConfigPath = resolveConfigPath; exports.parseConfigFromFile = parseConfigFromFile; exports.parseConfigFromDiscovery = parseConfigFromDiscovery; exports.parseConfiguration = parseConfiguration; exports.createConfigErrorMessage = createConfigErrorMessage; const fs_1 = require("fs"); const path_1 = require("path"); const logger_1 = require("../utils/logger"); /** * Configuration parsing error */ class ConfigParseError extends Error { constructor(message, cause, filePath) { super(message); this.cause = cause; this.filePath = filePath; this.name = 'ConfigParseError'; } } exports.ConfigParseError = ConfigParseError; /** * Discover configuration file in standard locations */ async function discoverConfigFile(baseDir = process.cwd()) { const configFiles = [ 'zod-generator.config.json', 'zod-generator.config.js', '.zod-generator.json', '.zod-generator.js', ]; for (const filename of configFiles) { const configPath = (0, path_1.resolve)(baseDir, filename); try { await fs_1.promises.access(configPath); return configPath; } catch { // File doesn't exist, continue searching } } return null; } /** * Validate that a file exists and is readable */ async function validateFileExists(filePath) { try { const stats = await fs_1.promises.stat(filePath); if (!stats.isFile()) { throw new ConfigParseError(`Configuration path exists but is not a file: ${filePath}`); } } catch (error) { if (error.code === 'ENOENT') { throw new ConfigParseError(`Configuration file not found at resolved path: ${filePath}. ` + `Please verify the file exists or remove the config attribute to use defaults.`); } throw new ConfigParseError(`Cannot access configuration file: ${filePath}`, error, filePath); } } /** * Read configuration file content from disk */ async function readConfigFile(filePath) { try { return await fs_1.promises.readFile(filePath, 'utf-8'); } catch (error) { throw new ConfigParseError(`Failed to read configuration file: ${filePath}`, error, filePath); } } /** * Parse JSON configuration content */ function parseJsonConfig(content, filePath) { try { const parsed = JSON.parse(content); if (typeof parsed !== 'object' || parsed === null) { throw new ConfigParseError('Configuration must be a JSON object', undefined, filePath); } // Transform legacy format to new format const transformedConfig = transformLegacyConfig(parsed); return transformedConfig; } catch (error) { if (error instanceof ConfigParseError) { throw error; } throw new ConfigParseError(`Invalid JSON in configuration file${filePath ? `: ${filePath}` : ''}`, error, filePath); } } function transformLegacyConfig(config) { const base = (typeof config === 'object' && config !== null ? config : {}); const transformed = { ...base, }; // Handle legacy Include/Select options if (base.addSelectType !== undefined) { logger_1.logger.debug(`🔄 Preserving legacy addSelectType: ${String(base.addSelectType)}`); transformed.addSelectType = Boolean(base.addSelectType); } if (base.addIncludeType !== undefined) { logger_1.logger.debug(`🔄 Preserving legacy addIncludeType: ${String(base.addIncludeType)}`); transformed.addIncludeType = Boolean(base.addIncludeType); } // Transform model-specific legacy fields.exclude to variants format if (transformed.models) { const models = transformed.models; Object.keys(models).forEach((modelName) => { var _a, _b; const modelConfig = models[modelName]; if (((_a = modelConfig.fields) === null || _a === void 0 ? void 0 : _a.exclude) && modelConfig.fields.exclude.length > 0) { logger_1.logger.debug(`🔄 Transforming legacy fields.exclude for model ${modelName}:`, modelConfig.fields.exclude); // Ensure variants object exists and work with a local, typed reference const variants = ((_b = modelConfig.variants) !== null && _b !== void 0 ? _b : {}); // ensure modelConfig keeps the same reference modelConfig.variants = variants; // Apply fields.exclude to all variants ['pure', 'input', 'result'].forEach((variant) => { var _a, _b, _c; const vKey = variant; if (!variants[vKey]) { variants[vKey] = {}; } // Merge with existing excludeFields if any const existingExcludes = ((_a = variants[vKey]) === null || _a === void 0 ? void 0 : _a.excludeFields) || []; const legacyExcludes = (_c = (_b = modelConfig.fields) === null || _b === void 0 ? void 0 : _b.exclude) !== null && _c !== void 0 ? _c : []; variants[vKey].excludeFields = [ ...new Set([...existingExcludes, ...legacyExcludes]), ]; }); // Preserve legacy fields.exclude so it can apply to custom array-based variants too // (Do not delete here; generator will honor it for all variants.) logger_1.logger.debug(`✅ Transformed model ${modelName} variants (preserving fields.exclude for compatibility):`, modelConfig.variants); } }); } return transformed; } /** * Resolve configuration file path (handle relative paths) */ function resolveConfigPath(configPath, baseDir = process.cwd()) { if ((0, path_1.isAbsolute)(configPath)) { return configPath; } return (0, path_1.resolve)(baseDir, configPath); } /** * Parse configuration from file path */ async function parseConfigFromFile(configPath, baseDir) { const resolvedPath = resolveConfigPath(configPath, baseDir); logger_1.logger.debug(`🔍 Attempting to load config from: ${resolvedPath}`); // Validate file exists try { await validateFileExists(resolvedPath); logger_1.logger.debug(`✅ Config file exists at: ${resolvedPath}`); } catch (error) { logger_1.logger.debug(`❌ Config file validation failed for: ${resolvedPath}`); throw error; } // Read file content const content = await readConfigFile(resolvedPath); logger_1.logger.debug(`📖 Successfully read config file: ${resolvedPath} (${content.length} characters)`); // Parse JSON content const config = parseJsonConfig(content, resolvedPath); logger_1.logger.debug(`✅ Successfully parsed config from: ${resolvedPath}`); return { config, configPath: resolvedPath, isDefault: false, }; } /** * Auto-discover and parse configuration file */ async function parseConfigFromDiscovery(baseDir) { const discoveredPath = await discoverConfigFile(baseDir); if (!discoveredPath) { return { config: {}, isDefault: true, }; } return parseConfigFromFile(discoveredPath, baseDir); } /** * Main configuration parser function * * @param configPath - Optional path to configuration file * @param baseDir - Base directory for resolving relative paths * @returns Parsed configuration result */ async function parseConfiguration(configPath, baseDir) { try { if (configPath) { // Explicit config path provided return await parseConfigFromFile(configPath, baseDir); } else { // Auto-discover configuration return await parseConfigFromDiscovery(baseDir); } } catch (error) { if (error instanceof ConfigParseError) { throw error; } throw new ConfigParseError('Unexpected error while parsing configuration', error, configPath); } } /** * Create a detailed error message for configuration parsing failures */ function createConfigErrorMessage(error) { let message = `Configuration Error: ${error.message}`; if (error.filePath) { message += `\n File: ${error.filePath}`; } if (error.cause) { message += `\n Cause: ${error.cause.message}`; } // Add helpful suggestions message += '\n\nTroubleshooting:'; message += '\n - Ensure the configuration file exists and is readable'; message += '\n - Verify the JSON syntax is valid'; message += '\n - Check file permissions'; if (!error.filePath) { message += '\n - Consider creating a zod-generator.config.json file'; } return message; } //# sourceMappingURL=parser.js.map