docusaurus-theme-openapi-docs 5.1.1 → 5.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -254,6 +254,7 @@ const AnyOneOf = ({ schema, schemaType, schemaPath }) => {
254
254
  }),
255
255
  anyOneSchema.type === "object" &&
256
256
  !anyOneSchema.properties &&
257
+ !anyOneSchema.additionalProperties &&
257
258
  !anyOneSchema.allOf &&
258
259
  !anyOneSchema.oneOf &&
259
260
  !anyOneSchema.anyOf &&
@@ -267,11 +268,23 @@ const AnyOneOf = ({ schema, schemaType, schemaPath }) => {
267
268
  }),
268
269
  (anyOneSchema.type === "object" || !anyOneSchema.type) &&
269
270
  anyOneSchema.properties &&
271
+ !anyOneSchema.oneOf &&
272
+ !anyOneSchema.anyOf &&
273
+ !anyOneSchema.allOf &&
270
274
  react_1.default.createElement(Properties, {
271
275
  schema: anyOneSchema,
272
276
  schemaType: schemaType,
273
277
  schemaPath: childSchemaPath,
274
278
  }),
279
+ (anyOneSchema.type === "object" || !anyOneSchema.type) &&
280
+ anyOneSchema.additionalProperties &&
281
+ !anyOneSchema.oneOf &&
282
+ !anyOneSchema.anyOf &&
283
+ !anyOneSchema.allOf &&
284
+ react_1.default.createElement(AdditionalProperties, {
285
+ schema: anyOneSchema,
286
+ schemaType: schemaType,
287
+ }),
275
288
  anyOneSchema.allOf &&
276
289
  react_1.default.createElement(SchemaNode, {
277
290
  schema: anyOneSchema,
@@ -996,7 +1009,11 @@ const SchemaNode = ({ schema: rawSchema, schemaType, schemaPath }) => {
996
1009
  workingSchema = (0, normalize_1.mergeAllOf)(schema);
997
1010
  }
998
1011
  if (!workingSchema.discriminator && resolvedDiscriminator) {
999
- workingSchema.discriminator = resolvedDiscriminator;
1012
+ // Clone: getDiscriminator returns a nested branch's discriminator by
1013
+ // reference, and DiscriminatorNode mutates `.mapping` when inferring
1014
+ // variants. Without cloning, a branch that declares its own discriminator
1015
+ // gets a mapping pointing back to itself and recurses forever.
1016
+ workingSchema.discriminator = { ...resolvedDiscriminator };
1000
1017
  }
1001
1018
  if (workingSchema.discriminator) {
1002
1019
  const { discriminator } = workingSchema;
@@ -140,6 +140,74 @@ describe("foldSiblingsIntoBranches", () => {
140
140
  expect(result.items).toBeUndefined();
141
141
  expect(result.oneOf).toHaveLength(1);
142
142
  });
143
+ // Regression for #1548 (property duplication in nested anyOf-of-oneOf):
144
+ // when the outer schema is `{ properties, anyOf: [{oneOf:[...]}, ...] }`,
145
+ // fold merges the outer properties into each anyOf branch, giving each
146
+ // branch its own inner `oneOf` alongside merged properties. The AnyOneOf
147
+ // render layer then delegates the branch to SchemaNode, which folds again
148
+ // into each inner variant. If AnyOneOf were to *also* render the branch's
149
+ // properties directly at that outer level, the same fields would appear
150
+ // twice per selected inner variant tab — hence the check in Schema/index.tsx
151
+ // that skips <Properties> when the branch has a nested oneOf/anyOf/allOf.
152
+ it("folds outer properties into each anyOf branch when branches contain nested oneOf", () => {
153
+ const schema = {
154
+ type: "object",
155
+ properties: {
156
+ name: { type: "string" },
157
+ default_value: { type: "string" },
158
+ },
159
+ anyOf: [
160
+ {
161
+ oneOf: [
162
+ {
163
+ title: "aggregate_group",
164
+ properties: {
165
+ aggregate_group: { type: "string" },
166
+ },
167
+ },
168
+ {
169
+ title: "tap",
170
+ properties: {
171
+ tap: { type: "object" },
172
+ },
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ oneOf: [
178
+ {
179
+ title: "folder",
180
+ properties: { folder: { type: "string" } },
181
+ },
182
+ {
183
+ title: "snippet",
184
+ properties: { snippet: { type: "string" } },
185
+ },
186
+ ],
187
+ },
188
+ ],
189
+ };
190
+ const outer = (0, normalize_1.foldSiblingsIntoBranches)(schema);
191
+ expect(outer.properties).toBeUndefined();
192
+ expect(outer.anyOf).toHaveLength(2);
193
+ for (const branch of outer.anyOf) {
194
+ // Each anyOf branch retains its own inner oneOf and picks up the
195
+ // merged outer properties as siblings — this is the shape that
196
+ // AnyOneOf must NOT render <Properties> for directly, because
197
+ // SchemaNode will fold them into the inner variants below.
198
+ expect(branch.oneOf).toBeDefined();
199
+ expect(branch.properties.name).toBeDefined();
200
+ expect(branch.properties.default_value).toBeDefined();
201
+ const inner = (0, normalize_1.foldSiblingsIntoBranches)(branch);
202
+ expect(inner.properties).toBeUndefined();
203
+ expect(inner.oneOf).toBeDefined();
204
+ for (const variant of inner.oneOf) {
205
+ // Merged outer properties appear exactly once — inside each variant.
206
+ expect(variant.properties.name).toBeDefined();
207
+ expect(variant.properties.default_value).toBeDefined();
208
+ }
209
+ }
210
+ });
143
211
  });
144
212
  describe("getDiscriminator", () => {
145
213
  it("returns direct discriminator", () => {
@@ -164,7 +164,7 @@ function parseCodeLinesFromContent(code, params) {
164
164
  .filter((d) => d.block)
165
165
  .map(({ className, block }) => [block.end, className])
166
166
  );
167
- for (let lineNumber = 0; lineNumber < lines.length; ) {
167
+ for (let lineNumber = 0; lineNumber < lines.length;) {
168
168
  const line = lines[lineNumber];
169
169
  const match = line.match(directiveRegex);
170
170
  if (!match) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-theme-openapi-docs",
3
3
  "description": "OpenAPI theme for Docusaurus.",
4
- "version": "5.1.1",
4
+ "version": "5.1.3",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -38,7 +38,7 @@
38
38
  "@types/postman-collection": "^3.5.11",
39
39
  "@types/react-modal": "^3.16.3",
40
40
  "concurrently": "^10.0.3",
41
- "docusaurus-plugin-openapi-docs": "^5.1.1",
41
+ "docusaurus-plugin-openapi-docs": "^5.1.3",
42
42
  "docusaurus-plugin-sass": "^0.2.6",
43
43
  "eslint-plugin-prettier": "^5.5.1"
44
44
  },
@@ -52,7 +52,7 @@
52
52
  "crypto-js": "^4.2.0",
53
53
  "file-saver": "^2.0.5",
54
54
  "lodash": "^4.17.21",
55
- "pako": "^2.1.0",
55
+ "pako": "^3.0.1",
56
56
  "path-browserify": "^1.0.1",
57
57
  "postman-code-generators": "^2.0.0",
58
58
  "postman-collection": "^5.0.2",
@@ -67,7 +67,7 @@
67
67
  "rehype-raw": "^7.0.0",
68
68
  "remark-gfm": "4.0.1",
69
69
  "sass": "^1.89.2",
70
- "sass-loader": "^16.0.5",
70
+ "sass-loader": "^17.0.0",
71
71
  "unist-util-visit": "^5.0.0",
72
72
  "url": "^0.11.4",
73
73
  "xml-formatter": "^3.6.6"
@@ -82,5 +82,5 @@
82
82
  "engines": {
83
83
  "node": ">=14"
84
84
  },
85
- "gitHead": "c2bc86da54f585f71d294c9f35decf133cb5e589"
85
+ "gitHead": "d5af4f22e951712e084df8b7c513a1708e56d372"
86
86
  }
@@ -236,6 +236,7 @@ const AnyOneOf: React.FC<SchemaProps> = ({
236
236
  {/* Handle empty object as a primitive type */}
237
237
  {anyOneSchema.type === "object" &&
238
238
  !anyOneSchema.properties &&
239
+ !anyOneSchema.additionalProperties &&
239
240
  !anyOneSchema.allOf &&
240
241
  !anyOneSchema.oneOf &&
241
242
  !anyOneSchema.anyOf && (
@@ -251,14 +252,33 @@ const AnyOneOf: React.FC<SchemaProps> = ({
251
252
 
252
253
  {/* Handle actual object types with properties or nested schemas */}
253
254
  {/* Note: In OpenAPI, properties implies type: object even if not explicitly set */}
255
+ {/* When the branch also has a nested oneOf/anyOf/allOf, skip
256
+ Properties here and delegate to SchemaNode below —
257
+ foldSiblingsIntoBranches will merge these properties into
258
+ each inner variant, so rendering them inline as well would
259
+ duplicate them per variant tab. See #1548. */}
254
260
  {(anyOneSchema.type === "object" || !anyOneSchema.type) &&
255
- anyOneSchema.properties && (
261
+ anyOneSchema.properties &&
262
+ !anyOneSchema.oneOf &&
263
+ !anyOneSchema.anyOf &&
264
+ !anyOneSchema.allOf && (
256
265
  <Properties
257
266
  schema={anyOneSchema}
258
267
  schemaType={schemaType}
259
268
  schemaPath={childSchemaPath}
260
269
  />
261
270
  )}
271
+ {/* Render a map variant's `additionalProperties` */}
272
+ {(anyOneSchema.type === "object" || !anyOneSchema.type) &&
273
+ anyOneSchema.additionalProperties &&
274
+ !anyOneSchema.oneOf &&
275
+ !anyOneSchema.anyOf &&
276
+ !anyOneSchema.allOf && (
277
+ <AdditionalProperties
278
+ schema={anyOneSchema}
279
+ schemaType={schemaType}
280
+ />
281
+ )}
262
282
  {anyOneSchema.allOf && (
263
283
  <SchemaNode
264
284
  schema={anyOneSchema}
@@ -1100,7 +1120,11 @@ const SchemaNode: React.FC<SchemaProps> = ({
1100
1120
  workingSchema = mergeAllOf(schema) as SchemaObject;
1101
1121
  }
1102
1122
  if (!workingSchema.discriminator && resolvedDiscriminator) {
1103
- workingSchema.discriminator = resolvedDiscriminator;
1123
+ // Clone: getDiscriminator returns a nested branch's discriminator by
1124
+ // reference, and DiscriminatorNode mutates `.mapping` when inferring
1125
+ // variants. Without cloning, a branch that declares its own discriminator
1126
+ // gets a mapping pointing back to itself and recurses forever.
1127
+ workingSchema.discriminator = { ...resolvedDiscriminator };
1104
1128
  }
1105
1129
 
1106
1130
  if (workingSchema.discriminator) {
@@ -1257,8 +1281,7 @@ const SchemaNode: React.FC<SchemaProps> = ({
1257
1281
  export default SchemaNode;
1258
1282
 
1259
1283
  type PrimitiveSchemaType =
1260
- | Exclude<NonNullable<SchemaObject["type"]>, "object" | "array">
1261
- | "null";
1284
+ Exclude<NonNullable<SchemaObject["type"]>, "object" | "array"> | "null";
1262
1285
 
1263
1286
  const PRIMITIVE_TYPES: Record<PrimitiveSchemaType, true> = {
1264
1287
  string: true,
@@ -158,6 +158,78 @@ describe("foldSiblingsIntoBranches", () => {
158
158
  expect(result.items).toBeUndefined();
159
159
  expect(result.oneOf).toHaveLength(1);
160
160
  });
161
+
162
+ // Regression for #1548 (property duplication in nested anyOf-of-oneOf):
163
+ // when the outer schema is `{ properties, anyOf: [{oneOf:[...]}, ...] }`,
164
+ // fold merges the outer properties into each anyOf branch, giving each
165
+ // branch its own inner `oneOf` alongside merged properties. The AnyOneOf
166
+ // render layer then delegates the branch to SchemaNode, which folds again
167
+ // into each inner variant. If AnyOneOf were to *also* render the branch's
168
+ // properties directly at that outer level, the same fields would appear
169
+ // twice per selected inner variant tab — hence the check in Schema/index.tsx
170
+ // that skips <Properties> when the branch has a nested oneOf/anyOf/allOf.
171
+ it("folds outer properties into each anyOf branch when branches contain nested oneOf", () => {
172
+ const schema = {
173
+ type: "object",
174
+ properties: {
175
+ name: { type: "string" },
176
+ default_value: { type: "string" },
177
+ },
178
+ anyOf: [
179
+ {
180
+ oneOf: [
181
+ {
182
+ title: "aggregate_group",
183
+ properties: {
184
+ aggregate_group: { type: "string" },
185
+ },
186
+ },
187
+ {
188
+ title: "tap",
189
+ properties: {
190
+ tap: { type: "object" },
191
+ },
192
+ },
193
+ ],
194
+ },
195
+ {
196
+ oneOf: [
197
+ {
198
+ title: "folder",
199
+ properties: { folder: { type: "string" } },
200
+ },
201
+ {
202
+ title: "snippet",
203
+ properties: { snippet: { type: "string" } },
204
+ },
205
+ ],
206
+ },
207
+ ],
208
+ };
209
+
210
+ const outer = foldSiblingsIntoBranches(schema);
211
+ expect(outer.properties).toBeUndefined();
212
+ expect(outer.anyOf).toHaveLength(2);
213
+
214
+ for (const branch of outer.anyOf) {
215
+ // Each anyOf branch retains its own inner oneOf and picks up the
216
+ // merged outer properties as siblings — this is the shape that
217
+ // AnyOneOf must NOT render <Properties> for directly, because
218
+ // SchemaNode will fold them into the inner variants below.
219
+ expect(branch.oneOf).toBeDefined();
220
+ expect(branch.properties.name).toBeDefined();
221
+ expect(branch.properties.default_value).toBeDefined();
222
+
223
+ const inner = foldSiblingsIntoBranches(branch);
224
+ expect(inner.properties).toBeUndefined();
225
+ expect(inner.oneOf).toBeDefined();
226
+ for (const variant of inner.oneOf) {
227
+ // Merged outer properties appear exactly once — inside each variant.
228
+ expect(variant.properties.name).toBeDefined();
229
+ expect(variant.properties.default_value).toBeDefined();
230
+ }
231
+ }
232
+ });
161
233
  });
162
234
 
163
235
  describe("getDiscriminator", () => {