UNPKG

fumadocs-openapi

Version:

Generate MDX docs for your OpenAPI spec

127 lines (125 loc) 3.57 kB
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