prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
262 lines • 10.7 kB
JavaScript
;
/**
* Zod Integration Helper
*
* Integrates @zod comment parsing with existing comment processing
* and transformer workflows while maintaining backward compatibility.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.processModelsWithZodIntegration = processModelsWithZodIntegration;
exports.shouldPreserveExistingCommentProcessing = shouldPreserveExistingCommentProcessing;
exports.extractNonZodCommentContent = extractNonZodCommentContent;
const logger_1 = require("../utils/logger");
const zod_comments_1 = require("../parsers/zod-comments");
/**
* Process models to extract and integrate @zod annotations
*
* This function processes all models and their fields to:
* 1. Extract @zod annotations from field comments
* 2. Generate Zod schema strings for annotated fields
* 3. Maintain compatibility with existing comment processing
* 4. Collect import requirements and error information
*
* @param models - Array of Prisma DMMF models
* @param options - Processing options
* @returns Enhanced model information with Zod integration
*/
function processModelsWithZodIntegration(models, options = {}) {
const enhancedModels = [];
for (const model of models) {
try {
const enhancedModel = processModelWithZodIntegration(model, options);
enhancedModels.push(enhancedModel);
}
catch (error) {
// Handle model processing errors gracefully
console.warn(`Failed to process model ${model.name} for Zod integration:`, error);
// Create fallback model info
enhancedModels.push({
model,
enhancedFields: model.fields.map((field) => createFallbackFieldInfo(field)),
allZodImports: new Set(),
hasAnyZodAnnotations: false,
zodProcessingErrors: [
`Model processing failed: ${error instanceof Error ? error.message : String(error)}`,
],
modelCustomImports: [],
allCustomImports: [],
});
}
}
return enhancedModels;
}
/**
* Process a single model with Zod integration
*
* @param model - Prisma DMMF model
* @param options - Processing options
* @returns Enhanced model information
*/
function processModelWithZodIntegration(model, options) {
const enhancedFields = [];
const allZodImports = new Set();
const zodProcessingErrors = [];
let hasAnyZodAnnotations = false;
// Extract model-level custom imports
const modelCustomImportsResult = (0, zod_comments_1.extractModelCustomImports)(model);
const modelCustomImports = modelCustomImportsResult.imports;
zodProcessingErrors.push(...modelCustomImportsResult.parseErrors);
// Extract comments from all fields in the model
const extractedComments = (0, zod_comments_1.extractFieldComments)([model]);
// Create a map for quick field comment lookup
const commentMap = new Map();
extractedComments.forEach((comment) => {
commentMap.set(comment.context.fieldName, comment);
});
// Process each field
for (const field of model.fields) {
try {
const enhancedField = processFieldWithZodIntegration(field, model, commentMap, options);
enhancedFields.push(enhancedField);
// Collect imports and track annotations
enhancedField.zodImports.forEach((imp) => allZodImports.add(imp));
if (enhancedField.hasZodAnnotations) {
hasAnyZodAnnotations = true;
}
// Collect errors
zodProcessingErrors.push(...enhancedField.zodErrors);
}
catch (error) {
// Handle field processing errors gracefully
console.warn(`Failed to process field ${field.name} in model ${model.name}:`, error);
const fallbackField = createFallbackFieldInfo(field);
fallbackField.zodErrors.push(`Field processing failed: ${error instanceof Error ? error.message : String(error)}`);
enhancedFields.push(fallbackField);
}
}
// Collect all custom imports (model-level + field-level)
const allCustomImports = [...modelCustomImports];
enhancedFields.forEach((field) => {
allCustomImports.push(...field.customImports);
});
return {
model,
enhancedFields,
allZodImports,
hasAnyZodAnnotations,
zodProcessingErrors: zodProcessingErrors.filter((error) => error.length > 0),
modelCustomImports,
allCustomImports,
};
}
/**
* Process a single field with Zod integration
*
* @param field - Prisma DMMF field
* @param model - Parent model
* @param commentMap - Map of field comments
* @param options - Processing options
* @returns Enhanced field information
*/
function processFieldWithZodIntegration(field, model, commentMap, options) {
const enhancedField = {
field,
hasZodAnnotations: false,
zodImports: new Set(),
zodErrors: [],
fallbackToDefault: false,
customImports: [],
};
// Extract field-level custom imports
const fieldCustomImportsResult = (0, zod_comments_1.extractFieldCustomImports)(field, model.name);
enhancedField.customImports = fieldCustomImportsResult.imports;
enhancedField.customSchema = fieldCustomImportsResult.customSchema;
enhancedField.zodErrors.push(...fieldCustomImportsResult.parseErrors);
// Check if Zod annotation processing is enabled
if (options.enableZodAnnotations === false) {
enhancedField.fallbackToDefault = true;
return enhancedField;
}
// Get comment for this field
const extractedComment = commentMap.get(field.name);
if (!extractedComment || !extractedComment.hasZodAnnotations) {
// No @zod annotations found
if (options.generateFallbackSchemas !== false) {
enhancedField.zodSchema = generateFallbackSchema(field);
enhancedField.zodImports = (0, zod_comments_1.getRequiredImports)(field.type);
}
enhancedField.fallbackToDefault = true;
return enhancedField;
}
// Process @zod annotations
try {
const parseResult = (0, zod_comments_1.parseZodAnnotations)(extractedComment.normalizedComment, extractedComment.context);
if (!parseResult.isValid) {
enhancedField.zodErrors.push(...parseResult.parseErrors);
enhancedField.fallbackToDefault = true;
// Surface the rejection — silently dropping @zod annotations means
// users ship schemas without the validation they wrote (issue #374).
logger_1.logger.warn(`[prisma-zod-generator] Skipping @zod annotations for field "${field.name}":`, parseResult.parseErrors.join('; '));
return enhancedField;
}
// Generate Zod schema from annotations
const baseType = (0, zod_comments_1.getBaseZodTypeForField)(field);
const schemaResult = (0, zod_comments_1.generateCompleteZodSchema)(baseType, parseResult.annotations, extractedComment.context, options.zodVersion || 'auto');
if (schemaResult.isValid) {
enhancedField.hasZodAnnotations = true;
enhancedField.zodSchema = schemaResult.schemaChain;
enhancedField.zodImports = schemaResult.imports;
}
else {
enhancedField.zodErrors.push(...schemaResult.errors);
enhancedField.fallbackToDefault = true;
// Generate fallback if enabled
if (options.generateFallbackSchemas !== false) {
enhancedField.zodSchema = generateFallbackSchema(field);
enhancedField.zodImports = (0, zod_comments_1.getRequiredImports)(field.type);
}
}
}
catch (error) {
enhancedField.zodErrors.push(`Annotation processing failed: ${error instanceof Error ? error.message : String(error)}`);
enhancedField.fallbackToDefault = true;
// Generate fallback if enabled
if (options.generateFallbackSchemas !== false) {
enhancedField.zodSchema = generateFallbackSchema(field);
enhancedField.zodImports = (0, zod_comments_1.getRequiredImports)(field.type);
}
}
return enhancedField;
}
/**
* Create fallback field info for error cases
*
* @param field - Prisma DMMF field
* @returns Fallback enhanced field info
*/
function createFallbackFieldInfo(field) {
return {
field,
hasZodAnnotations: false,
zodSchema: generateFallbackSchema(field),
zodImports: (0, zod_comments_1.getRequiredImports)(field.type),
zodErrors: [],
fallbackToDefault: true,
customImports: [],
customSchema: undefined,
};
}
/**
* Generate fallback Zod schema for field without annotations
*
* @param field - Prisma DMMF field
* @returns Fallback Zod schema string
*/
function generateFallbackSchema(field) {
return (0, zod_comments_1.getBaseZodTypeForField)(field);
}
/**
* Check if existing comment processing should be preserved
*
* This function ensures backward compatibility by checking if a field
* comment contains existing comment directives that should be processed
* by the original comment processing system.
*
* @param comment - Field comment string
* @returns True if existing processing should be preserved
*/
function shouldPreserveExistingCommentProcessing(comment) {
if (!comment) {
return false;
}
// Check for existing comment directives that should be preserved
const existingDirectives = [
/@Gen\./, // Existing generator directives
/@@/, // Model-level directives
/@map/, // Mapping directives
/@ignore/, // Ignore directives
];
return existingDirectives.some((directive) => directive.test(comment));
}
/**
* Extract non-Zod comment content for backward compatibility
*
* @param comment - Original comment string
* @returns Comment content with @zod annotations removed
*/
function extractNonZodCommentContent(comment) {
if (!comment) {
return '';
}
// Remove @zod annotations while preserving other content
const zodPattern = /@zod(\.[a-zA-Z_][a-zA-Z0-9_]*\s*(\([^)]*\))?)+/gi;
const cleanedComment = comment.replace(zodPattern, '').trim();
// Clean up extra whitespace
return cleanedComment.replace(/\s+/g, ' ').trim();
}
/*
* getZodIntegrationStatistics and integrateZodWithExistingComments were removed as
* unreachable — no caller inside or outside this module. processModelsWithZodIntegration is
* the entry point the generator actually uses.
*/
//# sourceMappingURL=zod-integration.js.map