-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathconstraint.ts
172 lines (157 loc) · 5.2 KB
/
constraint.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import type { Constraint } from "@conform-to/dom";
import type { GenericSchema, GenericSchemaAsync } from "valibot";
const keys: Array<keyof Constraint> = [
"required",
"minLength",
"maxLength",
"min",
"max",
"step",
"multiple",
"pattern",
];
export function getValibotConstraint<
T extends GenericSchema | GenericSchemaAsync,
>(schema: T): Record<string, Constraint> {
function updateConstraint(
schema: T,
data: Record<string, Constraint>,
name = "",
): void {
if (name !== "" && !data[name]) {
data[name] = { required: true };
}
const constraint = name !== "" ? data[name] : {};
if (schema.type === "object") {
// @ts-expect-error
for (const key in schema.entries) {
updateConstraint(
// @ts-expect-error
schema.entries[key],
data,
name ? `${name}.${key}` : key,
);
}
} else if (schema.type === "intersect") {
// @ts-expect-error
for (const option of schema.options) {
const result: Record<string, Constraint> = {};
updateConstraint(option, result, name);
Object.assign(data, result);
}
} else if (schema.type === "union" || schema.type === "variant") {
Object.assign(
data,
// @ts-expect-error
schema.options
// @ts-expect-error
.map((option) => {
const result: Record<string, Constraint> = {};
updateConstraint(option, result, name);
return result;
})
// @ts-expect-error
.reduce((prev, next) => {
const list = new Set([...Object.keys(prev), ...Object.keys(next)]);
const result: Record<string, Constraint> = {};
for (const name of list) {
const prevConstraint = prev[name];
const nextConstraint = next[name];
if (prevConstraint && nextConstraint) {
const constraint: Constraint = {};
result[name] = constraint;
for (const key of keys) {
if (
typeof prevConstraint[key] !== "undefined" &&
typeof nextConstraint[key] !== "undefined" &&
prevConstraint[key] === nextConstraint[key]
) {
constraint[key] = prevConstraint[key];
}
}
} else {
result[name] = {
...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 (schema.type === "array") {
constraint.multiple = true;
// @ts-expect-error
updateConstraint(schema.item, data, `${name}[]`);
} else if (schema.type === "string") {
// @ts-expect-error
const minLength = schema.pipe?.find(
// @ts-expect-error
(v) => "type" in v && v.type === "min_length",
);
if (minLength && "requirement" in minLength) {
constraint.minLength = minLength.requirement as number;
}
// @ts-expect-error
const maxLength = schema.pipe?.find(
// @ts-expect-error
(v) => "type" in v && v.type === "max_length",
);
if (maxLength && "requirement" in maxLength) {
constraint.maxLength = maxLength.requirement as number;
}
} else if (schema.type === "optional") {
constraint.required = false;
// @ts-expect-error
updateConstraint(schema.wrapped, data, name);
} else if (schema.type === "nullish") {
constraint.required = false;
// @ts-expect-error
updateConstraint(schema.wrapped, data, name);
} else if (schema.type === "number") {
// @ts-expect-error
const minValue = schema.pipe?.find(
// @ts-expect-error
(v) => "type" in v && v.type === "min_value",
);
if (minValue && "requirement" in minValue) {
constraint.min = minValue.requirement as number;
}
// @ts-expect-error
const maxValue = schema.pipe?.find(
// @ts-expect-error
(v) => "type" in v && v.type === "max_value",
);
if (maxValue && "requirement" in maxValue) {
constraint.max = maxValue.requirement as number;
}
} else if (schema.type === "enum") {
// @ts-expect-error
constraint.pattern = Object.entries(schema.enum)
.map(([_, option]) =>
// To escape unsafe characters on regex
typeof option === "string"
? option
.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&")
.replace(/-/g, "\\x2d")
: option,
)
.join("|");
} else if (schema.type === "tuple") {
// @ts-expect-error
for (let i = 0; i < schema.items.length; i++) {
// @ts-expect-error
updateConstraint(schema.items[i], data, `${name}[${i}]`);
}
} else {
// FIXME: If you are interested in this, feel free to create a PR
}
}
const result: Record<string, Constraint> = {};
updateConstraint(schema, result);
return result;
}