infoalloggi-monorepo/apps/infoalloggi/kanel.config.js

193 lines
5.8 KiB
JavaScript
Raw Normal View History

2025-08-04 17:45:44 +02:00
const { makeKyselyHook } = require("kanel-kysely");
const { makePgTsGenerator } = require("kanel");
const { generateZodSchemas, makeGenerateZodSchemas, defaultZodTypeMap } = require("kanel-zod");
const ignoredTables = ["schema_migrations"];
2025-08-04 17:45:44 +02:00
/**
* Allows for injection of custom types from the mapping of `table.field`
* @param {import("kanel").TypeMap} mappings
* @returns {import("kanel").PreRenderHook}
*/
const specificTypes = (mappings) => {
/** @type {import("kanel").PreRenderHook} */
return async (outputAcc, instantiatedConfig) => {
const output = { ...outputAcc };
for (const path of Object.keys(output)) {
const file = outputAcc[path];
const declarations = file.declarations.map((decl) => {
const declaration = { ...decl };
if (declaration.declarationType === "interface") {
for (const field of declaration.properties) {
const key = `${declaration.name}.${field.name}`;
if (mappings[key]) {
field.typeName = mappings[key].name;
declaration.typeImports = [
...(declaration.typeImports || []),
...mappings[key].typeImports,
];
}
}
}
return declaration;
});
output[path] = {
...file,
declarations,
};
}
return output;
};
}
const kanelZodCastRegex = /as unknown as z.Schema<(.*?)(Mutator|Initializer)>/;
const zodItemInitializer = /^export const \w+ = z\.object/
/**
* @type {import("kanel").PostRenderHook}
*
* Renames the type of the `as unknown as z.Schema` casts from `kanel-zod` to
* to be compatible with `kanel-kysely`, turning
* 1. `as unknown as z.Schema<TableMutator>` into `as unknown as z.Schema<TableUpdate>`
* 2. `as unknown as z.Schema<TableInitializer>` into `as unknown as z.Schema<NewTable>`
*/
function kanelKyselyZodCompatibilityHook(path, lines) {
return lines.map((line) => {
if (zodItemInitializer.test(line)) {
const parts = line.split(" ");
if (parts[2].includes("Initializer")) {
const nome = parts[2].replace("Initializer", "");
parts[2] = "New" + nome[0].toUpperCase() + nome.slice(1) + "Schema";
return parts.join(" ");
}
if (parts[2].includes("Mutator")) {
const nome = parts[2].replace("Mutator", "");
parts[2] = nome[0].toUpperCase() + nome.slice(1) + "UpdateSchema";
return parts.join(" ");
}
const nome = parts[2];
parts[2] = nome[0].toUpperCase() + nome.slice(1) + "Schema";
return parts.join(" ");
}
if (line.includes("as unknown as z.Schema")) {
const replacedLine = line.replace(
kanelZodCastRegex,
(_, typeName, mutatorOrInitializer) => {
if (!mutatorOrInitializer) {
return `as unknown as z.Schema<${typeName}>`;
}
if (mutatorOrInitializer === "Mutator") {
return `as unknown as z.Schema<${typeName}Update>`;
}
return `as unknown as z.Schema<New${typeName}>`;
}
);
return replacedLine;
}
return line;
});
}
/**@type {import("kanel").PostRenderHook} */
function zodCaratteristicheHook(path, lines) {
if (path.includes("src\\schemas\\public\\Annunci.ts")) {
return lines.map(line => {
if (line.includes("kanel-types")) {
return "import { CaratteristicheSchema, type Caratteristiche } from '../../utils/kanel-types';"
}
if (line.includes("caratteristiche: z.unknown().optional().nullable()")) {
return line.replace("caratteristiche: z.unknown().optional().nullable()", "caratteristiche: CaratteristicheSchema.optional().nullable()")
}
if (line.includes("caratteristiche: z.unknown().nullable()")) {
return line.replace("caratteristiche: z.unknown().nullable()", "caratteristiche: CaratteristicheSchema.nullable()")
}
return line;
})
}
return lines
}
/**@type {import("kanel").PostRenderHook} */
function zodUpdateInsertionDatesFix(path, lines) {
//z.date() -> z.union([z.date(), z.string()]).pipe(z.coerce.date())
let insideUpdateOrInsert = false;
return lines.map(line => {
if (line.includes("export const") && line.includes("UpdateSchema")) {
insideUpdateOrInsert = true;
}
if (line.includes("export const") && line.includes("New")) {
insideUpdateOrInsert = true;
}
if (insideUpdateOrInsert && line.includes("z.date()")) {
return line.replace("z.date()", "z.union([z.date(), z.string()]).pipe(z.coerce.date())");
}
if (line.includes("import")) {
insideUpdateOrInsert = false;
}
return line;
})
}
2025-08-04 17:45:44 +02:00
/** @type {import('kanel').Config} */
module.exports = {
connection: {
host: "localhost",
user: "postgres",
database: "postgres",
password: "rootpost",
port: 5433,
2025-08-04 17:45:44 +02:00
},
filter: (pgTable) => !ignoredTables.includes(pgTable.name),
2025-08-04 17:45:44 +02:00
outputPath: "./src/schemas",
generators: [
makePgTsGenerator({
customTypeMap: {
"pg_catalog.tsvector": "string",
"pg_catalog.bpchar": "string",
'pg_catalog.numeric': 'number',
//"pg_catalog.json": "string",
2026-05-06 17:19:40 +02:00
'pg_catalog.bytea': "Buffer"
},
preRenderHooks: [
specificTypes({
"Annunci.caratteristiche": {
name: "Caratteristiche",
typeImports: [{ name: "Caratteristiche", path: "src/utils/kanel-types.ts", importAsType: true, isAbsolute: false, isDefault: false }],
},
}),
makeKyselyHook(),
makeGenerateZodSchemas({
castToSchema: false,
zodTypeMap: {
...defaultZodTypeMap,
"pg_catalog.numeric": "z.number()",
2026-05-06 17:19:40 +02:00
"pg_catalog.bytea": "z.instanceof(Buffer)"
}
}),
],
}),
],
postRenderHooks: [kanelKyselyZodCompatibilityHook, zodCaratteristicheHook, zodUpdateInsertionDatesFix],
preDeleteOutputFolder: true,
2025-08-04 17:45:44 +02:00
};