fumadocs-openapi
Version:
Generate MDX docs for your OpenAPI spec
229 lines (227 loc) • 6.57 kB
JavaScript
import { FormatFlags, schemaToString } from "../../utils/schema-to-string.js";
import { mergeAllOf } from "../../utils/merge-schema.js";
import { SchemaUILazy } from "./lazy.js";
import { jsx, jsxs } from "react/jsx-runtime";
//#region src/ui/schema/index.tsx
function Schema({ ctx, ...options }) {
if (ctx.schemaUI?.render) return ctx.schemaUI.render(options, ctx);
return /* @__PURE__ */ jsx(SchemaUILazy, {
...options.client,
generated: generateSchemaUI(options, ctx)
});
}
function generateSchemaUI({ root, readOnly, writeOnly }, ctx) {
const refs = {};
const { showExample = false } = ctx.schemaUI ?? {};
function generateInfoTags(schema) {
const fields = [];
function field(key, value) {
return /* @__PURE__ */ jsxs("div", {
className: "bg-fd-secondary border rounded-lg text-xs p-1.5 shadow-md",
children: [/* @__PURE__ */ jsx("span", {
className: "font-medium me-2",
children: key
}), /* @__PURE__ */ jsx("code", {
className: "text-fd-muted-foreground",
children: value
})]
});
}
if (schema.default !== void 0) fields.push(field("Default", JSON.stringify(schema.default)));
if (schema.pattern) fields.push(field("Match", schema.pattern));
if (schema.format) fields.push(field("Format", schema.format));
if (schema.multipleOf) fields.push(field("Multiple Of", schema.multipleOf));
let range = formatRange("value", schema.minimum, schema.exclusiveMinimum, schema.maximum, schema.exclusiveMaximum);
if (range) fields.push(field("Range", range));
range = formatRange("length", schema.minLength, void 0, schema.maxLength, void 0);
if (range) fields.push(field("Length", range));
range = formatRange("properties", schema.minProperties, void 0, schema.maxProperties, void 0);
if (range) fields.push(field("Properties", range));
range = formatRange("items", schema.minItems, void 0, schema.maxItems, void 0);
if (range) fields.push(field("Items", range));
if (schema.enum) fields.push(field("Value in", schema.enum.map((value) => JSON.stringify(value)).join(" | ")));
if (showExample && schema.examples) for (const example of schema.examples) fields.push(field("Example", JSON.stringify(example, null, 2)));
return fields;
}
let _counter = 0;
const autoIds = /* @__PURE__ */ new WeakMap();
function getSchemaId(schema) {
if (typeof schema === "boolean") return String(schema);
const raw = ctx.schema.getRawRef(schema);
if (raw) return raw;
const prev = autoIds.get(schema);
if (prev) return prev;
const generated = `__${_counter++}`;
autoIds.set(schema, generated);
return generated;
}
function isVisible(schema) {
if (typeof schema === "boolean") return true;
if (schema.writeOnly) return writeOnly ?? false;
if (schema.readOnly) return readOnly ?? false;
return true;
}
function base(schema) {
if (typeof schema === "boolean") {
const name = schema ? "any" : "never";
return {
typeName: name,
aliasName: name
};
}
return {
description: schema.description && ctx.renderMarkdown(schema.description),
infoTags: generateInfoTags(schema),
typeName: schemaToString(schema, ctx.schema),
aliasName: schemaToString(schema, ctx.schema, FormatFlags.UseAlias),
deprecated: schema.deprecated
};
}
function scanRefs(id, schema) {
if (id in refs) return;
if (typeof schema === "boolean") {
refs[id] = {
type: "primitive",
...base(schema)
};
return;
}
if (Array.isArray(schema.type)) {
const out = {
type: "or",
items: [],
...base(schema)
};
refs[id] = out;
for (const type of schema.type) {
const key = `${id}_type:${type}`;
scanRefs(key, {
...schema,
type
});
out.items.push({
name: type,
$type: key
});
}
return;
}
if (schema.oneOf && schema.anyOf) {
const out = {
type: "and",
items: [],
...base(schema)
};
refs[id] = out;
for (const omit of ["anyOf", "oneOf"]) {
const $type = `${id}_omit:${omit}`;
scanRefs($type, {
...schema,
[omit]: void 0
});
out.items.push({
name: refs[$type].aliasName,
$type
});
}
return;
}
const union = schema.oneOf ?? schema.anyOf;
if (union) {
const out = {
type: "or",
items: [],
...base(schema)
};
refs[id] = out;
for (const item of union) {
if (typeof item !== "object" || !isVisible(item)) continue;
const itemId = getSchemaId(item);
const key = `${id}_extends:${itemId}`;
scanRefs(key, {
...schema,
oneOf: void 0,
anyOf: void 0,
...item,
properties: {
...schema.properties,
...item.properties
}
});
out.items.push({
$type: key,
name: refs[itemId]?.aliasName ?? schemaToString(item, ctx.schema, FormatFlags.UseAlias)
});
}
return;
}
if (schema.allOf) {
scanRefs(id, mergeAllOf(schema));
return;
}
if (schema.type === "object") {
const out = {
type: "object",
props: [],
...base(schema)
};
refs[id] = out;
const { properties = {}, patternProperties, additionalProperties } = schema;
const props = Object.entries(properties);
if (patternProperties) props.push(...Object.entries(patternProperties));
for (const [key, prop] of props) {
if (!isVisible(prop)) continue;
const $type = getSchemaId(prop);
scanRefs($type, prop);
out.props.push({
$type,
name: key,
required: schema.required?.includes(key) ?? false
});
}
if (additionalProperties !== void 0 && isVisible(additionalProperties)) {
const $type = getSchemaId(additionalProperties);
scanRefs($type, additionalProperties);
out.props.push({
$type,
name: "[key: string]",
required: false
});
}
return;
}
if (schema.type === "array") {
const items = schema.items ?? true;
const $type = getSchemaId(items);
refs[id] = {
type: "array",
item: { $type },
...base(schema)
};
scanRefs($type, items);
return;
}
refs[id] = {
type: "primitive",
...base(schema)
};
}
const $root = getSchemaId(root);
scanRefs($root, root);
return {
refs,
$root
};
}
function formatRange(value, min, exclusiveMin, max, exclusiveMax) {
const out = [];
if (min !== void 0) out.push(`${min} <=`);
else if (exclusiveMin !== void 0) out.push(`${exclusiveMin} <`);
out.push(value);
if (max !== void 0) out.push(`<= ${max}`);
else if (exclusiveMax !== void 0) out.push(`< ${exclusiveMax}`);
if (out.length > 1) return out.join(" ");
}
//#endregion
export { Schema };
//# sourceMappingURL=index.js.map