-
-
Notifications
You must be signed in to change notification settings - Fork 82
/
Copy pathschemaShape.ts
64 lines (53 loc) · 1.93 KB
/
schemaShape.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
import { SchemaError } from '$lib/errors.js';
import type { JSONSchema7Definition } from 'json-schema';
import { schemaInfo } from './schemaInfo.js';
import type { JSONSchema } from './index.js';
import { assertSchema } from '$lib/utils.js';
/**
* A tree structure where the existence of a node means that the field is an array or an object.
* Used in error mapping to determine whether to add errors to an _error field
* (as in arrays and objects), or directly on the field itself.
*/
export type SchemaShape = {
[K in string]: SchemaShape;
};
export function schemaShape(schema: JSONSchema, path: string[] = []): SchemaShape {
const output = _schemaShape(schema, path);
if (!output) throw new SchemaError('No shape could be created for schema.', path);
return output;
}
function _schemaShape(schema: JSONSchema7Definition, path: string[]): SchemaShape | undefined {
assertSchema(schema, path);
const info = schemaInfo(schema, false, path);
if (info.array || info.union) {
const arr = info.array || [];
const union = info.union || [];
return arr.concat(union).reduce(
(shape, next) => {
const nextShape = _schemaShape(next, path);
if (nextShape) shape = { ...(shape ?? {}), ...nextShape };
return shape;
},
arr.length ? {} : undefined
);
}
if (info.properties) {
const output: SchemaShape = {};
for (const [key, prop] of Object.entries(info.properties)) {
const shape = _schemaShape(prop, [...path, key]);
if (shape) output[key] = shape;
}
return output;
}
return info.types.includes('array') || info.types.includes('object') ? {} : undefined;
}
export function shapeFromObject(obj: object): SchemaShape {
let output: SchemaShape = {};
const isArray = Array.isArray(obj);
for (const [key, value] of Object.entries(obj)) {
if (!value || typeof value !== 'object') continue;
if (isArray) output = { ...output, ...shapeFromObject(value) };
else output[key] = shapeFromObject(value);
}
return output;
}