sveltekit-superforms
Version:
Making SvelteKit forms a pleasure to use!
103 lines (102 loc) • 3.83 kB
JavaScript
import { schemaInfo } from './schemaInfo.js';
import { merge as deepMerge } from 'ts-deepmerge';
export function constraints(schema) {
return _constraints(schemaInfo(schema, false, []), []);
}
function merge(...constraints) {
const filtered = constraints.filter((c) => !!c);
if (!filtered.length)
return undefined;
if (filtered.length == 1)
return filtered[0];
return deepMerge(...filtered);
}
function _constraints(info, path) {
if (!info)
return undefined;
let output = undefined;
// Union
if (info.union && info.union.length) {
const infos = info.union.map((s) => schemaInfo(s, info.isOptional, path));
const merged = infos.map((i) => _constraints(i, path));
output = merge(output, ...merged);
// Delete required if any part of the union is optional
if (output &&
(info.isNullable || info.isOptional || infos.some((i) => i?.isNullable || i?.isOptional))) {
delete output.required;
}
}
// Arrays
if (info.array) {
output = merge(output, ...info.array.map((i) => _constraints(schemaInfo(i, info.isOptional, path), path)));
}
// Objects
if (info.properties) {
const obj = {};
for (const [key, prop] of Object.entries(info.properties)) {
const propInfo = schemaInfo(prop, !info.required?.includes(key) || prop.default !== undefined, [key]);
const propConstraint = _constraints(propInfo, [...path, key]);
if (typeof propConstraint === 'object' && Object.values(propConstraint).length > 0) {
obj[key] = propConstraint;
}
}
output = merge(output, obj);
}
return output ?? constraint(info);
}
function constraint(info) {
const output = {};
const schema = info.schema;
const type = schema.type;
const format = schema.format;
// Must be before type check
if (type == 'integer' &&
format == 'unix-time' //||
//format == 'date-time' ||
//format == 'date' ||
//format == 'time'
) {
const date = schema;
if (date.minimum !== undefined)
output.min = new Date(date.minimum).toISOString();
if (date.maximum !== undefined)
output.max = new Date(date.maximum).toISOString();
}
else if (type == 'string') {
const str = schema;
const patterns = [
str.pattern,
...(str.allOf ? str.allOf.map((s) => (typeof s == 'boolean' ? undefined : s.pattern)) : [])
].filter((s) => s !== undefined);
if (patterns.length > 0)
output.pattern = patterns[0];
if (str.minLength !== undefined)
output.minlength = str.minLength;
if (str.maxLength !== undefined)
output.maxlength = str.maxLength;
}
else if (type == 'number' || type == 'integer') {
const num = schema;
if (num.minimum !== undefined)
output.min = num.minimum;
else if (num.exclusiveMinimum !== undefined)
output.min = num.exclusiveMinimum + (type == 'integer' ? 1 : Number.MIN_VALUE);
if (num.maximum !== undefined)
output.max = num.maximum;
else if (num.exclusiveMaximum !== undefined)
output.max = num.exclusiveMaximum - (type == 'integer' ? 1 : Number.MIN_VALUE);
if (num.multipleOf !== undefined)
output.step = num.multipleOf;
}
else if (type == 'array') {
const arr = schema;
if (arr.minItems !== undefined)
output.min = arr.minItems;
if (arr.maxItems !== undefined)
output.max = arr.maxItems;
}
if (!info.isNullable && !info.isOptional) {
output.required = true;
}
return Object.keys(output).length > 0 ? output : undefined;
}