-
-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
kanel-zod doesn't play nice with kanel-kesely #563
Comments
Without looking into the details here, have you tried reversing the order? Hooks are applied serially, which is why it's important, say, to run the index generator last. |
I came here to say the same thing. @kristiandupont, I just tried reversing the order of the hooks and I get the |
@ersinakinci are you referring to the "original" types or the Zod schemas? |
@kristiandupont I think they are referring to the fact that when using Here in my example, running only // @generated
// This file is automatically generated by Kanel. Do not modify manually.
import { communitiesId, type CommunitiesId } from './Communities';
import { usersId, type UsersId } from './Users';
import { z } from 'zod';
/** Identifier type for public.members */
export type MembersId = string & { __brand: 'MembersId' };
/** Represents the table public.members */
export default interface Members {
id: MembersId;
createdAt: Date;
updatedAt: Date;
canAdmin: boolean;
communityId: CommunitiesId;
userId: UsersId;
}
/** Represents the initializer for the table public.members */
export interface MembersInitializer {
/** Default value: gen_random_uuid() */
id?: MembersId;
/** Default value: CURRENT_TIMESTAMP */
createdAt?: Date;
/** Default value: CURRENT_TIMESTAMP */
updatedAt?: Date;
canAdmin: boolean;
communityId: CommunitiesId;
userId: UsersId;
}
/** Represents the mutator for the table public.members */
export interface MembersMutator {
id?: MembersId;
createdAt?: Date;
updatedAt?: Date;
canAdmin?: boolean;
communityId?: CommunitiesId;
userId?: UsersId;
}
export const membersId = z.string() as unknown as z.Schema<MembersId>;
export const members = z.object({
id: membersId,
createdAt: z.date(),
updatedAt: z.date(),
canAdmin: z.boolean(),
communityId: communitiesId,
userId: usersId,
}) as unknown as z.Schema<Members>;
export const membersInitializer = z.object({
id: membersId.optional(),
createdAt: z.date().optional(),
updatedAt: z.date().optional(),
canAdmin: z.boolean(),
communityId: communitiesId,
userId: usersId,
}) as unknown as z.Schema<MembersInitializer>;
export const membersMutator = z.object({
id: membersId.optional(),
createdAt: z.date().optional(),
updatedAt: z.date().optional(),
canAdmin: z.boolean().optional(),
communityId: communitiesId.optional(),
userId: usersId.optional(),
}) as unknown as z.Schema<MembersMutator>; which works. However, when i also run // @generated
// This file is automatically generated by Kanel. Do not modify manually.
import { communitiesId, type CommunitiesId } from './Communities';
import { usersId, type UsersId } from './Users';
import { type ColumnType, type Selectable, type Insertable, type Updateable } from 'kysely';
import { z } from 'zod';
/** Identifier type for public.members */
export type MembersId = string & { __brand: 'MembersId' };
/** Represents the table public.members */
export default interface MembersTable {
id: ColumnType<MembersId, MembersId | undefined, MembersId>;
createdAt: ColumnType<Date, Date | string | undefined, Date | string>;
updatedAt: ColumnType<Date, Date | string | undefined, Date | string>;
canAdmin: ColumnType<boolean, boolean, boolean>;
communityId: ColumnType<CommunitiesId, CommunitiesId, CommunitiesId>;
userId: ColumnType<UsersId, UsersId, UsersId>;
}
export type Members = Selectable<MembersTable>;
export type NewMembers = Insertable<MembersTable>;
export type MembersUpdate = Updateable<MembersTable>;
export const membersId = z.string() as unknown as z.Schema<MembersId>;
export const members = z.object({
id: membersId,
createdAt: z.date(),
updatedAt: z.date(),
canAdmin: z.boolean(),
communityId: communitiesId,
userId: usersId,
}) as unknown as z.Schema<Members>;
export const membersInitializer = z.object({
id: membersId.optional(),
createdAt: z.date().optional(),
updatedAt: z.date().optional(),
canAdmin: z.boolean(),
communityId: communitiesId,
userId: usersId,
}) as unknown as z.Schema<MembersInitializer>;
export const membersMutator = z.object({
id: membersId.optional(),
createdAt: z.date().optional(),
updatedAt: z.date().optional(),
canAdmin: z.boolean().optional(),
communityId: communitiesId.optional(),
userId: usersId.optional(),
}) as unknown as z.Schema<MembersMutator>; As you can see, the schemas created by This can be easily solved by either
I think a configuration option is probably the easiest. For now I will solve this by creating my own hook that renames the types used by the |
Another possible fix, is disabling the cast entirely in module.exports = {
connection: process.env["DATABASE_URL"],
schemas: ["public"],
preDeleteOutputFolder: true,
preRenderHooks: [
makeKyselyHook(),
makeGenerateZodSchemas({
getZodSchemaMetadata: defaultGetZodSchemaMetadata,
getZodIdentifierMetadata: defaultGetZodIdentifierMetadata,
castToSchema: false,
zodTypeMap: defaultZodTypeMap,
}),
],
outputPath: "../packages/db/src",
}; |
For anyone interested, here is a postRender hook that solves this issue the ugly way, by just renaming the types after they're generated. // .kanelrc.js
const { makeKyselyHook } = require("kanel-kysely");
const { generateZodSchemas } = require("kanel-zod");
const kanelZodCastRegex = /as unknown as z.Schema<(.*?)(Mutator|Initializer)>/;
/**
* @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, instantiatedConfig) {
return lines.map((line) => {
if (!line.includes("as unknown as z.Schema")) {
return line;
}
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;
});
}
/** @type {import('kanel').Config} */
module.exports = {
connection: process.env["DATABASE_URL"],
schemas: ["public"],
preDeleteOutputFolder: true,
preRenderHooks: [makeKyselyHook(), generateZodSchemas],
postRenderHooks: [kanelKyselyZodCompatibilityHook],
outputPath: "../packages/db/src",
}; |
I didn't even realize that's what's going on, but yes, to answer your question @kristiandupont, that's exactly it. |
Ah, I see. Yeah, the thing is that even though I did a pretty big rewrite two years ago, the architecture is already a bit dated for what I and you guys are trying to do with it already. Both of these extensions work in a a bit of a hacky way. It's a constant battle between making things flexible and keeping necessary configuration at a minimum. I will try to look into this when I have some time but I am not making any promises as to when that might be :-) |
For any other travelers who want to use @tefkah 's solution but get caught up on zod not transforming properties to camel case, here is a dirty hack to regex the properties and transform them with the same case change routine that it uses to to the table names. const { makeKyselyHook, kyselyCamelCaseHook } = require('kanel-kysely');
const { generateZodSchemas } = require('kanel-zod');
const { recase } = require('@kristiandupont/recase');
const kanelZodCastRegex = / as unknown as z.Schema<(.*?)(Mutator|Initializer)*>/;
const kanelZodIdColumnCastRegex = /as unknown as z.Schema<(.*?)Id>/;
const kanelZodPropertyRegex =/([a-z]+[_][a-z_]+): (z\.*.+|.+),/
/**
* @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, instantiatedConfig) {
return lines.map((line) => {
if (line.match(kanelZodPropertyRegex)) {
const captureGroup = line.match(kanelZodPropertyRegex);
console.log('captureGroup', captureGroup[1]);
try {
console.log(line);
return line.replace(captureGroup[1], recase(null, 'camel', captureGroup[1]));
} catch (error) {
console.log('error', error);
return "/** @warning: Error in kanelKyselyZodCompatibilityHook this is not camelCased!! */\n" + line;
}
}
if (!line.includes('as unknown as z.Schema')) {
return line;
}
if (line.match(kanelZodIdColumnCastRegex)) {
return line;
} else {
const replacedLine = line.replace(
kanelZodCastRegex,
(_, typeName, mutatorOrInitializer) => {
if (!mutatorOrInitializer) {
return ``;
}
if (mutatorOrInitializer === 'Mutator') {
return ``;
}
return ``;
},
);
return replacedLine;
}
});
} |
In
.kanelrc
I'm usingpreRenderHooks: [makeKyselyHook(), generateZodSchemas],
because I want both zod and kysely types. However, when using themakeGenerateZodSchemas
config propertycastToSchema: true
, the Schema types are missing.Here is an example:
In the output,
PrismaMigrationsInitializer
is not defined.If I remove
makeKyselyHook(),
plugin, then the schema types appear as expected:Is there a way to make these plugins work with each other?
The text was updated successfully, but these errors were encountered: