fumadocs-openapi
Version:
Generate MDX docs for your OpenAPI spec
127 lines (125 loc) • 3.57 kB
JavaScript
import { getDefaultValue } from "./get-default-values.js";
import { mergeAllOf } from "../utils/merge-schema.js";
import { createContext, use, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form";
import { jsx } from "react/jsx-runtime";
import { Ajv2020 } from "ajv/dist/2020";
//#region src/playground/schema.tsx
const SchemaContext = createContext(void 0);
const anyFields = {
type: [
"string",
"number",
"boolean",
"array",
"object"
],
items: true,
additionalProperties: true
};
function SchemaProvider({ references, fieldInfoMap, readOnly, writeOnly, children }) {
const ajv = useMemo(() => new Ajv2020({
strict: false,
validateSchema: false,
validateFormats: false,
schemas: references
}), [references]);
return /* @__PURE__ */ jsx(SchemaContext.Provider, {
value: useMemo(() => ({
references,
fieldInfoMap,
ajv,
readOnly,
writeOnly
}), [
references,
fieldInfoMap,
ajv,
readOnly,
writeOnly
]),
children
});
}
function useSchemaScope() {
return use(SchemaContext);
}
/**
* A hook to store dynamic info of a field, such as selected schema of `oneOf`.
*
* @param fieldName - field name of form.
* @param schema - The JSON Schema to generate initial values.
* @param depth - The depth to avoid duplicated field name with same schema (e.g. nested `oneOf`).
*/
function useFieldInfo(fieldName, schema, depth) {
const { fieldInfoMap, ajv } = use(SchemaContext);
const form = useFormContext();
const keyName = `${fieldName}:${depth}`;
const [info, setInfo] = useState(() => {
const value = form.getValues(fieldName);
const initialInfo = fieldInfoMap.get(keyName);
if (initialInfo) return initialInfo;
const out = { oneOf: -1 };
const union = getUnion(schema);
if (union) {
const [members, field] = union;
out.oneOf = members.findIndex((item) => ajv.validate(item, value));
if (out.oneOf === -1) out.oneOf = 0;
out.unionField = field;
}
if (Array.isArray(schema.type)) {
const types = schema.type;
out.selectedType = types.find((type) => {
schema.type = type;
const match = ajv.validate(schema, value);
schema.type = types;
return match;
}) ?? types.at(0);
}
if (schema.allOf) {
const merged = mergeAllOf(schema);
if (typeof merged !== "boolean") out.intersection = { merged };
}
return out;
});
fieldInfoMap.set(keyName, info);
return {
info,
updateInfo: (value) => {
const updated = {
...info,
...value
};
if (updated.oneOf === info.oneOf && updated.selectedType === info.selectedType) return;
setInfo(updated);
let valueSchema = schema;
if (updated.unionField) valueSchema = schema[updated.unionField][updated.oneOf];
else if (updated.selectedType) valueSchema = {
...schema,
type: updated.selectedType
};
form.setValue(fieldName, getDefaultValue(valueSchema));
}
};
}
/**
* Resolve `$ref` in the schema, **not recursive**.
*/
function useResolvedSchema(schema) {
const { references } = use(SchemaContext);
return useMemo(() => {
if (typeof schema === "boolean") return anyFields;
if (schema.$ref) return fallbackAny(references[schema.$ref]);
return schema;
}, [references, schema]);
}
function fallbackAny(schema) {
return typeof schema === "boolean" ? anyFields : schema;
}
function getUnion(schema) {
if (schema.anyOf) return [schema.anyOf, "anyOf"];
if (schema.oneOf) return [schema.oneOf, "oneOf"];
}
//#endregion
export { SchemaProvider, anyFields, useFieldInfo, useResolvedSchema, useSchemaScope };
//# sourceMappingURL=schema.js.map