Skip to content
52 changes: 40 additions & 12 deletions lib/process-schema.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,64 @@
const NESTED_WITH_NAME = ["definitions", "properties"];
/**
* @typedef {import("json-schema").JSONSchema7} JSONSchema
*/

const NESTED_DIRECT = ["items", "additionalProperties", "not"];
/**
* @typedef {Object} SchemaVisitor
* @property {(schema: JSONSchema | boolean, context?: unknown) => JSONSchema | boolean | void} [schema]
* @property {(obj: JSONSchema | boolean, context?: unknown) => JSONSchema | boolean | void} [object]
* @property {(arr: (JSONSchema | boolean)[], context?: unknown) => void} [array]
*/

const NESTED_WITH_NAME = ["definitions", "properties"];
const NESTED_DIRECT = ["items", "additionalProperties", "not"];
const NESTED_ARRAY = ["oneOf", "anyOf", "allOf"];

/**
* Recursively processes a JSON Schema using the visitor pattern.
*
* @param {SchemaVisitor} visitor
* @param {JSONSchema | boolean} json
* @param {unknown} [context]
* @returns {JSONSchema | boolean}
*/
const processSchema = (visitor, json, context) => {
if (!json || typeof json !== "object") return json;

json = { ...json };
if (visitor.schema) json = visitor.schema(json, context);

if (typeof visitor?.schema === "function") {
json = visitor.schema(json, context) || json;
}

for (const name of NESTED_WITH_NAME) {
if (name in json && json[name] && typeof json[name] === "object") {
if (visitor.object) json[name] = visitor.object(json[name], context);
for (const key in json[name]) {
if (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
if (typeof visitor?.object === "function") {
json[name] = visitor.object(json[name], context) || json[name];
}

for (const key of Object.keys(json[name])) {
json[name][key] = processSchema(visitor, json[name][key], context);
}
}
}

for (const name of NESTED_DIRECT) {
if (name in json && json[name] && typeof json[name] === "object") {
if (json[name] && typeof json[name] === "object" && !Array.isArray(json[name])) {
json[name] = processSchema(visitor, json[name], context);
}
}

for (const name of NESTED_ARRAY) {
if (name in json && Array.isArray(json[name])) {
json[name] = json[name].slice();
for (let i = 0; i < json[name].length; i++) {
json[name][i] = processSchema(visitor, json[name][i], context);
if (Array.isArray(json[name])) {
json[name] = json[name].map((item) => processSchema(visitor, item, context));

if (typeof visitor?.array === "function") {
visitor.array(json[name], context);
}
if (visitor.array) visitor.array(json[name], context);
}
}

return json;
};

module.exports = processSchema;