UNPKG

prisma-zod-generator

Version:

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

794 lines (793 loc) â€ĸ 124 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generate = generate; const internals_1 = require("@prisma/internals"); const fs_1 = __importStar(require("fs")); const module_1 = require("module"); const path_1 = __importDefault(require("path")); const defaults_1 = require("./config/defaults"); const generator_options_1 = require("./config/generator-options"); const parser_1 = require("./config/parser"); const helpers_1 = require("./helpers"); const transformer_1 = __importDefault(require("./transformer")); const logger_1 = require("./utils/logger"); const safeOutputManagement_1 = require("./utils/safeOutputManagement"); const safetyConfigResolver_1 = require("./utils/safetyConfigResolver"); const strict_mode_resolver_1 = require("./utils/strict-mode-resolver"); const singleFileAggregator_1 = require("./utils/singleFileAggregator"); const writeFileSafely_1 = require("./utils/writeFileSafely"); async function generate(options) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6; try { // Parse and validate new generator options const extendedOptions = (0, generator_options_1.parseGeneratorOptions)(options.generator.config); (0, generator_options_1.validateGeneratorOptions)(extendedOptions); // Handle backward compatibility and provide migration suggestions if ((0, generator_options_1.isLegacyUsage)(extendedOptions)) { const suggestions = (0, generator_options_1.getLegacyMigrationSuggestions)(extendedOptions); if (suggestions.length > 0) { logger_1.logger.debug('â„šī¸ Prisma Zod Generator: Legacy usage detected.'); logger_1.logger.debug('Consider migrating to the new configuration system for better control:'); suggestions.forEach((suggestion) => logger_1.logger.debug(` ${suggestion}`)); logger_1.logger.debug(''); // Add blank line for readability } } // NOTE: Output path is now initialized AFTER config precedence is resolved // to allow JSON config 'output' to be respected when the generator block omits it. const prismaClientGeneratorConfig = getGeneratorConfigByProvider(options.otherGenerators, 'prisma-client-js') || getGeneratorConfigByProvider(options.otherGenerators, 'prisma-client'); if (!prismaClientGeneratorConfig) { throw new Error('Prisma Zod Generator requires either "prisma-client-js" or "prisma-client" generator to be present in your schema.prisma file.\n\n' + 'Please add one of the following to your schema.prisma:\n\n' + '// For the legacy generator:\n' + 'generator client {\n' + ' provider = "prisma-client-js"\n' + '}\n\n' + '// Or for the new generator (Prisma 6.12.0+):\n' + 'generator client {\n' + ' provider = "prisma-client"\n' + '}'); } maybeWarnOnUnsupportedPrismaVersion(options); const prismaClientDmmf = await (0, internals_1.getDMMF)({ datamodel: options.datamodel, previewFeatures: prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.previewFeatures, }); // Load and process configuration with proper precedence hierarchy: // 1. Generator options (highest priority - from Prisma schema) // 2. Config file options (medium priority) // 3. Default options (lowest priority - applied by processConfiguration) // (Output path deferred until after this merge so JSON 'output' can be honored if the // generator block omits an output attribute.) let generatorConfig; let resolvedSafetyConfig; let singleFileMode = false; let singleFileName; try { const schemaBaseDir = path_1.default.dirname(options.schemaPath); let configFileOptions = {}; // Step 1: Load config file if specified or try auto-discovery (medium priority) if (extendedOptions.config) { logger_1.logger.debug(`🔧 Config path specified: ${extendedOptions.config}`); logger_1.logger.debug(`📁 Schema base directory: ${schemaBaseDir}`); try { const parseResult = await (0, parser_1.parseConfiguration)(extendedOptions.config, schemaBaseDir); configFileOptions = parseResult.config; logger_1.logger.debug(`📋 Successfully loaded configuration from: ${parseResult.configPath || 'discovered file'}`); } catch (configError) { if (configError instanceof Error) { const resolvedPath = path_1.default.isAbsolute(extendedOptions.config) ? extendedOptions.config : path_1.default.resolve(schemaBaseDir, extendedOptions.config); console.warn(`âš ī¸ Configuration loading failed:\n` + ` Specified path: ${extendedOptions.config}\n` + ` Resolved path: ${resolvedPath}\n` + ` Error: ${configError.message}\n` + ` Falling back to defaults.`); logger_1.logger.debug(`🔍 Config error details:`, configError); } throw configError; // Re-throw to be handled by outer catch block } } else { // Try auto-discovery and specific paths try { const parseResult = await (0, parser_1.parseConfiguration)(undefined, schemaBaseDir); if (!parseResult.isDefault) { configFileOptions = parseResult.config; logger_1.logger.debug(`📋 Auto-discovered configuration from: ${parseResult.configPath || 'discovered file'}`); } else { // Try specific paths for config.json const specificPaths = [ './prisma/config.json', './config.json', './zod-generator.config.json', ]; for (const path of specificPaths) { try { const parseResult = await (0, parser_1.parseConfiguration)(path, schemaBaseDir); configFileOptions = parseResult.config; logger_1.logger.debug(`📋 Found configuration at: ${path}`); break; } catch { // Continue to next path } } } } catch { logger_1.logger.debug(`📋 No configuration file found, using defaults`); } } // Step 2: Apply generator option overrides (highest priority) const generatorOptionOverrides = (0, generator_options_1.generatorOptionsToConfigOverrides)(extendedOptions); // Warn about file layout conflicts to prevent surprises warnOnFileLayoutConflicts(configFileOptions, generatorOptionOverrides); // Step 3: Merge with proper precedence (generator options override config file options) const mergedConfig = mergeConfigurationWithPrecedence(configFileOptions, generatorOptionOverrides); // Preserve config file output if still unset after overrides if (!('output' in mergedConfig) && 'output' in configFileOptions && configFileOptions.output) { mergedConfig.output = configFileOptions.output; logger_1.logger.debug('[debug] applied configFileOptions.output fallback'); } logger_1.logger.debug(`[debug] mergedConfig.naming preset=${(_a = mergedConfig.naming) === null || _a === void 0 ? void 0 : _a.preset}`); // Step 4: Process final configuration with defaults (lowest priority) const availableModels = prismaClientDmmf.datamodel.models.map((m) => m.name); const modelFieldInfo = {}; prismaClientDmmf.datamodel.models.forEach((model) => { modelFieldInfo[model.name] = model.fields.map((field) => field.name); }); generatorConfig = (0, defaults_1.processConfiguration)(mergedConfig, availableModels, modelFieldInfo); logger_1.logger.debug(`[debug] post-process generatorConfig.naming preset=${(_b = generatorConfig.naming) === null || _b === void 0 ? void 0 : _b.preset}`); logger_1.logger.debug(`[debug] generatorConfig.output=${generatorConfig.output}`); // Log configuration precedence information logConfigurationPrecedence(extendedOptions, configFileOptions, generatorOptionOverrides); logger_1.logger.debug(`[debug] generatorConfig.output (post-merge/process) = ${generatorConfig.output}`); // --- Safety Configuration Resolution --- const generatorSafetyConfig = (0, safetyConfigResolver_1.parseSafetyConfigFromGeneratorOptions)(options.generator.config || {}); const envSafetyConfig = (0, safetyConfigResolver_1.parseSafetyConfigFromEnvironment)(); const fileSafetyConfig = generatorConfig.safety || {}; // Merge safety configs with precedence: environment > generator options > config file > defaults const mergedSafetyConfig = (0, safetyConfigResolver_1.mergeSafetyConfigs)(fileSafetyConfig, generatorSafetyConfig, envSafetyConfig); resolvedSafetyConfig = (0, safetyConfigResolver_1.resolveSafetyConfig)(mergedSafetyConfig); logger_1.logger.debug(`[debug] resolvedSafetyConfig = ${JSON.stringify(resolvedSafetyConfig)}`); // --- Single File Mode Configuration --- singleFileMode = generatorConfig.useMultipleFiles === false; singleFileName = singleFileMode ? (generatorConfig.singleFileName || 'schemas.ts').trim() : undefined; // --- Output Path Resolution (replaces earlier immediate initialization) --- // Precedence for output now: // 1. Prisma generator block 'output' attribute (if provided) // 2. JSON config 'output' (if provided) // 3. Built-in default from processed configuration try { const schemaBaseDir = path_1.default.dirname(options.schemaPath); const prismaBlockOutput = options.generator.output; // Heuristic: parse schema.prisma to see if generator zod block explicitly contains an output = line let zodBlockHasExplicitOutput = false; try { const dm = options.datamodel; const blockMatch = dm.match(/generator\s+zod\s+{([\s\S]*?)}/m); if (blockMatch) { const blockBody = blockMatch[1]; zodBlockHasExplicitOutput = /\boutput\b\s*=/.test(blockBody); } } catch { } const userSpecifiedOutput = zodBlockHasExplicitOutput; if (prismaBlockOutput && userSpecifiedOutput) { // Resolve generator block output relative to the schema directory // to ensure per-test output paths are respected const raw = (0, internals_1.parseEnvValue)(prismaBlockOutput); const resolved = path_1.default.isAbsolute(raw) ? raw : path_1.default.join(schemaBaseDir, raw); await fs_1.promises.mkdir(resolved, { recursive: true }); const manifest = await (0, safeOutputManagement_1.safeCleanupOutput)(resolved, resolvedSafetyConfig, singleFileMode, singleFileName); transformer_1.default.setOutputPath(resolved); transformer_1.default.setCurrentManifest(manifest); } else if (generatorConfig.output) { // New behavior: allow JSON config to supply output when block omits it const resolved = path_1.default.isAbsolute(generatorConfig.output) ? generatorConfig.output : path_1.default.join(schemaBaseDir, generatorConfig.output); await fs_1.promises.mkdir(resolved, { recursive: true }); const manifest = await (0, safeOutputManagement_1.safeCleanupOutput)(resolved, resolvedSafetyConfig, singleFileMode, singleFileName); transformer_1.default.setOutputPath(resolved); transformer_1.default.setCurrentManifest(manifest); logger_1.logger.debug(`[prisma-zod-generator] â„šī¸ Using JSON config output path: ${resolved}`); } else { // Fallback (should rarely happen because processConfiguration sets default) const fallback = path_1.default.join(path_1.default.dirname(options.schemaPath), 'generated'); await fs_1.promises.mkdir(fallback, { recursive: true }); const manifest = await (0, safeOutputManagement_1.safeCleanupOutput)(fallback, resolvedSafetyConfig, singleFileMode, singleFileName); transformer_1.default.setOutputPath(fallback); transformer_1.default.setCurrentManifest(manifest); logger_1.logger.debug(`[prisma-zod-generator] â„šī¸ Using fallback output path: ${fallback}`); } } catch (outputInitError) { logger_1.logger.debug(`[prisma-zod-generator] âš ī¸ Failed to initialize output path: ${String(outputInitError)}`); throw outputInitError; } } catch (configError) { logger_1.logger.debug(`[prisma-generator] Caught config error: ${configError}`); logger_1.logger.debug(`[prisma-generator] Error type: ${(_c = configError === null || configError === void 0 ? void 0 : configError.constructor) === null || _c === void 0 ? void 0 : _c.name}`); logger_1.logger.debug(`[prisma-generator] Error message: ${configError === null || configError === void 0 ? void 0 : configError.message}`); // Only catch file not found errors - let validation errors bubble up const isFileNotFoundError = configError instanceof Error && configError.message.includes('Configuration file not found'); logger_1.logger.debug(`[prisma-generator] Is file not found error: ${isFileNotFoundError}`); if (isFileNotFoundError) { const baseDir = path_1.default.dirname(options.schemaPath); const configPath = extendedOptions.config || ''; const resolvedPath = path_1.default.isAbsolute(configPath) ? configPath : path_1.default.resolve(baseDir, configPath); const msg = `[prisma-zod-generator] âš ī¸ Configuration loading failed:\n` + ` Specified path: ${configPath}\n` + ` Resolved path: ${resolvedPath}\n` + ` Error: Configuration file not found\n` + ` Using defaults instead.`; logger_1.logger.info(msg); logger_1.logger.debug(`[prisma-generator] Warned about file not found, falling back to defaults`); // Fall back to defaults for file not found errors generatorConfig = (0, defaults_1.processConfiguration)({}); } else { logger_1.logger.debug(`[prisma-generator] Re-throwing error: ${configError}`); // Re-throw validation errors and other critical errors throw configError; } } checkForCustomPrismaClientOutputPath(prismaClientGeneratorConfig, path_1.default.dirname(options.schemaPath)); setPrismaClientProvider(prismaClientGeneratorConfig); setPrismaClientConfig(prismaClientGeneratorConfig); const modelOperations = prismaClientDmmf.mappings.modelOperations; const inputObjectTypes = prismaClientDmmf.schema.inputObjectTypes.prisma; // Filter out AndReturn types that were introduced in Prisma 6 but shouldn't have Zod schemas const outputObjectTypes = prismaClientDmmf.schema.outputObjectTypes.prisma.filter((type) => !type.name.includes('AndReturn')); const enumTypes = prismaClientDmmf.schema.enumTypes; const models = [...prismaClientDmmf.datamodel.models]; const mutableModelOperations = [...modelOperations]; const mutableEnumTypes = { model: enumTypes.model ? enumTypes.model.map(normalizeSchemaEnum) : undefined, prisma: enumTypes.prisma.map(normalizeSchemaEnum), }; const hiddenModels = []; const hiddenFields = []; (0, helpers_1.resolveModelsComments)(models, mutableModelOperations, mutableEnumTypes, hiddenModels, hiddenFields); const dataSource = (_d = options.datasources) === null || _d === void 0 ? void 0 : _d[0]; const previewFeatures = prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.previewFeatures; transformer_1.default.provider = dataSource.provider; transformer_1.default.previewFeatures = previewFeatures; // Set the generator configuration for filtering BEFORE generating schemas transformer_1.default.setGeneratorConfig(generatorConfig); // Init single-file mode if configured if (singleFileMode) { const bundleName = (generatorConfig.singleFileName || 'schemas.ts').trim(); const placeAtRoot = generatorConfig.placeSingleFileAtRoot !== false; // default true const baseDir = placeAtRoot ? transformer_1.default.getOutputPath() : transformer_1.default.getSchemasPath(); const bundlePath = path_1.default.join(baseDir, bundleName); (0, singleFileAggregator_1.initSingleFile)(bundlePath); // Configure custom Prisma client import path if user specified custom output (don't rely solely on isCustomOutput flag) const potentialClientOut = (_e = prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.output) === null || _e === void 0 ? void 0 : _e.value; if (potentialClientOut && potentialClientOut !== '@prisma/client') { try { // If potentialClientOut points to node_modules, use the standard @prisma/client import if (potentialClientOut.includes('node_modules')) { (0, singleFileAggregator_1.setSingleFilePrismaImportPath)('@prisma/client'); } else { let rel = path_1.default.relative(baseDir, potentialClientOut).replace(/\\/g, '/'); if (!rel || rel === '') { (0, singleFileAggregator_1.setSingleFilePrismaImportPath)('@prisma/client'); } else { // For the new prisma client generator, the public entry is the 'client' module const provider = ((_f = transformer_1.default.getPrismaClientProvider) === null || _f === void 0 ? void 0 : _f.call(transformer_1.default)) || ((_g = prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.provider) === null || _g === void 0 ? void 0 : _g.value); if (provider === 'prisma-client' && !/\/client\/?$/.test(rel)) { rel = `${rel.replace(/\/$/, '')}/client`; } const importPath = rel.startsWith('.') || rel.startsWith('/') ? rel : `./${rel}`; const importExtension = transformer_1.default.getImportFileExtension(); (0, singleFileAggregator_1.setSingleFilePrismaImportPath)(importPath || '@prisma/client', importExtension); } } } catch { // Fallback silently to default if relative computation fails } } } // Respect explicit emission controls for enums (default true) const emitEnums = ((_h = generatorConfig.emit) === null || _h === void 0 ? void 0 : _h.enums) !== false; if (emitEnums) { // Include datamodel enums to capture unused enums that don't appear in schema.enumTypes // Transform datamodel enums to match schema enum structure const transformedDatamodelEnums = prismaClientDmmf.datamodel.enums .filter((datamodelEnum) => { var _a; return !((_a = mutableEnumTypes.model) === null || _a === void 0 ? void 0 : _a.some((schemaEnum) => schemaEnum.name === datamodelEnum.name)); }) .map((datamodelEnum) => normalizeSchemaEnum({ name: datamodelEnum.name, values: datamodelEnum.values.map((v) => v.name), })); const allModelEnums = [...((_j = mutableEnumTypes.model) !== null && _j !== void 0 ? _j : []), ...transformedDatamodelEnums]; await generateEnumSchemas(mutableEnumTypes.prisma, allModelEnums); } else { logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.enums=false (skipping enum schemas)'); } // Determine if we should generate ONLY pure models (skip base/object/result schemas) // Conditions: // - Single-file mode (user wants a compact bundle) // - pureModels enabled // - All schema variants explicitly disabled (input/result/pure variant system) // - Custom mode (avoid surprising full/minimal modes) const variantsCfg = generatorConfig.variants; const allVariantsDisabled = variantsCfg ? Object.values(variantsCfg).every((v) => !(v === null || v === void 0 ? void 0 : v.enabled)) : true; // if absent, treat as disabled for this heuristic // New heuristic: when pureModels enabled AND all schema variants disabled in custom mode, emit ONLY pure model schemas // independent of single vs multi-file mode. This avoids generating enums/objects/CRUD scaffolding the user does not want. const pureModelsOnlyMode = !!generatorConfig.pureModels && allVariantsDisabled && generatorConfig.mode === 'custom'; if (pureModelsOnlyMode) { logger_1.logger.debug('[prisma-zod-generator] đŸŽ¯ Pure-models-only mode active (variants disabled)'); } const asVariants = variantsCfg; const pureVariantOnlyMode = !!generatorConfig.pureModels && !pureModelsOnlyMode && !!asVariants && !Array.isArray(asVariants) && ((_k = asVariants.pure) === null || _k === void 0 ? void 0 : _k.enabled) === true && ((_l = asVariants.input) === null || _l === void 0 ? void 0 : _l.enabled) === false && ((_m = asVariants.result) === null || _m === void 0 ? void 0 : _m.enabled) === false; if (pureVariantOnlyMode) { logger_1.logger.debug('[prisma-zod-generator] đŸŽ¯ Pure-variant-only mode active (skipping CRUD/input/result schemas)'); } // Validate filtering configuration and provide feedback const validationResult = transformer_1.default.validateFilterCombinations(models); if (!validationResult.isValid) { console.error('❌ Configuration validation failed:'); validationResult.errors.forEach((error) => console.error(` - ${error}`)); throw new Error('Invalid filtering configuration. Please fix the errors above.'); } if (validationResult.warnings.length > 0) { const header = '[prisma-zod-generator] âš ī¸ Configuration warnings (debug):'; logger_1.logger.debug(header); validationResult.warnings.forEach((warning) => { const line = `[prisma-zod-generator] - ${warning}`; logger_1.logger.debug(line); }); } if (validationResult.suggestions.length > 0) { logger_1.logger.debug('💡 Suggestions:'); validationResult.suggestions.forEach((suggestion) => logger_1.logger.debug(` - ${suggestion}`)); } // JSON Schema compatibility mode notification if (generatorConfig.jsonSchemaCompatible) { logger_1.logger.debug('[prisma-zod-generator] â„šī¸ JSON Schema compatibility mode enabled'); logger_1.logger.debug('[prisma-zod-generator] - DateTime fields: string regex validation (no runtime conversion)'); logger_1.logger.debug('[prisma-zod-generator] - BigInt fields: string or number representation'); logger_1.logger.debug('[prisma-zod-generator] - All transforms removed for z.toJSONSchema() compatibility'); logger_1.logger.debug('[prisma-zod-generator] - Test with: z.toJSONSchema(YourSchema)'); } // Merge backward compatibility options with new configuration // Priority: 1. Legacy generator options, 2. New config file options (addSelectType/addIncludeType) // Resolve dual-export controls with proper precedence: // 1) Prisma generator block (extendedOptions.raw) // 2) JSON config (generatorConfig) // 3) Defaults const cfgAny = generatorConfig; const exportTypedFromGenOpt = (_o = extendedOptions.raw) === null || _o === void 0 ? void 0 : _o.exportTypedSchemas; const exportTypedFromJson = cfgAny.exportTypedSchemas; const exportZodFromGenOpt = (_p = extendedOptions.raw) === null || _p === void 0 ? void 0 : _p.exportZodSchemas; const exportZodFromJson = cfgAny.exportZodSchemas; const typedSuffixFromGenOpt = (_q = extendedOptions.raw) === null || _q === void 0 ? void 0 : _q.typedSchemaSuffix; const typedSuffixFromJson = cfgAny.typedSchemaSuffix; const zodSuffixFromGenOpt = (_r = extendedOptions.raw) === null || _r === void 0 ? void 0 : _r.zodSchemaSuffix; const zodSuffixFromJson = cfgAny.zodSchemaSuffix; const toBoolString = (v) => { if (v === undefined) return undefined; if (typeof v === 'string') { const lc = v.trim().toLowerCase(); if (lc === 'true') return 'true'; if (lc === 'false') return 'false'; // Non-empty strings treated as truthy (defensive); but prefer explicit true/false in docs return lc ? 'true' : undefined; } if (typeof v === 'boolean') return v ? 'true' : 'false'; return undefined; }; const backwardCompatibleOptions = { isGenerateSelect: ((_s = extendedOptions.isGenerateSelect) === null || _s === void 0 ? void 0 : _s.toString()) || (generatorConfig.addSelectType !== undefined ? generatorConfig.addSelectType.toString() : 'true'), isGenerateInclude: ((_t = extendedOptions.isGenerateInclude) === null || _t === void 0 ? void 0 : _t.toString()) || (generatorConfig.addIncludeType !== undefined ? generatorConfig.addIncludeType.toString() : 'true'), exportTypedSchemas: (_v = (_u = toBoolString(exportTypedFromGenOpt)) !== null && _u !== void 0 ? _u : toBoolString(exportTypedFromJson)) !== null && _v !== void 0 ? _v : 'true', exportZodSchemas: (_x = (_w = toBoolString(exportZodFromGenOpt)) !== null && _w !== void 0 ? _w : toBoolString(exportZodFromJson)) !== null && _x !== void 0 ? _x : 'true', typedSchemaSuffix: (_y = typedSuffixFromGenOpt !== null && typedSuffixFromGenOpt !== void 0 ? typedSuffixFromGenOpt : typedSuffixFromJson) !== null && _y !== void 0 ? _y : 'Schema', zodSchemaSuffix: (_z = zodSuffixFromGenOpt !== null && zodSuffixFromGenOpt !== void 0 ? zodSuffixFromGenOpt : zodSuffixFromJson) !== null && _z !== void 0 ? _z : 'ZodSchema', }; const addMissingInputObjectTypeOptions = (0, helpers_1.resolveAddMissingInputObjectTypeOptions)(backwardCompatibleOptions); const mutableInputObjectTypes = Array.from(inputObjectTypes !== null && inputObjectTypes !== void 0 ? inputObjectTypes : []); const mutableOutputObjectTypes = Array.from(outputObjectTypes !== null && outputObjectTypes !== void 0 ? outputObjectTypes : []); (0, helpers_1.addMissingInputObjectTypes)(mutableInputObjectTypes, mutableOutputObjectTypes, models, mutableModelOperations, dataSource.provider, addMissingInputObjectTypeOptions); // Set dual export configuration options on Transformer // In minimal mode, forcibly disable select/include types regardless of legacy flags const minimalMode = generatorConfig.mode === 'minimal'; if (minimalMode) { const legacySelect = extendedOptions.isGenerateSelect; const legacyInclude = extendedOptions.isGenerateInclude; const cfgSelect = generatorConfig.addSelectType; const cfgInclude = generatorConfig.addIncludeType; if (legacySelect === true || cfgSelect === true) { // Use info-level to ensure visibility in Prisma CLI output logger_1.logger.info('[prisma-zod-generator] âš ī¸ Minimal mode active: Select schemas will be disabled even if enabled by legacy flags or config.'); } if (legacyInclude === true || cfgInclude === true) { // Use info-level to ensure visibility in Prisma CLI output logger_1.logger.info('[prisma-zod-generator] âš ī¸ Minimal mode active: Include schemas will be disabled even if enabled by legacy flags or config.'); } } transformer_1.default.setIsGenerateSelect(minimalMode ? false : addMissingInputObjectTypeOptions.isGenerateSelect); transformer_1.default.setIsGenerateInclude(minimalMode ? false : addMissingInputObjectTypeOptions.isGenerateInclude); transformer_1.default.setExportTypedSchemas(addMissingInputObjectTypeOptions.exportTypedSchemas); transformer_1.default.setExportZodSchemas(addMissingInputObjectTypeOptions.exportZodSchemas); transformer_1.default.setTypedSchemaSuffix(addMissingInputObjectTypeOptions.typedSchemaSuffix); transformer_1.default.setZodSchemaSuffix(addMissingInputObjectTypeOptions.zodSchemaSuffix); (0, helpers_1.hideInputObjectTypesAndRelatedFields)(mutableInputObjectTypes, hiddenModels, hiddenFields); // Determine explicit emission flags with fallbacks const emitObjects = ((_0 = generatorConfig.emit) === null || _0 === void 0 ? void 0 : _0.objects) !== false; const emitCrud = ((_1 = generatorConfig.emit) === null || _1 === void 0 ? void 0 : _1.crud) !== false; const emitResultsExplicit = (_2 = generatorConfig.emit) === null || _2 === void 0 ? void 0 : _2.results; const emitPureModels = (_4 = (_3 = generatorConfig.emit) === null || _3 === void 0 ? void 0 : _3.pureModels) !== null && _4 !== void 0 ? _4 : !!generatorConfig.pureModels; const emitVariants = ((_5 = generatorConfig.emit) === null || _5 === void 0 ? void 0 : _5.variants) !== false; // variants wrapper/index // If enums skipped but objects/crud requested, log warning if (!emitEnums && (emitObjects || emitCrud)) { logger_1.logger.warn('[prisma-zod-generator] âš ī¸ emit.enums=false may break object/CRUD schemas referencing enums.'); } const shouldSkipCrudAndObjectsDueToHeuristics = pureModelsOnlyMode || pureVariantOnlyMode; // Minimal mode: keep objects/CRUD enabled, but generation is constrained elsewhere: // - object schemas gated by isObjectSchemaEnabled (only basic Where*/Create*/Update*/OrderBy*Relation) // - operations gated by Transformer.isOperationEnabled (only find/create/update by default) if (minimalMode) { logger_1.logger.debug('[prisma-zod-generator] ⚡ Minimal mode: emitting limited objects and CRUD (findUnique/findFirst/findMany + create/update/delete only)'); } if (emitObjects && !shouldSkipCrudAndObjectsDueToHeuristics) { await generateObjectSchemas(mutableInputObjectTypes, models); } else if (!emitObjects) { logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.objects=false (skipping object/input schemas)'); } if (emitCrud && !shouldSkipCrudAndObjectsDueToHeuristics) { await generateModelSchemas(models, mutableModelOperations); } else if (!emitCrud) { logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.crud=false (skipping CRUD operation schemas)'); } // Only create objects index if objects or crud emitted (legacy expectation) if ((emitObjects || emitCrud) && !shouldSkipCrudAndObjectsDueToHeuristics) { await generateIndex(); } if (emitPureModels) { logger_1.logger.debug(`[debug] Before pure model generation: pureModels=${String(generatorConfig.pureModels || emitPureModels)} namingPreset=${((_6 = generatorConfig.naming) === null || _6 === void 0 ? void 0 : _6.preset) || 'none'}`); await generatePureModelSchemas(models, generatorConfig); } else { logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.pureModels=false (skipping pure model schemas)'); } if (emitVariants) { await generateVariantSchemas(models, generatorConfig); if (!singleFileMode) { await updateIndexWithVariants(generatorConfig); } } else { logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.variants=false (skipping variant wrapper schemas)'); } // Result schemas are generated inside Transformer.generateResultSchemas; we guard via emit.results if specified if (emitResultsExplicit === false) { // Monkey patch config variants.result.enabled to false to unify gating pathway safely const variantsRef = generatorConfig.variants || (generatorConfig.variants = {}); const resultVariantRef = variantsRef.result || (variantsRef.result = {}); resultVariantRef.enabled = false; logger_1.logger.debug('[prisma-zod-generator] â­ī¸ emit.results=false (forcing skip of result schemas)'); } if (!(pureModelsOnlyMode || pureVariantOnlyMode)) { generateFilteringSummary(models, generatorConfig); } // If single-file mode is enabled, flush aggregator and clean directory around the bundle if (singleFileMode) { await (0, singleFileAggregator_1.flushSingleFile)(); const placeAtRoot = generatorConfig.placeSingleFileAtRoot !== false; // default true const baseDir = placeAtRoot ? transformer_1.default.getOutputPath() : transformer_1.default.getSchemasPath(); const bundleName = (generatorConfig.singleFileName || 'schemas.ts').trim(); const bundlePath = path_1.default.join(baseDir, bundleName); // Add the single file to the manifest const manifest = transformer_1.default.getCurrentManifest(); if (manifest) { (0, safeOutputManagement_1.addFileToManifest)(manifest, bundlePath, transformer_1.default.getOutputPath()); } try { const entries = await fs_1.promises.readdir(baseDir, { withFileTypes: true }); for (const entry of entries) { const full = path_1.default.join(baseDir, entry.name); if (full === bundlePath) continue; if (entry.isDirectory()) { await fs_1.promises.rm(full, { recursive: true, force: true }); } else { await fs_1.promises.unlink(full); } } } catch { } } // Save the manifest at the end of generation const finalManifest = transformer_1.default.getCurrentManifest(); if (finalManifest && resolvedSafetyConfig && !resolvedSafetyConfig.skipManifest) { await (0, safeOutputManagement_1.saveManifest)(transformer_1.default.getOutputPath(), finalManifest); } else if (resolvedSafetyConfig === null || resolvedSafetyConfig === void 0 ? void 0 : resolvedSafetyConfig.skipManifest) { logger_1.logger.debug('[prisma-generator] Skipping manifest save (skipManifest enabled)'); } maybeShowSponsorMessage(); } catch (error) { console.error(error); } } function getGeneratorConfigByProvider(generators, provider) { return generators.find((it) => (0, internals_1.parseEnvValue)(it.provider) === provider); } function checkForCustomPrismaClientOutputPath(prismaClientGeneratorConfig, schemaBaseDir) { var _a; const outputValue = (_a = prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.output) === null || _a === void 0 ? void 0 : _a.value; const isCustomOutput = Boolean(prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.isCustomOutput); const provider = (prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.provider) ? (0, internals_1.parseEnvValue)(prismaClientGeneratorConfig.provider) : undefined; const looksLikeNodeModulesPath = Boolean(outputValue && outputValue.includes('node_modules')); const shouldUseCustomPath = Boolean(isCustomOutput && outputValue && !looksLikeNodeModulesPath); if (shouldUseCustomPath) { const rawOutput = outputValue; const normalizedOutput = path_1.default.isAbsolute(rawOutput) ? path_1.default.normalize(rawOutput) : path_1.default.resolve(schemaBaseDir, rawOutput); transformer_1.default.setPrismaClientOutputPath(normalizedOutput); return; } // New generator may require an explicit output path when users customize it; otherwise fall back // to the default package entrypoint just like prisma-client-js. transformer_1.default.setPrismaClientOutputPath('@prisma/client'); } function setPrismaClientProvider(prismaClientGeneratorConfig) { if (prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.provider) { transformer_1.default.setPrismaClientProvider((0, internals_1.parseEnvValue)(prismaClientGeneratorConfig.provider)); } } function setPrismaClientConfig(prismaClientGeneratorConfig) { if (prismaClientGeneratorConfig === null || prismaClientGeneratorConfig === void 0 ? void 0 : prismaClientGeneratorConfig.config) { transformer_1.default.setPrismaClientConfig(prismaClientGeneratorConfig.config); } } function maybeWarnOnUnsupportedPrismaVersion(options) { var _a; const version = detectInstalledPrismaVersion(options.schemaPath); if (!version) return; const major = Number.parseInt((_a = version.split('.')[0]) !== null && _a !== void 0 ? _a : '', 10); if (!Number.isFinite(major)) return; if (major < 7) { logger_1.logger.info(`\n[prisma-zod-generator] âš ī¸ Detected prisma@${version}, but this release requires Prisma >=7.\n` + 'Please pin prisma-zod-generator to ^1.32.1 while you remain on Prisma 6, or upgrade Prisma before using 2.x.'); } } function detectInstalledPrismaVersion(schemaPath) { try { const req = (0, module_1.createRequire)(schemaPath); const pkg = req('prisma/package.json'); return pkg === null || pkg === void 0 ? void 0 : pkg.version; } catch { return undefined; } } function normalizeSchemaEnum(enumType) { const rawValues = Array.isArray(enumType.values) ? Array.from(enumType.values) : undefined; const values = rawValues ? rawValues.map((v) => { if (typeof v === 'string') return v; if (v && typeof v === 'object' && 'name' in v) { return v.name; } if (v && typeof v === 'object' && 'value' in v) { return v.value; } return String(v); }) : Array.isArray(enumType.data) ? Array.from(enumType.data).map((entry) => entry.value || entry.key) : []; const data = enumType.data ? Array.from(enumType.data) : values.map((val) => ({ key: val, value: val })); return { name: enumType.name, data, values, }; } async function generateEnumSchemas(prismaSchemaEnum, modelSchemaEnum) { const enumTypes = [...prismaSchemaEnum, ...modelSchemaEnum]; // Include both raw and normalized enum names so import/name checks work const rawEnumNames = enumTypes.map((e) => e.name); // Mirror Transformer.normalizeEnumName logic locally to avoid surfacing private APIs const modelEnumPatterns = [/^(\w+)ScalarFieldEnum$/, /^(\w+)OrderByRelevanceFieldEnum$/]; const toPascalCase = (modelName) => modelName .replace(/[_-\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : '')) .replace(/^\w/, (c) => c.toUpperCase()); const normalizeEnumNameForRegistry = (name) => { for (const pattern of modelEnumPatterns) { const match = name.match(pattern); if (match) { const modelName = match[1]; return name.replace(modelName, toPascalCase(modelName)); } } return null; }; const normalizedEnumNames = enumTypes .map((e) => { var _a; return (_a = normalizeEnumNameForRegistry(e.name)) !== null && _a !== void 0 ? _a : e.name; }) .filter(Boolean); const combinedEnumNames = Array.from(new Set([...rawEnumNames, ...normalizedEnumNames])); transformer_1.default.enumNames = combinedEnumNames; const transformer = new transformer_1.default({ enumTypes, }); await transformer.generateEnumSchemas(); } async function generateObjectSchemas(inputObjectTypes, models) { var _a, _b; // Debug: List all UpdateManyWithWhere types in DMMF const updateManyWithWhereTypes = inputObjectTypes.filter((t) => t.name.includes('UpdateManyWithWhere')); logger_1.logger.debug(`\n🔍 DEBUG: Found ${updateManyWithWhereTypes.length} UpdateManyWithWhere types in DMMF:`); updateManyWithWhereTypes.forEach((t) => { logger_1.logger.debug(` - ${t.name}: fields [${t.fields.map((f) => f.name).join(', ')}]`); }); for (let i = 0; i < inputObjectTypes.length; i += 1) { const originalFields = (_a = inputObjectTypes[i]) === null || _a === void 0 ? void 0 : _a.fields; const name = (_b = inputObjectTypes[i]) === null || _b === void 0 ? void 0 : _b.name; // Debug specific type if (name === 'PostUpdateManyWithWhereWithoutAuthorInput') { logger_1.logger.debug(`\n🔍 DEBUG: Found ${name}`); logger_1.logger.debug(`Fields: ${originalFields === null || originalFields === void 0 ? void 0 : originalFields.map((f) => f.name).join(', ')}`); originalFields === null || originalFields === void 0 ? void 0 : originalFields.forEach((field) => { logger_1.logger.debug(` - ${field.name}: ${field.inputTypes.map((t) => t.type).join(' | ')}`); }); } // Filter object schemas based on enabled models if (name && !isObjectSchemaEnabled(name)) { logger_1.logger.debug(`[DEBUG] Skipping object schema: ${name} (disabled by config)`); continue; } // Apply field filtering before creating transformer let filteredFields = [...(originalFields || [])]; if (name && originalFields) { // Extract model name from schema name (e.g., "UserCreateInput" -> "User") const modelName = transformer_1.default.extractModelNameFromContext(name); const variant = transformer_1.default.determineSchemaVariant(name); if (modelName) { // Apply field filtering using the transformer's filtering logic // Cast to the expected type to handle ReadonlyDeep wrapper filteredFields = transformer_1.default.filterFields([...originalFields], modelName, variant, models, name); } } const transformer = new transformer_1.default({ name, fields: filteredFields, models }); await transformer.generateObjectSchema(); } } /** * Check if an object schema should be generated based on enabled models and operations */ function isObjectSchemaEnabled(objectSchemaName) { // Always allow scalar/enum filter and field update helper schemas const helperTypePatterns = [ // Basic filters and their nullable variants /^(?:String|Int|Float|Decimal|BigInt|Bool|Boolean|DateTime|Bytes|Json)(?:Nullable)?Filter$/, // Enum filters (e.g., EnumRoleNullableFilter, EnumRoleFilter) /^Enum\w+(?:Nullable)?Filter$/, // WithAggregates variants /^(?:String|Int|Float|Decimal|BigInt|Bool|Boolean|DateTime|Bytes|Json)(?:Nullable)?WithAggregatesFilter$/, /^Enum\w+(?:Nullable)?WithAggregatesFilter$/, // Nested filters /^Nested\w+(?:Nullable)?(?:WithAggregates)?Filter$/, // Field update operation inputs (e.g., NullableBytesFieldUpdateOperationsInput) /^(?:Nullable)?\w+FieldUpdateOperationsInput$/, ]; if (helperTypePatterns.some((p) => p.test(objectSchemaName))) { logger_1.logger.debug(`🔍 Helper schema allowed: ${objectSchemaName}`); return true; } // Extract potential model name from object schema name const modelName = extractModelNameFromObjectSchema(objectSchemaName); // In minimal mode, suppress complex/nested input schemas proactively const cfg = transformer_1.default.getGeneratorConfig(); if ((cfg === null || cfg === void 0 ? void 0 : cfg.mode) === 'minimal') { // Allow-list of basic inputs still needed in minimal mode (covers find/create/update/delete) const allowedBasics = [ /WhereInput$/, /WhereUniqueInput$/, /UncheckedCreateInput$/, // Prefer UncheckedCreateInput over CreateInput in minimal mode /UpdateInput$/, // Allow UpdateInput for update operations /UncheckedUpdateInput$/, // Also allow UncheckedUpdateInput variants /UpdateManyMutationInput$/, // Allow UpdateMany mutation inputs /OrderByWithRelationInput$/, ]; if (allowedBasics.some((p) => p.test(objectSchemaName))) { // Special case: CreateMany inputs are heavier; only allow when explicitly requested if (/CreateManyInput$/.test(objectSchemaName)) { const ops = cfg.minimalOperations; const allowCreateMany = Array.isArray(ops) ? ops.includes('createMany') || ops.includes('create') : false; // default off in pure minimal mode if (!allowCreateMany) { logger_1.logger.debug(`â­ī¸ Minimal mode: skipping heavy ${objectSchemaName} (no createMany in ops)`); return false;