diff --git a/packages/rapidoc/src/components/schema-table.ts b/packages/rapidoc/src/components/schema-table.ts
index b6d68dc3..8c9c903f 100644
--- a/packages/rapidoc/src/components/schema-table.ts
+++ b/packages/rapidoc/src/components/schema-table.ts
@@ -409,18 +409,29 @@ export default class SchemaTable extends SchemaBaseElement {
${this.renderDescrCell(node.description)}
- ${node.options?.map(
- (opt, i) => html`
-
-
- OPT ${opt.optionIndex || i + 1}
- ${opt.optionTitle ? html`${opt.optionTitle}` : ""}
-
-
-
-
-
${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}
- `,
+ ${node.options?.map((opt, i) =>
+ opt.kind === "primitive"
+ ? // A primitive option fits on a single row: the badge, then its type and description
+ html`
+ ${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}
+
`
+ : html`
+
+
+ OPT ${opt.optionIndex || i + 1}
+ ${
+ opt.optionTitle
+ ? html`${opt.optionTitle}`
+ : ""
+ }
+
+
+
+
+
+ ${this.renderAST(opt, "xxx-of-option", schemaLevel + 1, indentLevel + 1)}
+
+ `,
)}
`;
@@ -429,11 +440,26 @@ export default class SchemaTable extends SchemaBaseElement {
// 4. Primitive kind
if (node.kind === "primitive") {
const dataTypeCss = this.getDataTypeCss(node.type);
+ // Primitive option of a oneOf / anyOf: the OPT n badge (and the title) takes the place of the name
+ const isOption = parentType === "xxx-of-option" && !!node.optionIndex;
return html`
- ${deprecatedIcon}${node.name ? html`${node.name}` : ""}
+ ${
+ isOption
+ ? html`OPT ${node.optionIndex}${
+ node.optionTitle
+ ? html`${node.optionTitle}`
+ : ""
+ }`
+ : ""
+ }${deprecatedIcon}${
+ node.name && !isOption
+ ? html`${node.name}`
+ : ""
+ }
${node.required ? html`*` : ""}
diff --git a/packages/rapidoc/src/utils/schema-utils.js b/packages/rapidoc/src/utils/schema-utils.js
index 60c6238c..ac852bb5 100644
--- a/packages/rapidoc/src/utils/schema-utils.js
+++ b/packages/rapidoc/src/utils/schema-utils.js
@@ -129,6 +129,23 @@ export function isBinaryFileField(schema) {
);
}
+// Keywords that only annotate a schema, without telling anything about the type of the value
+const ANNOTATION_KEYWORDS = new Set([
+ "title",
+ "description",
+ "example",
+ "examples",
+ "default",
+ "deprecated",
+ "readOnly",
+ "writeOnly",
+ "externalDocs",
+]);
+
+function isAnnotationOnly(schema) {
+ return Object.keys(schema).every((key) => ANNOTATION_KEYWORDS.has(key) || key.startsWith("x-"));
+}
+
/* Generates an schema object containing type and constraint info */
export function getTypeInfo(schema) {
if (!schema) {
@@ -170,7 +187,7 @@ export function getTypeInfo(schema) {
}
});
dataType = subTypes.length > 0 ? subTypes.join("┃") : "{missing-type-info}";
- } else if (Object.keys(schema).length === 0) {
+ } else if (isAnnotationOnly(schema)) {
dataType = "any";
} else {
dataType = "{missing-type-info}";
@@ -1502,6 +1519,10 @@ function composeSchemaAST(schema, level, name, isRequired, suffix) {
optAst.writeOnly = false;
optAst.optionIndex = idx + 1;
optAst.optionTitle = optTitle;
+ if (optAst.kind === "primitive" && !optTitle) {
+ // The OPT n badge already names it, a row labelled "Option n" would only repeat it
+ optAst.name = "";
+ }
}
return optAst;
})
diff --git a/tests/unit/schema-ast.test.js b/tests/unit/schema-ast.test.js
index 7d99b402..b4eab7ad 100644
--- a/tests/unit/schema-ast.test.js
+++ b/tests/unit/schema-ast.test.js
@@ -414,6 +414,27 @@ describe("schemaToAST", () => {
});
});
+ describe("oneOf options", () => {
+ it("should not name the untitled primitive options, but keep the name of the other options", () => {
+ const ast = schemaToAST({
+ oneOf: [
+ { type: "string" },
+ { type: "number", title: "Count" },
+ { type: "object", properties: { id: { type: "string" } } },
+ ],
+ });
+ assert.strictEqual(ast.kind, "union");
+ assert.deepStrictEqual(
+ ast.options.map((o) => [o.optionIndex, o.name]),
+ [
+ [1, ""],
+ [2, "Count"],
+ [3, "Option 3"],
+ ],
+ );
+ });
+ });
+
describe("recursion limit & null inputs", () => {
it("should return null for falsy schema input", () => {
assert.strictEqual(schemaToAST(null), null);
diff --git a/tests/unit/schema-utils.test.js b/tests/unit/schema-utils.test.js
index f62c830a..caa15b82 100644
--- a/tests/unit/schema-utils.test.js
+++ b/tests/unit/schema-utils.test.js
@@ -327,6 +327,19 @@ describe("generateExample", () => {
});
describe("getTypeInfo", () => {
+ it("should return any for a schema that only carries annotations", () => {
+ assert.strictEqual(getTypeInfo({}).type, "any");
+ assert.strictEqual(getTypeInfo({ description: "A string, a number or a boolean" }).type, "any");
+ assert.strictEqual(
+ getTypeInfo({ title: "Value", description: "Anything", example: 1, "x-internal": true }).type,
+ "any",
+ );
+ });
+
+ it("should still report a missing type when the schema has constraints but no type", () => {
+ assert.strictEqual(getTypeInfo({ minLength: 3 }).type, "{missing-type-info}");
+ });
+
it("should format non-string const values as strings in allowedValues", () => {
const numSchema = { type: "number", const: 1 };
const info = getTypeInfo(numSchema);