Skip to content

Commit 30bee32

Browse files
committed
initial recursive types work
1 parent 4abb8e1 commit 30bee32

File tree

1 file changed

+44
-12
lines changed

1 file changed

+44
-12
lines changed

src/server/templates/zod.ts

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import prettier from 'prettier'
22
import type {
33
PostgresColumn,
44
PostgresFunction,
5+
PostgresRelationship,
56
PostgresSchema,
67
PostgresTable,
78
PostgresType,
@@ -20,7 +21,9 @@ export const apply = async ({
2021
materializedViews,
2122
columns,
2223
types,
24+
relationships,
2325
}: GeneratorMetadata): Promise<string> => {
26+
// return `/* START GENERATED TYPES */\n` + JSON.stringify(relationships, undefined, 2)
2427
// Index columns by relation id
2528
const columnsByTableId = Object.fromEntries<PostgresColumn[]>(
2629
[...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [t.id, []])
@@ -69,6 +72,11 @@ export const apply = async ({
6972
return `${JSON.stringify(col.name)}: ${z}`
7073
}
7174

75+
const makeRelationshipShapeLine = (relation: PostgresRelationship, ctx: PgTypeCtx) => {
76+
const typeVal = `supabaseZodSchemas.${relation.referenced_relation}.list`
77+
return `get ${relation.referenced_relation}() { return ${typeVal}.optional() }`
78+
}
79+
7280
const makeUpdateShapeLine = (col: PostgresColumn, ctx: PgTypeCtx) => {
7381
// Update: everything optional; identity ALWAYS -> forbid value if provided
7482
if (col.identity_generation === 'ALWAYS') {
@@ -90,14 +98,32 @@ export const supabaseZodSchemas = {
9098
.map((table) => {
9199
const ctx: PgTypeCtx = { types, schemas, tables, views }
92100
const cols = columnsByTableId[table.id] ?? []
101+
const relevantRels = relationships
102+
.filter(
103+
(relationship) =>
104+
relationship.schema === table.schema &&
105+
relationship.referenced_schema === table.schema &&
106+
relationship.relation === table.name
107+
)
108+
.sort(
109+
(a, b) =>
110+
a.foreign_key_name.localeCompare(b.foreign_key_name) ||
111+
a.referenced_relation.localeCompare(b.referenced_relation) ||
112+
JSON.stringify(a.referenced_columns).localeCompare(JSON.stringify(b.referenced_columns))
113+
)
93114
94115
const listShape = cols.map((c) => makeListShapeLine(c, ctx)).join(',\n ')
116+
const relationshipShape = relevantRels
117+
.map((rel) => makeRelationshipShapeLine(rel, ctx))
118+
.join(',\n ')
119+
95120
const insertShape = cols.map((c) => makeInsertShapeLine(c, ctx)).join(',\n ')
96121
const updateShape = cols.map((c) => makeUpdateShapeLine(c, ctx)).join(',\n ')
97122
98123
return `${JSON.stringify(table.name)}: {
99124
list: z.object({
100-
${listShape}
125+
${listShape},
126+
${relationshipShape}
101127
}),
102128
insert: z.object({
103129
${insertShape}
@@ -116,6 +142,10 @@ export const supabaseZodSchemas = {
116142
return out
117143
}
118144

145+
const wrapAsArray = (zodSchema: string): string => {
146+
return `z.array(${zodSchema})`
147+
}
148+
119149
type PgTypeCtx = {
120150
types: PostgresType[]
121151
schemas: PostgresSchema[]
@@ -161,17 +191,19 @@ const pgTypeToZodSchema = (
161191

162192
// Arrays: leading underscore means array of the inner type
163193
if (pgType.startsWith('_')) {
164-
return `z.array(${pgTypeToZodSchema(
165-
schema,
166-
pgType.slice(1),
167-
{
168-
types,
169-
schemas,
170-
tables,
171-
views,
172-
},
173-
mode
174-
)})`
194+
return wrapAsArray(
195+
pgTypeToZodSchema(
196+
schema,
197+
pgType.slice(1),
198+
{
199+
types,
200+
schemas,
201+
tables,
202+
views,
203+
},
204+
mode
205+
)
206+
)
175207
}
176208

177209
// Enums inline

0 commit comments

Comments
 (0)