UNPKG

prisma-zod-generator

Version:

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

353 lines 15.3 kB
"use strict"; /** * Extended generator options for Prisma Zod Generator * * This module provides parsing and validation for both existing and new * generator options while maintaining backward compatibility. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GeneratorOptionError = void 0; exports.parseGeneratorOptions = parseGeneratorOptions; exports.validateGeneratorOptions = validateGeneratorOptions; exports.generatorOptionsToConfigOverrides = generatorOptionsToConfigOverrides; exports.isLegacyUsage = isLegacyUsage; exports.getLegacyMigrationSuggestions = getLegacyMigrationSuggestions; const logger_1 = require("../utils/logger"); /** * Parse and validate generator options from Prisma config */ function parseGeneratorOptions(generatorConfig = {}) { const options = { raw: { ...generatorConfig }, }; // Parse config file path if (generatorConfig.config !== undefined) { options.config = parseConfigPath(generatorConfig.config); } // Parse minimal mode flag if (generatorConfig.minimal !== undefined) { options.minimal = parseBoolean(generatorConfig.minimal, 'minimal'); } // Parse variants list if (generatorConfig.variants) { options.variants = parseVariantsList(generatorConfig.variants); } // Parse file output mode if (generatorConfig.useMultipleFiles !== undefined) { options.useMultipleFiles = parseBoolean(generatorConfig.useMultipleFiles, 'useMultipleFiles'); } if (generatorConfig.singleFileName !== undefined) { options.singleFileName = generatorConfig.singleFileName; } if (generatorConfig.placeSingleFileAtRoot !== undefined) { options.placeSingleFileAtRoot = parseBoolean(generatorConfig.placeSingleFileAtRoot, 'placeSingleFileAtRoot'); } if (generatorConfig.pureModelsLean !== undefined) { options.pureModelsLean = parseBoolean(generatorConfig.pureModelsLean, 'pureModelsLean'); } if (generatorConfig.pureModelsIncludeRelations !== undefined) { options.pureModelsIncludeRelations = parseBoolean(generatorConfig.pureModelsIncludeRelations, 'pureModelsIncludeRelations'); } if (generatorConfig.pureModelsExcludeCircularRelations !== undefined) { options.pureModelsExcludeCircularRelations = parseBoolean(generatorConfig.pureModelsExcludeCircularRelations, 'pureModelsExcludeCircularRelations'); } if (generatorConfig.dateTimeStrategy !== undefined) { const v = generatorConfig.dateTimeStrategy; if (!['date', 'coerce', 'isoString'].includes(v)) { throw new GeneratorOptionError('dateTimeStrategy', v, 'Must be one of "date", "coerce", "isoString"'); } options.dateTimeStrategy = v; } if (generatorConfig.dateTimeSplitStrategy !== undefined) { options.dateTimeSplitStrategy = parseBoolean(generatorConfig.dateTimeSplitStrategy, 'dateTimeSplitStrategy'); } if (generatorConfig.jsonSchemaCompatible !== undefined) { options.jsonSchemaCompatible = parseBoolean(generatorConfig.jsonSchemaCompatible, 'jsonSchemaCompatible'); } if (generatorConfig.jsonSchemaOptions !== undefined) { options.jsonSchemaOptions = parseJsonObjectOption(generatorConfig.jsonSchemaOptions, 'jsonSchemaOptions'); } if (generatorConfig.optionalFieldBehavior !== undefined) { const v = generatorConfig.optionalFieldBehavior; if (!['optional', 'nullable', 'nullish'].includes(v)) { throw new GeneratorOptionError('optionalFieldBehavior', v, 'Must be one of "optional", "nullable", "nullish"'); } options.optionalFieldBehavior = v; } // Parse existing options for backward compatibility if (generatorConfig.isGenerateSelect !== undefined) { options.isGenerateSelect = parseBoolean(generatorConfig.isGenerateSelect, 'isGenerateSelect'); } if (generatorConfig.isGenerateInclude !== undefined) { options.isGenerateInclude = parseBoolean(generatorConfig.isGenerateInclude, 'isGenerateInclude'); } return options; } /** * Parse config file path option */ function parseConfigPath(configValue) { if (!configValue || typeof configValue !== 'string') { logger_1.logger.info('❌ [prisma-zod-generator] Invalid configuration:\n' + ` config = "${configValue}"\n` + ' Error: Config path must be a non-empty string.\n' + ' Solution: Remove the config attribute entirely to use defaults, or provide a valid path.'); throw new GeneratorOptionError('config', configValue, 'Config path must be a non-empty string'); } const trimmed = configValue.trim(); if (trimmed.length === 0) { logger_1.logger.info('❌ [prisma-zod-generator] Invalid configuration:\n' + ' config = ""\n' + ' Error: Config path cannot be empty or whitespace only.\n' + ' Solution: Remove the config attribute entirely to use defaults, or provide a valid path.'); throw new GeneratorOptionError('config', configValue, 'Config path cannot be empty or whitespace only. If you want to disable config file loading, remove the config attribute entirely.'); } return trimmed; } /** * Parse boolean option value */ function parseBoolean(value, optionName) { if (typeof value !== 'string') { throw new GeneratorOptionError(optionName, value, `${optionName} must be a string ("true" or "false")`); } const lowercased = value.toLowerCase().trim(); if (lowercased === 'true') { return true; } else if (lowercased === 'false') { return false; } else { throw new GeneratorOptionError(optionName, value, `${optionName} must be "true" or "false", got "${value}"`); } } /** * Parse JSON object option */ function parseJsonObjectOption(value, optionName) { if (typeof value !== 'string' || value.trim() === '') { throw new GeneratorOptionError(optionName, value, `${optionName} must be a JSON string`); } try { const obj = JSON.parse(value); if (obj && typeof obj === 'object' && !Array.isArray(obj)) return obj; } catch { // fall through } throw new GeneratorOptionError(optionName, value, `${optionName} must be a valid JSON object string`); } /** * Parse variants list option */ function parseVariantsList(variantsValue) { if (!variantsValue || typeof variantsValue !== 'string') { throw new GeneratorOptionError('variants', variantsValue, 'Variants must be a comma-separated string'); } const variants = variantsValue .split(',') .map((v) => v.trim()) .filter((v) => v.length > 0); if (variants.length === 0) { throw new GeneratorOptionError('variants', variantsValue, 'Variants list cannot be empty'); } // Validate each variant const validVariants = ['pure', 'input', 'result']; const invalidVariants = variants.filter((v) => !validVariants.includes(v)); if (invalidVariants.length > 0) { throw new GeneratorOptionError('variants', variantsValue, `Invalid variants: ${invalidVariants.join(', ')}. Valid variants are: ${validVariants.join(', ')}`); } // Remove duplicates return Array.from(new Set(variants)); } /** * Validate generator options compatibility */ function validateGeneratorOptions(options) { // Check for conflicting options if (options.minimal && options.variants) { // In minimal mode, only certain variants make sense const minimalCompatibleVariants = ['pure', 'input']; const incompatibleVariants = options.variants.filter((v) => !minimalCompatibleVariants.includes(v)); if (incompatibleVariants.length > 0) { const msg = `[prisma-zod-generator] ⚠️ In minimal mode, variants ${incompatibleVariants.join(', ')} may not be useful. Consider using only: ${minimalCompatibleVariants.join(', ')}`; logger_1.logger.debug(msg); } } // Validate config path if provided if (options.config) { validateConfigPath(options.config); } } /** * Validate config file path */ function validateConfigPath(configPath) { // Basic path validation if (configPath.includes('..') && !configPath.startsWith('./')) { const msg = `[prisma-zod-generator] ⚠️ Config path "${configPath}" contains ".." - ensure this is intentional and secure`; logger_1.logger.debug(msg); } // Check for common config file extensions const validExtensions = ['.json', '.js', '.ts']; const hasValidExtension = validExtensions.some((ext) => configPath.endsWith(ext)); if (!hasValidExtension) { const msg = `[prisma-zod-generator] ⚠️ Config path "${configPath}" doesn't have a recognized extension. Expected: ${validExtensions.join(', ')}`; logger_1.logger.debug(msg); } } /** * Convert generator options to configuration overrides * * This creates a partial configuration that can be merged with * the loaded configuration file to implement option precedence. */ function generatorOptionsToConfigOverrides(options) { const overrides = {}; // Apply minimal mode override if (options.minimal !== undefined) { overrides.mode = options.minimal ? 'minimal' : undefined; if (options.minimal) { // Force-disable select/include when minimal flag is set in generator options overrides.isGenerateSelect = false; overrides.isGenerateInclude = false; } } // Apply variants override if (options.variants && options.variants.length > 0) { overrides.variants = {}; // Enable only specified variants const allVariants = ['pure', 'input', 'result']; allVariants.forEach((variant) => { var _a; if (overrides.variants) { overrides.variants[variant] = { enabled: ((_a = options.variants) === null || _a === void 0 ? void 0 : _a.includes(variant)) || false, }; } }); } // Apply file output mode overrides if (options.useMultipleFiles !== undefined) { overrides.useMultipleFiles = options.useMultipleFiles; } if (options.singleFileName) { overrides.singleFileName = options.singleFileName; } if (options.placeSingleFileAtRoot !== undefined) { overrides.placeSingleFileAtRoot = options.placeSingleFileAtRoot; } if (options.pureModelsLean !== undefined) { overrides.pureModelsLean = options.pureModelsLean; } if (options.pureModelsIncludeRelations !== undefined) { overrides.pureModelsIncludeRelations = options.pureModelsIncludeRelations; } if (options.pureModelsExcludeCircularRelations !== undefined) { overrides.pureModelsExcludeCircularRelations = options.pureModelsExcludeCircularRelations; } if (options.dateTimeStrategy) { overrides.dateTimeStrategy = options.dateTimeStrategy; } if (options.dateTimeSplitStrategy !== undefined) { overrides.dateTimeSplitStrategy = options.dateTimeSplitStrategy; } if (options.jsonSchemaCompatible !== undefined) { overrides.jsonSchemaCompatible = options.jsonSchemaCompatible; } if (options.jsonSchemaOptions) { overrides.jsonSchemaOptions = options.jsonSchemaOptions; } if (options.optionalFieldBehavior) { overrides.optionalFieldBehavior = options.optionalFieldBehavior; } return overrides; } /** * Generator option parsing error */ class GeneratorOptionError extends Error { constructor(optionName, optionValue, message) { super(`Invalid generator option "${optionName}": ${message}`); this.optionName = optionName; this.optionValue = optionValue; this.name = 'GeneratorOptionError'; } getUserFriendlyMessage() { let message = `Invalid generator option: ${this.optionName}\n`; message += `Value: ${JSON.stringify(this.optionValue)}\n`; message += `Error: ${this.message}\n\n`; // Add option-specific help switch (this.optionName) { case 'config': message += `Expected: Path to configuration file (e.g., "./zod-config.json")\n`; message += `Example:\n`; message += `generator zod {\n`; message += ` provider = "prisma-zod-generator"\n`; message += ` config = "./zod-generator.config.json"\n`; message += `}`; break; case 'minimal': message += `Expected: "true" or "false"\n`; message += `Example:\n`; message += `generator zod {\n`; message += ` provider = "prisma-zod-generator"\n`; message += ` minimal = "true"\n`; message += `}`; break; case 'variants': message += `Expected: Comma-separated list of variants: "pure", "input", "result"\n`; message += `Example:\n`; message += `generator zod {\n`; message += ` provider = "prisma-zod-generator"\n`; message += ` variants = "pure,input"\n`; message += `}`; break; default: message += `Check the documentation for valid values for "${this.optionName}"`; break; } return message; } } exports.GeneratorOptionError = GeneratorOptionError; /** * Check if generator options indicate legacy usage */ function isLegacyUsage(options) { // No new options and has legacy options or no options at all return (!options.config && !options.minimal && !options.variants && (options.isGenerateSelect !== undefined || options.isGenerateInclude !== undefined || Object.keys(options.raw).length <= 2)); // Only legacy options or empty } /** * Get migration suggestions for legacy usage */ function getLegacyMigrationSuggestions(options) { const suggestions = []; if (options.isGenerateSelect || options.isGenerateInclude) { suggestions.push('Consider migrating to configuration file for better organization:\n' + ' 1. Create zod-generator.config.json\n' + ' 2. Move isGenerateSelect/isGenerateInclude to config file\n' + ' 3. Add config = "./zod-generator.config.json" to generator block'); } return suggestions; } /* * Five exports were removed here as unreachable: createDefaultGeneratorOptions, * mergeGeneratorOptions, formatGeneratorOptions, detectOptionConflicts and * getEffectiveConfigurationSummary. None had a caller. * * mergeGeneratorOptions was worth removing rather than wiring up: it merged 5 of this * module's ~20 option fields and silently dropped the rest, so any future caller would * have lost useMultipleFiles, singleFileName, placeSingleFileAtRoot and the pureModels * flags. detectOptionConflicts duplicated warnOnFileLayoutConflicts in * prisma-generator.ts, which is the mechanism the precedence docs actually describe and is * wired up. */ //# sourceMappingURL=generator-options.js.map