feat(json-schema-2020-12): infer type of schema if not defined
Handles schema cycles as well. Refs #8513
This commit is contained in:
committed by
Vladimír Gorej
parent
83ba76c117
commit
e517397fd2
@@ -8,7 +8,6 @@ import classNames from "classnames"
|
|||||||
import * as propTypes from "../../prop-types"
|
import * as propTypes from "../../prop-types"
|
||||||
import {
|
import {
|
||||||
useComponent,
|
useComponent,
|
||||||
useFn,
|
|
||||||
useLevel,
|
useLevel,
|
||||||
useIsEmbedded,
|
useIsEmbedded,
|
||||||
useIsExpandedDeeply,
|
useIsExpandedDeeply,
|
||||||
@@ -22,7 +21,6 @@ const JSONSchema = ({ schema, name }) => {
|
|||||||
const isExpandedDeeply = useIsExpandedDeeply()
|
const isExpandedDeeply = useIsExpandedDeeply()
|
||||||
const [expanded, setExpanded] = useState(isExpandedDeeply)
|
const [expanded, setExpanded] = useState(isExpandedDeeply)
|
||||||
const [expandedDeeply, setExpandedDeeply] = useState(false)
|
const [expandedDeeply, setExpandedDeeply] = useState(false)
|
||||||
const fn = useFn()
|
|
||||||
const [level, nextLevel] = useLevel()
|
const [level, nextLevel] = useLevel()
|
||||||
const isEmbedded = useIsEmbedded()
|
const isEmbedded = useIsEmbedded()
|
||||||
const Accordion = useComponent("Accordion")
|
const Accordion = useComponent("Accordion")
|
||||||
|
|||||||
@@ -16,16 +16,97 @@ export const getTitle = (schema) => {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getType = (schema) => {
|
export const getType = (schema, processedSchemas = new WeakSet()) => {
|
||||||
if (Array.isArray(schema?.type)) {
|
if (schema == null) {
|
||||||
return schema.type.map(String).join(" | ")
|
return "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema?.type != null) {
|
if (typeof schema === "boolean") {
|
||||||
return String(schema.type)
|
return schema ? "any" : "never"
|
||||||
}
|
}
|
||||||
|
|
||||||
return "any"
|
if (typeof schema !== "object") {
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedSchemas.has(schema)) {
|
||||||
|
return "any" // detect a cycle
|
||||||
|
}
|
||||||
|
processedSchemas.add(schema)
|
||||||
|
|
||||||
|
const { type, items } = schema
|
||||||
|
|
||||||
|
const getArrayType = () => {
|
||||||
|
if (!items) {
|
||||||
|
return "array<any>"
|
||||||
|
}
|
||||||
|
const itemsType = getType(items, processedSchemas)
|
||||||
|
return `array<${itemsType}>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const inferType = () => {
|
||||||
|
if (items) {
|
||||||
|
return getArrayType()
|
||||||
|
} else if (schema.properties || schema.additionalProperties) {
|
||||||
|
return "object"
|
||||||
|
} else if (
|
||||||
|
schema.pattern ||
|
||||||
|
schema.format ||
|
||||||
|
schema.minLength ||
|
||||||
|
schema.maxLength
|
||||||
|
) {
|
||||||
|
return "string"
|
||||||
|
} else if (
|
||||||
|
schema.minimum ||
|
||||||
|
schema.maximum ||
|
||||||
|
schema.exclusiveMinimum ||
|
||||||
|
schema.exclusiveMaximum ||
|
||||||
|
schema.multipleOf
|
||||||
|
) {
|
||||||
|
return "number|integer"
|
||||||
|
} else if (schema.const !== undefined) {
|
||||||
|
if (schema.const === null) {
|
||||||
|
return "null"
|
||||||
|
} else if (typeof schema.const === "boolean") {
|
||||||
|
return "boolean"
|
||||||
|
} else if (typeof schema.const === "number") {
|
||||||
|
return Number.isInteger(schema.const) ? "integer" : "number"
|
||||||
|
} else if (typeof schema.const === "string") {
|
||||||
|
return "string"
|
||||||
|
} else if (typeof schema.const === "object") {
|
||||||
|
return "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeString = Array.isArray(type)
|
||||||
|
? type.map((t) => (t === "array" ? getArrayType() : t)).join(" | ")
|
||||||
|
: type && type.includes("array")
|
||||||
|
? getArrayType()
|
||||||
|
: type || inferType()
|
||||||
|
|
||||||
|
const handleCombiningKeywords = (keyword, separator) => {
|
||||||
|
if (Array.isArray(schema[keyword])) {
|
||||||
|
const combinedTypes = schema[keyword].map((subSchema) =>
|
||||||
|
getType(subSchema, processedSchemas)
|
||||||
|
)
|
||||||
|
return `(${combinedTypes.join(separator)})`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const oneOfString = handleCombiningKeywords("oneOf", " | ")
|
||||||
|
const anyOfString = handleCombiningKeywords("anyOf", " | ")
|
||||||
|
const allOfString = handleCombiningKeywords("allOf", " & ")
|
||||||
|
|
||||||
|
const combinedStrings = [typeString, oneOfString, anyOfString, allOfString]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" | ")
|
||||||
|
|
||||||
|
processedSchemas.delete(schema)
|
||||||
|
|
||||||
|
return combinedStrings || "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isBooleanJSONSchema = (schema) => typeof schema === "boolean"
|
export const isBooleanJSONSchema = (schema) => typeof schema === "boolean"
|
||||||
|
|||||||
Reference in New Issue
Block a user