@datazod/zod-sql
Version:
Convert Zod schemas to SQL table definitions with support for SQLite, PostgreSQL, and MySQL
55 lines (54 loc) • 2.35 kB
JavaScript
import { z } from 'zod';
import { mapZodToSql } from '../../maps';
import { isInteger, isNullable, quoteIdentifier } from '../identifier';
/**
* Process a nested object and flatten it according to the specified depth
*/
export function processNestedObject(prefix, objectType, cols, depth, dialect = 'sqlite') {
if (depth <= 0) {
// If max depth reached, store as JSON
const sqlType = mapZodToSql(objectType, dialect);
const nullable = isNullable(objectType) ? '' : ' NOT NULL';
cols.push(`${quoteIdentifier(prefix, dialect)} ${sqlType}${nullable}`);
return;
}
const shape = objectType.shape;
for (const [nestedKey, nestedType] of Object.entries(shape)) {
const colName = `${prefix}_${nestedKey}`;
// Unwrap nullable/optional to get the inner type for type identification
let unwrappedType = nestedType;
if (unwrappedType instanceof z.ZodNullable ||
unwrappedType instanceof z.ZodOptional) {
unwrappedType = unwrappedType.unwrap();
}
if (unwrappedType instanceof z.ZodObject && depth > 0) {
// Changed from depth > 1 to depth > 0
// Recursively process nested objects
processNestedObject(colName, unwrappedType, cols, depth - 1, dialect);
}
else if (unwrappedType instanceof z.ZodNumber) {
// Explicitly handle numbers
const isInt = isInteger(unwrappedType);
let sqlType;
switch (dialect) {
case 'postgres':
sqlType = isInt ? 'INTEGER' : 'DOUBLE PRECISION';
break;
case 'mysql':
sqlType = isInt ? 'INT' : 'DOUBLE';
break;
case 'sqlite':
default:
sqlType = isInt ? 'INTEGER' : 'REAL';
}
const nullable = isNullable(nestedType) ? '' : ' NOT NULL';
cols.push(`${quoteIdentifier(colName, dialect)} ${sqlType}${nullable}`);
}
else {
// Add flattened column
const sqlType = mapZodToSql(unwrappedType, dialect);
const nullable = isNullable(nestedType) ? '' : ' NOT NULL';
cols.push(`${quoteIdentifier(colName, dialect)} ${sqlType}${nullable}`);
}
}
}