prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
115 lines (112 loc) • 4.96 kB
JavaScript
"use strict";
/**
* Helper functions and utilities for Decimal type support
* Matches zod-prisma-types implementation for seamless migration
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateDecimalHelpers = generateDecimalHelpers;
exports.generateDecimalInputSchema = generateDecimalInputSchema;
exports.generateDecimalModelSchema = generateDecimalModelSchema;
exports.isDecimalJsAvailable = isDecimalJsAvailable;
/**
* Generate helper schemas for Decimal validation
* These helpers enable proper Prisma.Decimal and Decimal.js support
*
* @param hasDecimalJs - Whether decimal.js is installed and should be imported
* @param zodNamespace - The zod namespace to use (e.g., 'z' or 'zod')
* @returns Object containing helper schema code and necessary imports
*/
function generateDecimalHelpers(hasDecimalJs, zodNamespace = 'z', prismaImportPath = '@prisma/client') {
const imports = [`import { Prisma } from '${prismaImportPath}';`];
if (hasDecimalJs) {
imports.push("import Decimal from 'decimal.js';");
}
const helperCode = `
// DECIMAL HELPERS
//------------------------------------------------------
export const DecimalJSLikeSchema: ${zodNamespace}.ZodType<Prisma.DecimalJsLike> = ${zodNamespace}.object({
d: ${zodNamespace}.array(${zodNamespace}.number()),
e: ${zodNamespace}.number(),
s: ${zodNamespace}.number(),
// Zod v3/v4 compatible callable check
toFixed: ${zodNamespace}.custom<Prisma.DecimalJsLike['toFixed']>((v) => typeof v === 'function'),
});
// Accept canonical decimal strings (+/-, optional fraction, optional exponent), or Infinity/NaN.
export const DECIMAL_STRING_REGEX = /^(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?(?:[eE][+\-]?[0-9]+)?|Infinity)|NaN)$/;
export const isValidDecimalInput = (
v?: null | string | number | Prisma.DecimalJsLike,
): v is string | number | Prisma.DecimalJsLike => {
if (v === undefined || v === null) return false;
return (
// Explicit instance checks first. Decimal.isDecimal is used instead of instanceof because
// the browser and server Prisma runtimes bundle separate Decimal class copies, so
// instanceof fails across copies while the structural isDecimal check is cross-copy safe.
Prisma.Decimal.isDecimal(v) ||
// If Decimal.js is present and imported by the generator, this symbol exists at runtime
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Decimal may be undefined when not installed; codegen controls the import
(typeof Decimal !== 'undefined' && v instanceof Decimal) ||
(typeof v === 'object' &&
'd' in v &&
'e' in v &&
's' in v &&
'toFixed' in v) ||
(typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) ||
typeof v === 'number'
);
};
`.trim();
return { helperCode, imports };
}
/**
* Generate Decimal field schema for input types (Create, Update, etc.)
* Uses union of multiple formats with refinement validation
*
* @param zodNamespace - The zod namespace to use (e.g., 'z' or 'zod')
* @param hasDecimalJs - Whether decimal.js is installed
* @param fieldName - Name of the field (for error messages)
* @returns Zod schema string
*/
function generateDecimalInputSchema(zodNamespace, hasDecimalJs, fieldName) {
const unionTypes = [`${zodNamespace}.number()`, `${zodNamespace}.string()`];
if (hasDecimalJs) {
unionTypes.push(`${zodNamespace}.instanceof(Decimal)`);
}
// Structural cross-runtime-copy safe branch: keeps foreign-copy Decimal instances intact
// (instanceof would miss them and DecimalJSLikeSchema would strip them to plain objects).
unionTypes.push(`${zodNamespace}.custom<InstanceType<typeof Prisma.Decimal>>((v) => Prisma.Decimal.isDecimal(v))`, 'DecimalJSLikeSchema');
const errorMessage = fieldName ? `Field '${fieldName}' must be a Decimal` : 'Must be a Decimal';
return `${zodNamespace}.union([
${unionTypes.join(',\n ')},
]).refine((v) => isValidDecimalInput(v), {
message: "${errorMessage}",
})`;
}
/**
* Generate Decimal field schema for pure models (output/result types)
* Uses the cross-runtime-copy safe Prisma.Decimal.isDecimal check
*
* @param zodNamespace - The zod namespace to use (e.g., 'z' or 'zod')
* @param fieldName - Name of the field
* @param modelName - Name of the model (for error messages)
* @returns Zod schema string
*/
function generateDecimalModelSchema(zodNamespace, fieldName, modelName) {
return `${zodNamespace}.custom<InstanceType<typeof Prisma.Decimal>>((v) => Prisma.Decimal.isDecimal(v), {
message: "Field '${fieldName}' must be a Decimal. Location: ['Models', '${modelName}']",
})`;
}
/**
* Check if decimal.js is installed in the project
* @returns true if decimal.js is available
*/
function isDecimalJsAvailable() {
try {
require.resolve('decimal.js');
return true;
}
catch {
return false;
}
}
//# sourceMappingURL=decimal-helpers.js.map