From 2a6b5b71455ba193e884019b0a8e5b7acd733af0 Mon Sep 17 00:00:00 2001 From: Steven Serrata Date: Tue, 19 May 2026 10:46:01 -0500 Subject: [PATCH] fix(plugin): render OpenAPI 3.1 type unions in schema names (#950) OpenAPI 3.1 allows `type` to be an array of type names (e.g. `["string", "null"]`). The plugin's `getSchemaName` previously returned the raw value, which downstream rendered as `"string,null"` (or with no separator at all in some paths). The runtime SchemaItem fix in #1017 only covered the React render path; the static MDX output remained broken. Normalize array-form `type` to a `" | "`-joined string and propagate through format, title, and array wrapping. Single-element arrays unwrap to the bare type. Co-Authored-By: Claude Opus 4.7 --- .../src/markdown/schema.test.ts | 26 ++++++++++ .../src/markdown/schema.ts | 47 ++++++++++++++----- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.test.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.test.ts index 6ff14a421..577b4569b 100644 --- a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.test.ts +++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.test.ts @@ -50,4 +50,30 @@ describe("getSchemaName", () => { } as SchemaObject; expect(getSchemaName(schema)).toBe("integer[][][]"); }); + + it("joins OpenAPI 3.1 type arrays with ` | ` (issue #950)", () => { + const schema = { type: ["string", "null"] } as unknown as SchemaObject; + expect(getSchemaName(schema)).toBe("string | null"); + }); + + it("unwraps a single-element type array", () => { + const schema = { type: ["integer"] } as unknown as SchemaObject; + expect(getSchemaName(schema)).toBe("integer"); + }); + + it("renders type union with format", () => { + const schema = { + type: ["string", "null"], + format: "uuid", + } as unknown as SchemaObject; + expect(getSchemaName(schema)).toBe("string | null"); + }); + + it("renders array of items whose type is a union", () => { + const schema: SchemaObject = { + type: "array", + items: { type: ["string", "null"] } as any, + } as SchemaObject; + expect(getSchemaName(schema)).toBe("string | null[]"); + }); }); diff --git a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts index 9ea5e9e4c..1a4b8154a 100644 --- a/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts +++ b/packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts @@ -7,6 +7,24 @@ import { SchemaObject } from "../openapi/types"; +// OpenAPI 3.1 / JSON Schema 2020-12 allows `type` to be an array of type names +// (e.g. `["string", "null"]`). Normalize to a `string | string[]` view and a +// pretty-printed form joined with ` | `. +function normalizeType(type: unknown): { + single?: string; + pretty?: string; + isUnion: boolean; +} { + if (Array.isArray(type)) { + const filtered = type.filter((t): t is string => typeof t === "string"); + if (filtered.length === 0) return { isUnion: false }; + if (filtered.length === 1) return { single: filtered[0], isUnion: false }; + return { pretty: filtered.join(" | "), isUnion: true }; + } + if (typeof type === "string") return { single: type, isUnion: false }; + return { isUnion: false }; +} + function prettyName(schema: SchemaObject, circular?: boolean) { // Handle enum-only schemas (valid in JSON Schema) // When enum is present without explicit type, treat as string @@ -14,9 +32,14 @@ function prettyName(schema: SchemaObject, circular?: boolean) { return "string"; } + const t = normalizeType(schema.type); + if (schema.format) { - if (schema.type) { - return `${schema.type}<${schema.format}>`; + if (t.single) { + return `${t.single}<${schema.format}>`; + } + if (t.isUnion) { + return `${t.pretty}<${schema.format}>`; } return schema.format; } @@ -39,21 +62,23 @@ function prettyName(schema: SchemaObject, circular?: boolean) { return "object"; } - if (schema.type === "object") { - return schema.xml?.name ?? schema.type; - // return schema.type; + if (t.single === "object") { + return schema.xml?.name ?? t.single; + } + + if (t.single === "array") { + return schema.xml?.name ?? t.single; } - if (schema.type === "array") { - return schema.xml?.name ?? schema.type; - // return schema.type; + if (t.isUnion) { + return schema.title ? `${schema.title} (${t.pretty})` : t.pretty; } - if (schema.title && schema.type) { - return `${schema.title} (${schema.type})`; + if (schema.title && t.single) { + return `${schema.title} (${t.single})`; } - return schema.title ?? schema.type; + return schema.title ?? t.single; } export function getSchemaName(