UNPKG

fumadocs-openapi

Version:

Generate MDX docs for your OpenAPI spec

229 lines (227 loc) 6.57 kB
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