Skip to content

Commit

Permalink
✨ feat: implement typebox transform types for json schema compatibili…
Browse files Browse the repository at this point in the history
…ty option
  • Loading branch information
m1212e committed Nov 18, 2024
1 parent b45bfae commit 3f90151
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 21 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@prisma/generator-helper": "^5.19.1",
"@sinclair/typebox": "^0.33.11",
"@prisma/generator-helper": "^5.22.0",
"@sinclair/typebox": "^0.34.3",
"prettier": "^3.3.3"
},
"devDependencies": {
"@biomejs/biome": "^1.9.1",
"@prisma/client": "5.19.1",
"@biomejs/biome": "^1.9.4",
"@prisma/client": "5.22.0",
"@types/bun": "latest",
"esbuild": "^0.23.1",
"prisma": "5.19.1",
"typescript": "^5.6.2"
"esbuild": "^0.24.0",
"prisma": "5.22.0",
"typescript": "^5.6.3"
}
}
6 changes: 5 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ const configSchema = Type.Object(
```
*/
additionalFieldsPlain: Type.Optional(Type.Array(Type.String())),
/**
* How the transform date type should be named
*/
transformDateName: Type.String({ default: "__transformDate__" }),
/**
* When enabled, this option ensures that only primitive types are generated as JSON types.
* This ensures compatibility with tooling that only supports the serilization to JSON primitive types.
Expand All @@ -72,7 +76,7 @@ const configSchema = Type.Object(
*/
exportedTypePrefix: Type.String({ default: "" }),
},
{ additionalProperties: false },
{ additionalProperties: false }
);

// biome-ignore lint/suspicious/noExplicitAny: we want to set the default value
Expand Down
10 changes: 2 additions & 8 deletions src/generators/primitiveField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const PrimitiveFields = [
export type PrimitivePrismaFieldType = (typeof PrimitiveFields)[number];

export function isPrimitivePrismaFieldType(
str: string,
str: string
): str is PrimitivePrismaFieldType {
// biome-ignore lint/suspicious/noExplicitAny: we want to check if the string is a valid primitive field
return PrimitiveFields.includes(str as any);
Expand All @@ -42,14 +42,8 @@ export function stringifyPrimitiveType({

if (["DateTime"].includes(fieldType)) {
const config = getConfig();
let opts = options;
if (config.useJsonTypes) {
if (opts.includes("{") && opts.includes("}")) {
opts = opts.replace("{", "{ format: 'date-time', ");
} else {
opts = `{ format: 'date-time' }`;
}
return `${config.typeboxImportVariableName}.String(${opts})`;
return `${getConfig().transformDateName}(${options})`;
}

return `${getConfig().typeboxImportVariableName}.Date(${options})`;
Expand Down
16 changes: 16 additions & 0 deletions src/generators/transformDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getConfig } from "../config";

export function transformDateType() {
return `import { type StringOptions, ${getConfig().typeboxImportVariableName} } from "${getConfig().typeboxImportDependencyName}";
export const ${getConfig().transformDateName} = (options?: StringOptions) => ${
getConfig().typeboxImportVariableName
}.Transform(${getConfig().typeboxImportVariableName}.String({ format: 'date-time', ...options }))
.Decode((value) => new Date(value))
.Encode((value) => value.toISOString())\n`;
}

export function transformDateImportStatement() {
return `import { ${getConfig().transformDateName} } from "./${
getConfig().transformDateName
}${getConfig().importFileExtension}"\n`;
}
15 changes: 10 additions & 5 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ import { processedSelect } from "./generators/select";
import { processedWhere, processedWhereUnique } from "./generators/where";
import { makeComposite } from "./generators/wrappers/composite";
import { nullableImport, nullableType } from "./generators/wrappers/nullable";
import {
transformDateType,
transformDateImportStatement,
} from "./generators/transformDate";

export type ProcessedModel = {
name: string;
stringRepresentation: string;
};

function convertModelToStandalone(
input: Pick<ProcessedModel, "name" | "stringRepresentation">,
input: Pick<ProcessedModel, "name" | "stringRepresentation">
) {
return `export const ${getConfig().exportedTypePrefix}${input.name} = ${input.stringRepresentation}\n`;
}
Expand Down Expand Up @@ -85,7 +89,7 @@ export function mapAllModelsForWrite() {
`${value}\n${convertModelToStandalone({
name: key,
stringRepresentation: composite,
})}`,
})}`
);
}

Expand All @@ -102,7 +106,7 @@ export function mapAllModelsForWrite() {
`${value}\n${convertModelToStandalone({
name: `${key}InputCreate`,
stringRepresentation: composite,
})}`,
})}`
);
}
}
Expand All @@ -120,19 +124,20 @@ export function mapAllModelsForWrite() {
`${value}\n${convertModelToStandalone({
name: `${key}InputUpdate`,
stringRepresentation: composite,
})}`,
})}`
);
}
}

for (const [key, value] of modelsPerName) {
modelsPerName.set(
key,
`${typepoxImportStatement()}\n${nullableImport()}\n${value}`,
`${typepoxImportStatement()}\n${transformDateImportStatement()}\n${nullableImport()}\n${value}`
);
}

modelsPerName.set(getConfig().nullableName, nullableType());
modelsPerName.set(getConfig().transformDateName, transformDateType());

return modelsPerName;
}

0 comments on commit 3f90151

Please sign in to comment.