prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
794 lines (793 loc) âĸ 124 kB
JavaScript
"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;