Skip to content

Commit

Permalink
fixed #100, #99, #96, #90
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishoermann committed Mar 10, 2023
1 parent 44a98ac commit a7bc182
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 149 deletions.
2 changes: 1 addition & 1 deletion packages/generator/package.json
@@ -1,6 +1,6 @@
{
"name": "zod-prisma-types",
"version": "2.4.1-beta.0",
"version": "2.4.1",
"description": "Generates zod schemas from Prisma models with advanced validation",
"author": "Chris Hörmann",
"license": "MIT",
Expand Down
14 changes: 7 additions & 7 deletions packages/generator/src/classes/extendedDMMFInputType.ts
Expand Up @@ -57,9 +57,9 @@ export class ExtendedDMMFInputType
this.omitFields = this._setOmitFields();
this.imports = this._setImports();

if (this.name === 'ProfileWhereUniqueInput') {
console.log(type);
}
// if (this.name === 'ProfileWhereUniqueInput') {
// console.log(type);
// }
}

/**
Expand All @@ -80,11 +80,11 @@ export class ExtendedDMMFInputType
(modelField) => modelField.name === field.name,
);

const hasConstraints = this.constraints.fields?.includes(field.name);
// const hasConstraints = this.constraints.fields?.includes(field.name);

if (this.name === 'ProfileWhereUniqueInput') {
console.log({ fieldname: field.name, hasConstraints });
}
// if (this.name === 'ProfileWhereUniqueInput') {
// console.log({ fieldname: field.name, hasConstraints });
// }

// validators and omitField should only be written for create and update types.
// this prevents validation in e.g. search queries in "where inputs",
Expand Down
2 changes: 1 addition & 1 deletion packages/generator/src/classes/extendedDMMFSchemaArg.ts
Expand Up @@ -125,7 +125,7 @@ export class ExtendedDMMFSchemaArg
* @returns `true` if the arg.name matches one of `create|update|upsert|delete|data`
*/
rewriteArgWithNewType() {
return !!this.name.match(/create|update|upsert|delete|data/);
return /create|update|upsert|delete|data/.test(this.name);
}

getImports(fieldName: string) {
Expand Down
72 changes: 47 additions & 25 deletions packages/generator/src/classes/extendedDMMFSchemaField.ts
@@ -1,15 +1,15 @@
import { DMMF } from '@prisma/generator-helper';

import { ExtendedDMMFDatamodel } from './extendedDMMFDatamodel';
import { ExtendedDMMFModel } from './extendedDMMFModel';
import { ExtendedDMMFSchemaArg } from './extendedDMMFSchemaArg';
import { FormattedNames } from './formattedNames';
import {
FilterdPrismaAction,
PRISMA_ACTION_ARG_MAP,
PRISMA_ACTION_ARRAY,
} from '../constants/objectMaps';
import { GeneratorConfig } from '../schemas';
import { ExtendedDMMFDatamodel } from './extendedDMMFDatamodel';
import { ExtendedDMMFModel } from './extendedDMMFModel';
import { ExtendedDMMFSchemaArg } from './extendedDMMFSchemaArg';
import { FormattedNames } from './formattedNames';

/////////////////////////////////////////////////
// REGEX
Expand All @@ -24,6 +24,9 @@ const WRITE_INCLUDE_SELECT_FIELDS_REGEX =

const WRITE_NO_INCLUDE_SELECT_FIELDS_REGEX = /createMany|updateMany|deleteMany/;

// const MUTEX_FIELDS_REGEX = /create|update|upsert/;
// const MUTEX_FIELDS_MANY_REGEX = /createMany|updateMany/;

/////////////////////////////////////////////////
// CLASS
/////////////////////////////////////////////////
Expand Down Expand Up @@ -294,8 +297,8 @@ export class ExtendedDMMFSchemaField
);
}

private _addIncludeToOmitUnionArray(omitUnionArray: string[]) {
if (
private _shouldAddIncludeToOmitUnionArray() {
return (
// "include" or "select" should be added to omit union when they match the regex pattern
this._setWriteSelectAndIncludeArgs() &&
// "include" should be added to omit union when field is of type "outputObjectType"
Expand All @@ -304,24 +307,34 @@ export class ExtendedDMMFSchemaField
!this.generatorConfig.addIncludeType &&
// "include" should be added to omit union when it has relation fields
this.linkedModel?.hasRelationFields
) {
omitUnionArray.push('"include"');
}
);
}

private _addSelectToOmitUnionArray(omitUnionArray: string[]) {
if (
private _shouldAddSelectToOmitUnionArray() {
return (
// "include" or "select" should be added to omit union when they match the regex pattern
this._setWriteSelectAndIncludeArgs() &&
// "select" should be added to omit union when field is of type "outputObjectType"
this._setWriteSelectField() &&
// "select" should be added to omit union when it is set to be omitted via generator config
!this.generatorConfig.addSelectType
) {
omitUnionArray.push('"select"');
}
);
}

/**
* Used to determine if the field contains a union that
* should be mutually exclusive as in prismas `Without<..>` type
* used in `create`, `update` and `upsert` args.
*/
// private _shouldAddDataToOmitUnionArray() {
// return (
// // check if the field contains `create`, `upsert`o `update` in its name
// MUTEX_FIELDS_REGEX.test(this.name) &&
// // check if the field does not contains `createMany` or `updateMany` in its name
// !MUTEX_FIELDS_MANY_REGEX.test(this.name)
// );
// }

private _getOmitFieldsUnion(omitUnionArray: string[]) {
return omitUnionArray.join(' | ');
}
Expand All @@ -343,8 +356,17 @@ export class ExtendedDMMFSchemaField
private _setCustomArgType() {
const omitUnionArray: string[] = [];

this._addSelectToOmitUnionArray(omitUnionArray);
this._addIncludeToOmitUnionArray(omitUnionArray);
// if (this._shouldAddDataToOmitUnionArray()) {
// omitUnionArray.push('"data"');
// }

if (this._shouldAddSelectToOmitUnionArray()) {
omitUnionArray.push('"select"');
}

if (this._shouldAddIncludeToOmitUnionArray()) {
omitUnionArray.push('"include"');
}

if (this._shouldAddOmittedFieldsToOmitUnionArray()) {
this._addOmittedFieldsToOmitUnionArray(omitUnionArray);
Expand Down Expand Up @@ -388,6 +410,15 @@ export class ExtendedDMMFSchemaField
return `${arg.name}${arg.isRequired ? '' : '?'}: `;
}

/**
* Returns the union of types or a single type.
*/
private _getCustomArgsType(arg: ExtendedDMMFSchemaArg) {
return arg.hasMultipleTypes
? this._getCustomArgsMultipleTypes(arg)
: this._getCustomArgsSingleType(arg);
}

/**
* If the arg has multiple types, the type is a union of the types.
*/
Expand All @@ -409,15 +440,6 @@ export class ExtendedDMMFSchemaField
return `z.infer<typeof ${arg.inputTypes[0].type}Schema>`;
}

/**
* Returns the union of types or a single type.
*/
private _getCustomArgsType(arg: ExtendedDMMFSchemaArg) {
return arg.hasMultipleTypes
? this._getCustomArgsMultipleTypes(arg)
: this._getCustomArgsSingleType(arg);
}

// HELPER METHODS
//---------------------------------------------------------------------

Expand Down
Expand Up @@ -9,17 +9,13 @@ export const writeDecimalJsLike = ({

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

writer
.blankLine()
.writeLine(
`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(
`export type DecimalJSLike = z.infer<typeof DecimalJSLikeSchema>;`,
`export const DecimalJSLikeSchema: z.ZodType<Prisma.DecimalJsLike> = z.object({ d: z.array(z.number()), e: z.number(), s: z.number(), toFixed: z.function().args().returns(z.string()), });`,
);

if (useMultipleFiles && !getSingleFileContent) {
Expand Down
Expand Up @@ -9,13 +9,13 @@ export const writeDecimalJsLikeList = ({

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

writer
.blankLine()
.writeLine(
`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();`,
`export const DecimalJSLikeListSchema: z.ZodType<Prisma.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 @@ -5,10 +5,10 @@ export const writeIsValidDecimalInput = ({
dmmf,
getSingleFileContent = false,
}: ContentWriterOptions) => {
const { useMultipleFiles } = dmmf.generatorConfig;
const { useMultipleFiles, prismaClientPath } = dmmf.generatorConfig;

if (useMultipleFiles && !getSingleFileContent) {
writeImport('type { DecimalJSLike }', `./DecimalJsLikeSchema`);
writeImport('type { Prisma }', `${prismaClientPath}`);
}

writer
Expand All @@ -21,11 +21,11 @@ export const writeIsValidDecimalInput = ({
.withIndentationLevel(1, () => {
writer
.write(
`(v?: null | string | number | DecimalJSLike): v is string | number | DecimalJSLike => `,
`(v?: null | string | number | Prisma.DecimalJsLike): v is string | number | Prisma.DecimalJsLike => `,
)
.inlineBlock(() => {
writer
.writeLine(`if (!v) return false;`)
.writeLine(`if (v === undefined || v === null) return false;`)
.writeLine(`return (`)
.withIndentationLevel(3, () => {
writer
Expand Down
Expand Up @@ -20,11 +20,9 @@ export const writeDecimal = ({
.write(`)`)
.write(`.refine((v) => isValidDecimalInput(v),`)
.write(
` { message: 'Field "${field.formattedNames.original}" must be a Decimal', `,
` { message: "Field '${field.formattedNames.original}' must be a Decimal. Location: ['Models', '${model.formattedNames.original}']", `,
)
.write(`path: ['Models', '${model.formattedNames.original}']`)
.write(` })`);
// .write(`.transform((v) => new Prism.Decimal(v))`);

writeFieldAdditions({ writer, field, writeOptionalDefaults });
};
58 changes: 25 additions & 33 deletions packages/generator/src/functions/fieldWriters/writeSpecialType.ts
Expand Up @@ -41,46 +41,38 @@ export const writeSpecialType: WriteTypeFunction<WriteTypeOptions> = (

if (inputType.isDecimalType) {
if (inputType.isList) {
return (
writer
.write(`z.union([`)
.write(`z.number().array(),`)
.write(`z.string().array(),`)
.write(`DecimalJSLikeListSchema,`)
.write(`]`)
.conditionalWrite(!!zodCustomErrors, `, ${zodCustomErrors!}`)
.write(`)`)

.write(`.refine((v) => `)
.write(
`Array.isArray(v) && (v as any[]).every((v) => isValidDecimalInput(v)),`,
)
.write(` { message: 'Must be a Decimal' })`)
// .write(
// `.transform(Array.isArray(v) && (v as any[]).every((v) => new Prisma.Decimal(v)))`,
// )
.conditionalWrite(isOptional, `.optional()`)
.conditionalWrite(isNullable, `.nullable()`)
.conditionalWrite(writeComma, `,`)
);
}

return (
writer
return writer
.write(`z.union([`)
.write(`z.number(),`)
.write(`z.string(),`)
.write(`DecimalJSLikeSchema,`)
.write(`z.number().array(),`)
.write(`z.string().array(),`)
.write(`DecimalJSLikeListSchema,`)
.write(`]`)
.conditionalWrite(!!zodCustomErrors, `, ${zodCustomErrors!}`)
.write(`)`)
.write(`.refine((v) => isValidDecimalInput(v),`)

.write(`.refine((v) => `)
.write(
`Array.isArray(v) && (v as any[]).every((v) => isValidDecimalInput(v)),`,
)
.write(` { message: 'Must be a Decimal' })`)
// .write(`.transform((v) => new Prisma.Decimal(v))`)
.conditionalWrite(isOptional, `.optional()`)
.conditionalWrite(isNullable, `.nullable()`)
.conditionalWrite(writeComma, `,`)
);
.conditionalWrite(writeComma, `,`);
}

return writer
.write(`z.union([`)
.write(`z.number(),`)
.write(`z.string(),`)
.write(`DecimalJSLikeSchema,`)
.write(`]`)
.conditionalWrite(!!zodCustomErrors, `, ${zodCustomErrors!}`)
.write(`)`)
.write(`.refine((v) => isValidDecimalInput(v),`)
.write(` { message: 'Must be a Decimal' })`)
.conditionalWrite(isOptional, `.optional()`)
.conditionalWrite(isNullable, `.nullable()`)
.conditionalWrite(writeComma, `,`);
}

if (inputType.isJsonType) {
Expand Down
Expand Up @@ -18,7 +18,7 @@ export const writeSingleFileImportStatements: WriteStatements = (
if (dmmf.schema.hasJsonTypes) {
writeImport(`{ Prisma }`, `${prismaClientPath}`);
} else {
writeImport(`{ type Prisma }`, `${prismaClientPath}`);
writeImport(`type { Prisma }`, `${prismaClientPath}`);
}

if (dmmf.customImports) {
Expand Down
10 changes: 5 additions & 5 deletions packages/usage/package.json
Expand Up @@ -13,23 +13,23 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^18.14.5",
"@types/node": "^18.15.0",
"prisma": "^4.11.0",
"typescript": "^4.9.5"
},
"dependencies": {
"@prisma/client": "^4.11.0",
"@trpc/client": "^10.14.0",
"@trpc/server": "^10.14.0",
"@trpc/client": "^10.15.0",
"@trpc/server": "^10.15.0",
"@types/validator": "^13.7.13",
"cross-fetch": "^3.1.5",
"decimal.js": "^10.4.3",
"prisma-zod-generator": "^0.7.4",
"prisma-zod-generator": "^0.8.11",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"validator": "^13.9.0",
"vitest": "^0.25.8",
"zod": "^3.21.4",
"zod-prisma-types": "workspace:2.3.6"
"zod-prisma-types": "workspace:2.4.1"
}
}
11 changes: 8 additions & 3 deletions packages/usage/prisma/schema.prisma
Expand Up @@ -20,12 +20,12 @@ generator zod {
provider = "ts-node-dev ../generator/src/bin.ts"
// provider = "zod-prisma-types"
output = "./generated/zod" // default is ./generated/zod
useMultipleFiles = true // default is false
// useMultipleFiles = true // default is false
// createInputTypes = false // default is true
// createModelTypes = false // default is true
// addInputTypeValidation = false // default is true
addIncludeType = false // default is true
addSelectType = false // default is true
// addIncludeType = false // default is true
// addSelectType = false // default is true
// validateWhereUniqueInput = true // default is false
createOptionalDefaultValuesTypes = true // default is false
createRelationValuesTypes = true // default is false
Expand All @@ -36,6 +36,11 @@ generator zod {
// prismaClientPath = "./generated/client" // optional
}

generator zodPrismaGen {
provider = "prisma-zod-generator"
output = "./generated/zodPrisma"
}

// MIXED CASE ENUMS AND MODELS
// -----------------------------------------------

Expand Down

0 comments on commit a7bc182

Please sign in to comment.