@conform-to/zod
Version:
Conform helpers for integrating with Zod
174 lines (168 loc) • 6.68 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
var dom = require('@conform-to/dom');
var future = require('@conform-to/dom/future');
var keys = ['required', 'minLength', 'maxLength', 'min', 'max', 'step', 'multiple', 'pattern', 'accept'];
function getZodConstraint(schema) {
var processingPaths = new Map();
var aliases = [];
var result = {};
var cache = {};
function updateConstraint(schema, data) {
var _data$name;
var name = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
// Detect re-entrant calls caused by getter-based recursive schemas
var processingPath = processingPaths.get(schema);
if (typeof processingPath !== 'undefined') {
aliases.push({
from: dom.getPaths(name),
to: dom.getPaths(processingPath)
});
return;
}
processingPaths.set(schema, name);
var constraint = name !== '' ? (_data$name = data[name]) !== null && _data$name !== void 0 ? _data$name : data[name] = {
required: true
} : {};
var def = schema._zod.def;
if (def.type === 'object') {
for (var key in def.shape) {
// @ts-expect-error
updateConstraint(def.shape[key], data, name ? "".concat(name, ".").concat(key) : key);
}
} else if (def.type === 'pipe') {
// FIXME: What to do with .pipe()?
if (def.out._zod.def.type === 'transform') {
updateConstraint(def.in, data, name);
}
updateConstraint(def.out, data, name);
} else if (def.type === 'intersection') {
var leftResult = {};
var rightResult = {};
updateConstraint(def.left, leftResult, name);
updateConstraint(def.right, rightResult, name);
Object.assign(data, leftResult, rightResult);
} else if (def.type === 'union' // || def.type === 'discriminatedUnion'
) {
Object.assign(data, def.options.map(option => {
var result = {};
updateConstraint(option, result, name);
return result;
}).reduce((prev, next) => {
var list = new Set([...Object.keys(prev), ...Object.keys(next)]);
var result = {};
for (var _name of list) {
var prevConstraint = prev[_name];
var nextConstraint = next[_name];
if (prevConstraint && nextConstraint) {
var _constraint = {};
result[_name] = _constraint;
for (var _key of keys) {
if (typeof prevConstraint[_key] !== 'undefined' && typeof nextConstraint[_key] !== 'undefined' && prevConstraint[_key] === nextConstraint[_key]) {
// @ts-expect-error Both are on the same type
_constraint[_key] = prevConstraint[_key];
}
}
} else {
result[_name] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prevConstraint), nextConstraint), {}, {
required: false
});
}
}
return result;
}));
} else if (name === '') {
// All the cases below are not allowed on root
throw new Error('Unsupported schema');
} else if (def.type === 'array') {
constraint.multiple = true;
updateConstraint(def.element, data, "".concat(name, "[]"));
} else if (def.type === 'string') {
var _schema$_zod$bag$patt;
var _schema = schema;
if (_schema._zod.bag.minimum != null) {
constraint.minLength = _schema._zod.bag.minimum;
}
if (_schema._zod.bag.maximum != null) {
constraint.maxLength = _schema._zod.bag.maximum;
}
if ((_schema$_zod$bag$patt = _schema._zod.bag.patterns) !== null && _schema$_zod$bag$patt !== void 0 && _schema$_zod$bag$patt.size) {
var pattern = future.serializeHtmlPattern([..._schema._zod.bag.patterns]);
if (pattern) {
constraint.pattern = pattern;
}
}
} else if (def.type === 'optional') {
constraint.required = false;
updateConstraint(def.innerType, data, name);
} else if (def.type === 'nullable') {
updateConstraint(def.innerType, data, name);
} else if (def.type === 'default') {
constraint.required = false;
updateConstraint(def.innerType, data, name);
} else if (def.type === 'number') {
var _schema2 = schema;
if (_schema2._zod.bag.minimum != null) {
constraint.min = _schema2._zod.bag.minimum;
}
if (_schema2._zod.bag.maximum != null) {
constraint.max = _schema2._zod.bag.maximum;
}
} else if (def.type === 'enum') {
constraint.pattern = Object.keys(def.entries).map(option =>
// To escape unsafe characters on regex
option.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d')).join('|');
} else if (def.type === 'tuple') {
for (var i = 0; i < def.items.length; i++) {
// @ts-expect-error
updateConstraint(def.items[i], data, "".concat(name, "[").concat(i, "]"));
}
} else if (def.type === 'file') {
var _schema3 = schema;
if (_schema3._zod.bag.mime) {
constraint.accept = _schema3._zod.bag.mime.join();
}
} else if (def.type === 'lazy') {
var inner = def.getter();
updateConstraint(inner, data, name);
}
processingPaths.delete(schema);
}
function resolve(nameOrSegments) {
var name = typeof nameOrSegments === 'string' ? nameOrSegments : dom.formatPaths(nameOrSegments);
if (name in result) {
return result[name];
}
var segments = typeof nameOrSegments === 'string' ? dom.getPaths(nameOrSegments) : nameOrSegments;
// Alias collapse first to handle tuple indices
// like branch[0] before normalization would erase them
for (var alias of aliases) {
var tail = dom.getRelativePath(segments, alias.from);
if (tail !== null && tail.length > 0) {
return resolve([...alias.to, ...tail]);
}
}
for (var i = segments.length - 1; i >= 0; i--) {
if (typeof segments[i] === 'number') {
// Normalizing indices by replacing rightmost numeric index with "[]"
segments[i] = '';
return resolve(segments);
}
}
return undefined;
}
updateConstraint(schema, result);
return new Proxy(result, {
get(target, name, receiver) {
if (typeof name !== 'string') {
return Reflect.get(target, name, receiver);
}
if (name in cache) {
return cache[name];
}
return cache[name] = resolve(name);
}
});
}
exports.getZodConstraint = getZodConstraint;