astro-pocketbase-loader
Version:
A content loader for Astro that uses the PocketBase API
103 lines (92 loc) • 3.11 kB
text/typescript
import { z } from "astro/zod";
import type {
PocketBaseCollection,
PocketBaseSchemaEntry
} from "../types/pocketbase-schema.type";
export function parseSchema(
collection: PocketBaseCollection,
customSchemas: Record<string, z.ZodType> | undefined
): Record<string, z.ZodType> {
// Prepare the schemas fields
const fields: Record<string, z.ZodType> = {};
// Parse every field in the schema
for (const field of collection.schema) {
let fieldType;
// Determine the field type and create the corresponding Zod type
switch (field.type) {
case "number":
// Coerce the value to a number
fieldType = z.coerce.number();
break;
case "bool":
// Coerce the value to a boolean
fieldType = z.coerce.boolean();
break;
case "date":
// Coerce and parse the value as a date
fieldType = z.coerce.date();
break;
case "select":
if (!field.options.values) {
throw new Error(
`Field ${field.name} is of type "select" but has no values defined.`
);
}
// Create an enum for the select values
// @ts-expect-error - Zod complains because the values are not known at compile time and thus the array is not static.
const values = z.enum(field.options.values);
// Parse the field type based on the number of values it can have
fieldType = parseSingleOrMultipleValues(field, values);
break;
case "relation":
case "file":
// NOTE: Relations are currently not supported and are treated as strings
// NOTE: Files are later transformed to URLs
// Parse the field type based on the number of values it can have
fieldType = parseSingleOrMultipleValues(field, z.string());
break;
case "json":
if (customSchemas && customSchemas[field.name]) {
// Use the user defined custom schema for the field
fieldType = customSchemas[field.name];
} else {
// Parse the field as unknown JSON
fieldType = z.unknown();
}
break;
default:
// Default to a string
fieldType = z.string();
break;
}
// If the field is not required, mark it as optional
if (!field.required) {
fieldType = z.preprocess(
(val) => val || undefined,
z.optional(fieldType)
);
}
// Add the field to the fields object
fields[field.name] = fieldType;
}
return fields;
}
/**
* Parse the field type based on the number of values it can have
*
* @param field Field to parse
* @param type Type of each value
*
* @returns The parsed field type
*/
function parseSingleOrMultipleValues(
field: PocketBaseSchemaEntry,
type: z.ZodType
) {
// If the select allows multiple values, create an array of the enum
if (field.options.maxSelect === undefined || field.options.maxSelect === 1) {
return type;
} else {
return z.array(type);
}
}