Skip to content

Commit

Permalink
added fix for prisma decimal with 'toFixed' in 4.11
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishoermann committed Mar 8, 2023
1 parent c8cdfe2 commit 44a98ac
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 326 deletions.
16 changes: 8 additions & 8 deletions packages/generator/package.json
@@ -1,6 +1,6 @@
{
"name": "zod-prisma-types",
"version": "2.4.0",
"version": "2.4.1-beta.0",
"description": "Generates zod schemas from Prisma models with advanced validation",
"author": "Chris Hörmann",
"license": "MIT",
Expand Down Expand Up @@ -32,21 +32,21 @@
"format": "prettier --write \"**/*.{ts,tsx,md,json}\""
},
"devDependencies": {
"@prisma/internals": "^4.10.1",
"@prisma/internals": "^4.11.0",
"@types/lodash": "^4.14.191",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"eslint-config-universe": "^11.1.1",
"@typescript-eslint/eslint-plugin": "^5.54.1",
"@typescript-eslint/parser": "^5.54.1",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.7.0",
"eslint-config-universe": "^11.2.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.4",
"typescript": "^4.9.5",
"vitest": "^0.24.5"
},
"dependencies": {
"@prisma/generator-helper": "^4.10.1",
"@prisma/generator-helper": "^4.11.0",
"code-block-writer": "^11.0.3",
"lodash": "^4.17.21",
"zod": "^3.21.4"
Expand Down
Expand Up @@ -5,16 +5,17 @@ export const writeDecimalJsLike = ({
dmmf,
getSingleFileContent = false,
}: ContentWriterOptions) => {
const { useMultipleFiles } = dmmf.generatorConfig;
const { useMultipleFiles, prismaClientPath } = dmmf.generatorConfig;

if (useMultipleFiles && !getSingleFileContent) {
writeImport('{ z }', 'zod');
writeImport('type { DecimalJsLike }', `${prismaClientPath}/runtime`);
}

writer
.blankLine()
.writeLine(
`export const DecimalJSLikeSchema = z.object({ d: z.array(z.number()), e: z.number(), s: z.number() });`,
`export const DecimalJSLikeSchema: z.ZodType<DecimalJsLike> = z.object({ d: z.array(z.number()), e: z.number(), s: z.number(), toFixed: z.function().args().returns(z.string()), });`,
)
.newLine()
.writeLine(
Expand Down
Expand Up @@ -5,16 +5,17 @@ export const writeDecimalJsLikeList = ({
dmmf,
getSingleFileContent = false,
}: ContentWriterOptions) => {
const { useMultipleFiles } = dmmf.generatorConfig;
const { useMultipleFiles, prismaClientPath } = dmmf.generatorConfig;

if (useMultipleFiles && !getSingleFileContent) {
writeImport('{ z }', 'zod');
writeImport('type { DecimalJsLike }', `${prismaClientPath}/runtime`);
}

writer
.blankLine()
.writeLine(
`export const DecimalJSLikeListSchema = z.object({ d: z.array(z.number()), e: z.number(), s: z.number() }).array();`,
`export const DecimalJSLikeListSchema: z.ZodType<DecimalJsLike[]> = z.object({ d: z.array(z.number()), e: z.number(), s: z.number(), toFixed: z.function().args().returns(z.string()), }).array();`,
);

if (useMultipleFiles && !getSingleFileContent) {
Expand Down
Expand Up @@ -20,15 +20,17 @@ export const writeIsValidDecimalInput = ({
.writeLine(`export const isValidDecimalInput =`)
.withIndentationLevel(1, () => {
writer
.write(`(v?: null | string | number | DecimalJSLike) => `)
.write(
`(v?: null | string | number | DecimalJSLike): v is string | number | DecimalJSLike => `,
)
.inlineBlock(() => {
writer
.writeLine(`if (!v) return false;`)
.writeLine(`return (`)
.withIndentationLevel(3, () => {
writer
.writeLine(
`(typeof v === 'object' && 'd' in v && 'e' in v && 's' in v) ||`,
`(typeof v === 'object' && 'd' in v && 'e' in v && 's' in v && 'toFixed' in v) ||`,
)
.writeLine(
`(typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) ||`,
Expand Down
4 changes: 2 additions & 2 deletions packages/usage/prisma/schema.prisma
Expand Up @@ -4,8 +4,8 @@
generator client {
provider = "prisma-client-js"
// output = "./generated/client"
previewFeatures = ["views", "extendedWhereUnique"]
// previewFeatures = ["views"]
// previewFeatures = ["views", "extendedWhereUnique"]
previewFeatures = ["views"]
}

datasource db {
Expand Down
10 changes: 5 additions & 5 deletions packages/usage/tests/decimal.test.ts
Expand Up @@ -3,12 +3,12 @@ import {
DecimalListSchema,
DecimalSchema,
DECIMAL_STRING_REGEX,
isValidDecimalInput,
} from './implementations/decimalSchema';
import { client } from './trpc/client';
import { getServer } from './trpc/server';
import Decimal from 'decimal.js';
import { DecimalJsLike } from '@prisma/client/runtime';
import { isValidDecimalInput } from '../prisma/generated/zod';

///////////////////////////////////////
// CONSTANTS
Expand All @@ -33,7 +33,7 @@ export const decimalJsLikeTwo: DecimalJsLike = {
///////////////////////////////////////

const isDecimalJsLike = (v: any): v is DecimalJsLike => {
return !!v && 'd' in v && 'e' in v && 's' in v;
return !!v && 'd' in v && 'e' in v && 's' in v && 'toFixed' in v;
};

///////////////////////////////////////
Expand Down Expand Up @@ -122,7 +122,7 @@ it('should be a valid input when a decimalJSLike is provided to "isValidDecimalI

it('should be able to use prisma decimal as input in DecimalSchema', () => {
const parsedDecimal = DecimalSchema.parse(new Prisma.Decimal(1.1));
expect(Prisma.Decimal.isDecimal(parsedDecimal)).toBe(true);
expect(isDecimalJsLike(parsedDecimal)).toBe(true);
});

it('should be able to use string as input in DecimalSchema', () => {
Expand Down Expand Up @@ -161,8 +161,8 @@ it('should be able to use prisma decimal as input in DecimalListSchema', () => {
new Prisma.Decimal(1.123),
]);

expect(Prisma.Decimal.isDecimal(parsedDecimals[0])).toBe(true);
expect(Prisma.Decimal.isDecimal(parsedDecimals[1])).toBe(true);
expect(isDecimalJsLike(parsedDecimals[0])).toBe(true);
expect(isDecimalJsLike(parsedDecimals[1])).toBe(true);
});

it('should be able to use decimalJS as input in DecimalListSchema', () => {
Expand Down
35 changes: 6 additions & 29 deletions packages/usage/tests/implementations/decimalSchema.ts
@@ -1,51 +1,28 @@
import { Prisma } from '@prisma/client';
import { DecimalJsLike } from '@prisma/client/runtime';
import { z } from 'zod';

export const DecimalJSLikeSchema = z.object({
d: z.array(z.number()),
e: z.number(),
s: z.number(),
});
import {
DecimalJSLikeSchema,
isValidDecimalInput,
} from '../../prisma/generated/zod';

// TYPEGURADS
// ------------------------------

export const DECIMAL_STRING_REGEX = /^[0-9.,e+\-bxffo_cp]+$|Infinity|NaN/;

export const isValidDecimalInput = (
v?: null | string | number | Prisma.Decimal | DecimalJsLike,
) => {
if (!v) return false;
return (
(typeof v === 'object' && Prisma.Decimal.isDecimal(v)) ||
(typeof v === 'object' && 'd' in v && 'e' in v && 's' in v) ||
(typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) ||
typeof v === 'number'
);
};
// TEST SCHEMAS
// ------------------------------

export const DecimalListSchema = z
.union([
z.number().array(),
z.string().array(),
z.instanceof(Prisma.Decimal).array(),
DecimalJSLikeSchema.array(),
])
.union([z.number().array(), z.string().array(), DecimalJSLikeSchema.array()])
.refine((v) => (v as any[]).every((v) => isValidDecimalInput(v)), {
message: 'Field "decimal" must be a Decimal',
path: ['Models', 'DecimalModel'],
});

export const DecimalSchema = z
.union([
z.number(),
z.string(),
z.instanceof(Prisma.Decimal),
DecimalJSLikeSchema,
])
.union([z.number(), z.string(), DecimalJSLikeSchema])
.refine((v) => isValidDecimalInput(v), {
message: 'Field "decimal" must be a Decimal',
path: ['Models', 'DecimalModel'],
Expand Down

0 comments on commit 44a98ac

Please sign in to comment.