@scalar/api-reference
Version:
Generate beautiful API references from OpenAPI documents
215 lines (214 loc) • 6.97 kB
JavaScript
import { objectKeys } from "@scalar/helpers/object/object-keys";
import { getResolvedRef } from "@scalar/workspace-store/helpers/get-resolved-ref";
import { isArraySchema } from "@scalar/workspace-store/schemas/v3.1/strict/type-guards";
const mergeAllOfSchemas = (schemas, rootSchema) => {
if (!schemas?.allOf?.length || !Array.isArray(schemas.allOf)) {
return rootSchema || {};
}
const result = {};
const { allOf: _, ...baseSchema } = schemas;
for (const _schema of schemas.allOf) {
if (!_schema || typeof _schema !== "object") {
continue;
}
const schema = getResolvedRef(_schema);
if (schema.allOf) {
const nestedMerged = mergeAllOfSchemas(schema);
mergeSchemaIntoResult(result, nestedMerged);
continue;
}
mergeSchemaIntoResult(result, schema);
}
if (Object.keys(baseSchema).length > 0) {
mergeSchemaIntoResult(result, baseSchema, true);
}
if (rootSchema && typeof rootSchema === "object") {
if (rootSchema.allOf) {
const nestedMerged = mergeAllOfSchemas(rootSchema);
mergeSchemaIntoResult(result, nestedMerged, true);
} else {
mergeSchemaIntoResult(result, rootSchema, true);
}
}
return result;
};
const mergeSchemaIntoResult = (result, schema, override = false) => {
const schemaKeys = objectKeys(schema);
if (schemaKeys.length === 0) {
return;
}
for (const key of schemaKeys) {
const value = getResolvedRef(schema[key]);
if (value === void 0) {
continue;
}
if (key === "required") {
if (Array.isArray(value) && value.length > 0) {
if (result.required?.length) {
result.required = [.../* @__PURE__ */ new Set([...result.required, ...value])];
} else {
result.required = value.slice();
}
}
} else if (key === "properties") {
if (value && typeof value === "object") {
if (!result.properties) {
result.properties = {};
}
mergePropertiesIntoResult(result.properties, value);
}
} else if (key === "items") {
const items = getResolvedRef(value);
if (items) {
if (isArraySchema(schema)) {
if (!result.items) {
result.items = {};
}
if (items.allOf) {
const mergedItems = mergeAllOfSchemas(items);
Object.assign(result.items, mergedItems);
} else {
mergeItemsIntoResult(getResolvedRef(result.items), items);
}
} else if (items.allOf) {
const mergedItems = mergeAllOfSchemas(items);
if ("properties" in mergedItems) {
if (!("properties" in result)) {
result.properties = {};
}
"properties" in result && mergePropertiesIntoResult(result.properties, mergedItems.properties);
}
} else if (!("items" in result)) {
result.items = items;
}
}
} else if (key === "enum") {
if (Array.isArray(value) && value.length > 0) {
result.enum = [.../* @__PURE__ */ new Set([...result.enum || [], ...value])];
}
} else if (key === "oneOf" || key === "anyOf") {
if (Array.isArray(value)) {
if (!("properties" in result)) {
result.properties = {};
}
for (const _option of value) {
const option = getResolvedRef(_option);
if (option.properties && "properties" in result) {
mergePropertiesIntoResult(result.properties, option.properties);
}
}
}
} else if (key === "allOf") {
continue;
} else {
if (override || result[key] === void 0) {
result[key] = value;
}
}
}
};
const mergePropertiesIntoResult = (result, properties) => {
const propertyKeys = Object.keys(properties ?? {});
if (!properties || !result || propertyKeys.length === 0) {
return;
}
for (const key of propertyKeys) {
const schema = getResolvedRef(properties[key]);
if (!schema) {
delete result[key];
continue;
}
if (typeof schema !== "object") {
result[key] = schema;
continue;
}
if (!result[key]) {
if (schema.allOf) {
result[key] = mergeAllOfSchemas(schema);
} else if (isArraySchema(schema) && getResolvedRef(schema.items)?.allOf) {
result[key] = {
...schema,
items: mergeAllOfSchemas(getResolvedRef(schema.items))
};
} else if (properties[key]) {
result[key] = properties[key];
}
continue;
}
const existing = getResolvedRef(result[key]);
if (schema.allOf) {
result[key] = mergeAllOfSchemas({ allOf: [existing, ...schema.allOf] });
} else if (isArraySchema(schema) && isArraySchema(existing) && schema.items) {
const existingItems = getResolvedRef(existing.items);
result[key] = {
...existing,
type: "array",
items: existingItems ? mergeItems(existingItems, getResolvedRef(schema.items)) : getResolvedRef(schema.items)
};
} else {
if ("properties" in existing && "properties" in schema) {
const merged = { ...existing, ...schema };
merged.properties = { ...existing.properties };
mergePropertiesIntoResult(merged.properties, schema.properties);
result[key] = merged;
} else {
result[key] = { ...schema, ...existing };
}
}
}
};
const mergeItemsIntoResult = (result, items) => {
if (items.allOf || result.allOf) {
const allOfSchemas = [];
if (result.allOf) {
for (const schema of result.allOf) {
allOfSchemas.push(getResolvedRef(schema));
}
} else {
allOfSchemas.push(result);
}
if (items.allOf) {
for (const schema of items.allOf) {
allOfSchemas.push(getResolvedRef(schema));
}
} else {
allOfSchemas.push(items);
}
const merged = mergeAllOfSchemas({ allOf: allOfSchemas });
Object.assign(result, merged);
return;
}
Object.assign(result, items);
if ("properties" in result && "properties" in items) {
mergePropertiesIntoResult(result.properties, items.properties);
}
};
const mergeItems = (existing, incoming) => {
if (existing.allOf || incoming.allOf) {
const allOfSchemas = [];
if (existing.allOf) {
for (const schema of existing.allOf) {
allOfSchemas.push(getResolvedRef(schema));
}
} else {
allOfSchemas.push(existing);
}
if (incoming.allOf) {
for (const schema of incoming.allOf) {
allOfSchemas.push(getResolvedRef(schema));
}
} else {
allOfSchemas.push(incoming);
}
return mergeAllOfSchemas({ allOf: allOfSchemas });
}
const merged = { ...existing, ...incoming };
if ("properties" in existing && "properties" in incoming) {
merged.properties = { ...existing.properties };
mergePropertiesIntoResult(merged.properties, incoming.properties);
}
return merged;
};
export {
mergeAllOfSchemas
};