Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# prismabox
Generate versatile [typebox](https://github.com/sinclairzx81/typebox) schemes from your [prisma](https://github.com/prisma) schema.

> Currently does not support [mongoDB composite types](https://www.prisma.io/docs/orm/prisma-schema/data-model/models#defining-composite-types)

Install it in your project,
```bash
npm i -D prismabox
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"scripts": {
"dev": "bun run build && bunx prisma generate",
"dev-mongo": "bun build.ts && bunx prisma generate --schema ./prisma/mongo-schema.prisma",
"build": "bun run typecheck && bun build.ts",
"lint": "biome check --write .",
"typecheck": "tsc --noEmit"
Expand Down
76 changes: 76 additions & 0 deletions src/generators/composite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { DMMF } from "@prisma/generator-helper";
import { extractAnnotations } from "../annotations/annotations";
import { generateTypeboxOptions } from "../annotations/options";
import { getConfig } from "../config";
import type { ProcessedModel } from "../model";
import { processedEnums } from "./enum";
import {
type PrimitivePrismaFieldType,
isPrimitivePrismaFieldType,
stringifyPrimitiveType,
} from "./primitiveField";
import { wrapWithArray } from "./wrappers/array";
import { wrapWithNullable } from "./wrappers/nullable";
import { wrapWithOptional } from "./wrappers/optional";

export const processedComposites: ProcessedModel[] = [];

export function processComposites(
types: DMMF.Model[] | Readonly<DMMF.Model[]>,
) {
for (const t of types) {
const o = stringifyComposite(t);
if (o) {
processedComposites.push({ name: t.name, stringRepresentation: o });
}
}
Object.freeze(processedComposites);
}

export function stringifyComposite(
data: DMMF.Model,
isInputModelCreate = false,
isInputModelUpdate = false,
) {
const annotations = extractAnnotations(data.documentation);

if (
annotations.isHidden ||
((isInputModelCreate || isInputModelUpdate) && annotations.isHiddenInput) ||
(isInputModelCreate && annotations.isHiddenInputCreate) ||
(isInputModelUpdate && annotations.isHiddenInputUpdate)
)
return undefined;

const fields = data.fields
.map((field) => {
let stringifiedType = `${getConfig().typeboxImportVariableName}.Object({
})`;

if (isPrimitivePrismaFieldType(field.type)) {
stringifiedType = stringifyPrimitiveType({
fieldType: field.type as PrimitivePrismaFieldType,
options: generateTypeboxOptions({ input: annotations }),
});
} else if (processedEnums.find((e) => e.name === field.type)) {
// biome-ignore lint/style/noNonNullAssertion: we checked this manually
stringifiedType = processedEnums.find(
(e) => e.name === field.type,
)!.stringRepresentation;
}

if (field.kind === "object") {
stringifiedType = field.type;
}

return `${field.name}: ${stringifiedType}`;
})
.filter((x) => x) as string[];

return `${getConfig().typeboxImportVariableName}.Object({${[
...fields,
!(isInputModelCreate || isInputModelUpdate)
? (getConfig().additionalFieldsPlain ?? [])
: [],
].join(",")}},${generateTypeboxOptions({ input: annotations })})\n`;
}
6 changes: 6 additions & 0 deletions src/generators/plain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { generateTypeboxOptions } from "../annotations/options";
import { getConfig } from "../config";
import type { ProcessedModel } from "../model";
import { processedComposites } from "./composite";
import { processedEnums } from "./enum";
import {
type PrimitivePrismaFieldType,
Expand Down Expand Up @@ -113,6 +114,11 @@ export function stringifyPlain(
stringifiedType = processedEnums.find(
(e) => e.name === field.type,
)!.stringRepresentation;
} else if (processedComposites.find((c) => c.name === field.type)) {
// biome-ignore lint/style/noNonNullAssertion: we checked this manually
stringifiedType = processedComposites.find(
(c) => c.name === field.type,
)!.stringRepresentation;
} else {
return undefined;
}
Expand Down
20 changes: 11 additions & 9 deletions src/generators/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { extractAnnotations } from "../annotations/annotations";
import { generateTypeboxOptions } from "../annotations/options";
import { getConfig } from "../config";
import type { ProcessedModel } from "../model";
import { processedComposites } from "./composite";
import { processedEnums } from "./enum";
import { processedPlain } from "./plain";
import { isPrimitivePrismaFieldType } from "./primitiveField";
Expand Down Expand Up @@ -35,7 +36,8 @@ export function stringifyRelations(data: DMMF.Model) {
if (
annotations.isHidden ||
isPrimitivePrismaFieldType(field.type) ||
processedEnums.find((e) => e.name === field.type)
processedEnums.find((e) => e.name === field.type) ||
processedComposites.find((c) => c.name === field.type)
) {
return undefined;
}
Expand Down Expand Up @@ -110,10 +112,10 @@ export function stringifyRelationsInputCreate(

let typeboxIdType = "String";

switch (
allModels.find((m) => m.name === field.type)?.fields.find((f) => f.isId)
?.type
) {
const t = allModels
.find((m) => m.name === field.type)
?.fields.find((f) => f.isId)?.type;
switch (t) {
case "String":
typeboxIdType = "String";
break;
Expand Down Expand Up @@ -203,10 +205,10 @@ export function stringifyRelationsInputUpdate(

let typeboxIdType = "String";

switch (
allModels.find((m) => m.name === field.type)?.fields.find((f) => f.isId)
?.type
) {
const t = allModels
.find((m) => m.name === field.type)
?.fields.find((f) => f.isId)?.type;
switch (t) {
case "String":
typeboxIdType = "String";
break;
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { access, mkdir, rm } from "node:fs/promises";
import { generatorHandler } from "@prisma/generator-helper";
import { getConfig, setConfig } from "./config";
import { processComposites } from "./generators/composite";
import { processEnums } from "./generators/enum";
import { processInclude } from "./generators/include";
import { processOrderBy } from "./generators/orderBy";
Expand Down Expand Up @@ -38,6 +39,7 @@ generatorHandler({
await mkdir(getConfig().output, { recursive: true });

processEnums(options.dmmf.datamodel.enums);
processComposites(options.dmmf.datamodel.types);
processPlain(options.dmmf.datamodel.models);
processRelations(options.dmmf.datamodel.models);
processWhere(options.dmmf.datamodel.models);
Expand Down
2 changes: 2 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getConfig } from "./config";
import { processedComposites } from "./generators/composite";
import { processedEnums } from "./generators/enum";
import { processedInclude } from "./generators/include";
import { processedOrderBy } from "./generators/orderBy";
Expand Down Expand Up @@ -60,6 +61,7 @@ export function mapAllModelsForWrite() {
process(processedEnums, "");
process(processedPlain, "Plain");
process(processedRelations, "Relations");
process(processedComposites, "");
process(processedPlainInputCreate, "PlainInputCreate");
process(processedPlainInputUpdate, "PlainInputUpdate");
process(processedRelationsInputCreate, "RelationsInputCreate");
Expand Down