json-schema-library
Version:
Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation
104 lines (91 loc) • 3.12 kB
text/typescript
import { JsonSchema } from "../types";
import { getTypeOf } from "./getTypeOf";
import { isObject } from "./isObject";
export function mergeSchema<T extends JsonSchema>(a: T, b: T, ...omit: string[]): T {
if (b?.type === "error") {
return b;
} else if (a?.type === "error") {
return a;
}
const aType = getTypeOf(a);
const bType = getTypeOf(b);
if (aType !== bType) {
return a;
}
const schema = mergeSchema2(a, b) as T;
for (const s of omit) {
delete schema[s]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
}
return schema;
}
export function mergeSchema2(a: unknown, b: unknown, property?: string): unknown {
if (isObject(a) && isObject(b)) {
const newObject: Record<string, unknown> = {};
[...Object.keys(a), ...Object.keys(b)]
.filter((item, index, array) => array.indexOf(item) === index)
.forEach((key) => (newObject[key] = mergeSchema2(a[key], b[key], key)));
return newObject;
}
const aIsArray = Array.isArray(a);
const bIsArray = Array.isArray(b);
if (aIsArray && bIsArray) {
if (property === "required" || property === "anyOf") {
return a.concat(b).filter((item, index, array) => array.indexOf(item) === index);
}
if (property === "items" || property === "prefixItems") {
const result: unknown[] = [];
for (let i = 0; i < b.length; i += 1) {
if (isObject(a[i]) && isObject(b[i]) && a[i].type === b[i].type) {
result[i] = mergeSchema2(a[i], b[i]);
} else {
result.push(b[i] ?? a[i]);
}
}
return result;
}
const result: unknown[] = [];
const append: unknown[] = [];
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
if (isObject(a[i]) && isObject(b[i])) {
result[i] = mergeSchema2(a[i], b[i]);
} else {
if (a[i] !== undefined && b[i] !== undefined) {
result[i] = a[i];
append.push(b[i]);
} else if (a[i] !== undefined) {
result[i] = a[i];
} else if (b[i] !== undefined) {
append.push(b[i]);
}
}
}
return [...result, ...append].filter((item, index, array) => array.indexOf(item) === index);
}
// mixed data-type for type keyword
if (property === "type" && (aIsArray || bIsArray)) {
// we merge to the specific type
if (aIsArray && a.includes(b)) {
return b;
}
if (bIsArray && b.includes(a)) {
return a;
}
// extend the types if they do not match
if (aIsArray) {
return [...a, b];
}
if (bIsArray) {
return [...b, a];
}
}
if (bIsArray) {
return b;
}
if (aIsArray) {
return a;
}
if (b !== undefined) {
return b;
}
return a;
}