Skip to content

Commit

Permalink
✨ feat: add input create/hide annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
m1212e committed Aug 25, 2024
1 parent efcab66 commit f1dffd1
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 61 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Prismabox offers annotations to adjust the output of models and fields.
| @prismabox.hide | - | Hides the field or model from the output |
| @prismabox.hidden | - | Alias for @prismabox.hide |
| @prismabox.input.hide | - | Hides the field or model from the output only in the input model |
|@prismabox.create.input.hide| - | Hides the field or model from the outputs only in the input create model|
|@prismabox.update.input.hide| - | Hides the field or model from the outputs only in the input update model|
| @prismabox.options | @prismabox.options{ min: 10, max: 20 } | Uses the provided options for the field or model in the generated schema. Be careful to use valid JS/TS syntax! |
> For a more detailed list of available annotations, please see [annotations.ts](src/annotations/annotations.ts)
Expand Down
50 changes: 42 additions & 8 deletions src/annotations/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,49 @@ import type { DMMF } from "@prisma/generator-helper";
export type Annotation =
| { type: "HIDDEN" }
| { type: "HIDDEN_INPUT" }
| { type: "HIDDEN_INPUT_CREATE" }
| { type: "HIDDEN_INPUT_UPDATE" }
| { type: "OPTIONS"; value: string };

export function isHiddenVariant(
annotation: Annotation,
annotation: Annotation
): annotation is { type: "HIDDEN"; value: number } {
return annotation.type === "HIDDEN";
}

export function isHiddenInputVariant(
annotation: Annotation,
annotation: Annotation
): annotation is { type: "HIDDEN_INPUT"; value: number } {
return annotation.type === "HIDDEN_INPUT";
}

export function isHiddenInputCreateVariant(
annotation: Annotation
): annotation is { type: "HIDDEN_INPUT_CREATE"; value: number } {
return annotation.type === "HIDDEN_INPUT_CREATE";
}

export function isHiddenInputUpdateVariant(
annotation: Annotation
): annotation is { type: "HIDDEN_INPUT_UPDATE"; value: number } {
return annotation.type === "HIDDEN_INPUT_UPDATE";
}

export function isOptionsVariant(
annotation: Annotation,
annotation: Annotation
): annotation is { type: "OPTIONS"; value: string } {
return annotation.type === "OPTIONS";
}

const annotationKeys: { type: Annotation["type"]; keys: string[] }[] = [
{
type: "HIDDEN_INPUT_CREATE",
keys: ["@prismabox.create.input.hide", "@prismabox.create.input.hidden"],
},
{
type: "HIDDEN_INPUT_UPDATE",
keys: ["@prismabox.update.input.hide", "@prismabox.update.input.hidden"],
},
{
type: "HIDDEN_INPUT",
keys: [
Expand All @@ -44,12 +66,14 @@ const annotationKeys: { type: Annotation["type"]; keys: string[] }[] = [
];

export function extractAnnotations(
input: DMMF.Model["fields"][number]["documentation"],
input: DMMF.Model["fields"][number]["documentation"]
): {
annotations: Annotation[];
description: string | undefined;
isHidden: boolean;
isHiddenInput: boolean;
isHiddenInputCreate: boolean;
isHiddenInputUpdate: boolean;
} {
const annotations: Annotation[] = [];
let description = "";
Expand All @@ -61,27 +85,27 @@ export function extractAnnotations(
.map((l) => l.trim())
.filter((l) => l.length > 0)) {
const annotationKey = annotationKeys.find((key) =>
key.keys.some((k) => line.startsWith(k)),
key.keys.some((k) => line.startsWith(k))
);

if (annotationKey) {
if (annotationKey.type === "OPTIONS") {
if (!line.startsWith(`${annotationKey.keys[0]}{`)) {
throw new Error(
"Invalid syntax, expected opening { after prismabox.options",
"Invalid syntax, expected opening { after prismabox.options"
);
}
if (!line.endsWith("}")) {
throw new Error(
"Invalid syntax, expected closing } for prismabox.options",
"Invalid syntax, expected closing } for prismabox.options"
);
}

annotations.push({
type: "OPTIONS",
value: line.substring(
annotationKey.keys[0].length + 1,
line.length - 1,
line.length - 1
),
});
} else {
Expand All @@ -98,6 +122,8 @@ export function extractAnnotations(
description: description.length > 0 ? description : undefined,
isHidden: isHidden(annotations),
isHiddenInput: isHiddenInput(annotations),
isHiddenInputCreate: isHiddenInputCreate(annotations),
isHiddenInputUpdate: isHiddenInputUpdate(annotations),
};
}

Expand All @@ -108,3 +134,11 @@ export function isHidden(annotations: Annotation[]): boolean {
export function isHiddenInput(annotations: Annotation[]): boolean {
return annotations.some((a) => a.type === "HIDDEN_INPUT");
}

export function isHiddenInputCreate(annotations: Annotation[]): boolean {
return annotations.some((a) => a.type === "HIDDEN_INPUT_CREATE");
}

export function isHiddenInputUpdate(annotations: Annotation[]): boolean {
return annotations.some((a) => a.type === "HIDDEN_INPUT_UPDATE");
}
42 changes: 32 additions & 10 deletions src/generators/plain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,40 +25,60 @@ export function processPlain(models: DMMF.Model[] | Readonly<DMMF.Model[]>) {
Object.freeze(processedPlain);
}

export function stringifyPlain(data: DMMF.Model, isInputModel = false) {
export function stringifyPlain(
data: DMMF.Model,
isInputModelCreate = false,
isInputModelUpdate = false
) {
const annotations = extractAnnotations(data.documentation);
if (annotations.isHidden || (isInputModel && annotations.isHiddenInput))

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

const fields = data.fields
.map((field) => {
const annotations = extractAnnotations(field.documentation);
if (annotations.isHidden || (isInputModel && annotations.isHiddenInput))
if (
annotations.isHidden ||
((isInputModelCreate || isInputModelUpdate) &&
annotations.isHiddenInput) ||
(isInputModelCreate && annotations.isHiddenInputCreate) ||
(isInputModelUpdate && annotations.isHiddenInputUpdate)
)
return undefined;

// ===============================
// INPUT MODEL FILTERS
// ===============================
// if we generate an input model we want to omit certain fields

if (getConfig().ignoreIdOnInputModel && isInputModel && field.isId)
if (
getConfig().ignoreIdOnInputModel &&
(isInputModelCreate || isInputModelUpdate) &&
field.isId
)
return undefined;
if (
getConfig().ignoreCreatedAtOnInputModel &&
isInputModel &&
(isInputModelCreate || isInputModelUpdate) &&
field.name === "createdAt" &&
field.hasDefaultValue
)
return undefined;
if (
getConfig().ignoreUpdatedAtOnInputModel &&
isInputModel &&
(isInputModelCreate || isInputModelUpdate) &&
field.isUpdatedAt
)
return undefined;

if (
isInputModel &&
(isInputModelCreate || isInputModelUpdate) &&
(field.name.toLowerCase().endsWith("id") ||
field.name.toLowerCase().endsWith("foreign") ||
field.name.toLowerCase().endsWith("foreignkey"))
Expand All @@ -80,7 +100,7 @@ export function stringifyPlain(data: DMMF.Model, isInputModel = false) {
} 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,
(e) => e.name === field.type
)!.stringRepresentation;
} else {
return undefined;
Expand All @@ -92,7 +112,7 @@ export function stringifyPlain(data: DMMF.Model, isInputModel = false) {

if (!field.isRequired) {
stringifiedType = wrapWithNullable(stringifiedType);
if (isInputModel) {
if (isInputModelCreate || isInputModelUpdate) {
stringifiedType = wrapWithOptional(stringifiedType);
}
}
Expand All @@ -103,6 +123,8 @@ export function stringifyPlain(data: DMMF.Model, isInputModel = false) {

return `${getConfig().typeboxImportVariableName}.Object({${[
...fields,
!isInputModel ? getConfig().additionalFieldsPlain ?? [] : [],
!(isInputModelCreate || isInputModelUpdate)
? getConfig().additionalFieldsPlain ?? []
: [],
].join(",")}},${generateTypeboxOptions({ input: annotations })})\n`;
}
17 changes: 0 additions & 17 deletions src/generators/plainInput.ts

This file was deleted.

17 changes: 17 additions & 0 deletions src/generators/plainInputCreate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { DMMF } from "@prisma/generator-helper";
import type { ProcessedModel } from "../model";
import { stringifyPlain } from "./plain";

export const processedPlainInputCreate: ProcessedModel[] = [];

export function processPlainInputCreate(
models: DMMF.Model[] | Readonly<DMMF.Model[]>,
) {
for (const m of models) {
const o = stringifyPlain(m, true, false);
if (o) {
processedPlainInputCreate.push({ name: m.name, stringRepresentation: o });
}
}
Object.freeze(processedPlainInputCreate);
}
17 changes: 17 additions & 0 deletions src/generators/plainInputUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { DMMF } from "@prisma/generator-helper";
import type { ProcessedModel } from "../model";
import { stringifyPlain } from "./plain";

export const processedPlainInputUpdate: ProcessedModel[] = [];

export function processPlainInputUpdate(
models: DMMF.Model[] | Readonly<DMMF.Model[]>
) {
for (const m of models) {
const o = stringifyPlain(m, false, true);
if (o) {
processedPlainInputUpdate.push({ name: m.name, stringRepresentation: o });
}
}
Object.freeze(processedPlainInputUpdate);
}
Loading

0 comments on commit f1dffd1

Please sign in to comment.