docusaurus-plugin-openapi-docs 2.2.1 → 2.2.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.
package/README.md CHANGED
@@ -50,7 +50,7 @@ Key Features:
50
50
  Run the following to bootstrap a Docsaurus v3 site (classic theme) with `docusaurus-openapi-docs`:
51
51
 
52
52
  ```bash
53
- npx create-docusaurus@3.4.0 my-website --package-manager yarn
53
+ npx create-docusaurus@2.4.3 my-website --package-manager yarn
54
54
  ```
55
55
 
56
56
  > When prompted to select a template choose `Git repository`.
package/lib/index.js CHANGED
@@ -375,6 +375,15 @@ custom_edit_url: null
375
375
  downloadUrl: metadata.downloadUrl,
376
376
  });
377
377
  }
378
+ if (!fs_1.default.existsSync(outputDir)) {
379
+ try {
380
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
381
+ console.log(chalk_1.default.green(`Successfully created "${outputDir}"`));
382
+ }
383
+ catch (err) {
384
+ console.error(chalk_1.default.red(`Failed to create "${outputDir}"`), chalk_1.default.yellow(err));
385
+ }
386
+ }
378
387
  const versionsJson = JSON.stringify(versionsArray, null, 2);
379
388
  try {
380
389
  fs_1.default.writeFileSync(`${outputDir}/versions.json`, versionsJson, "utf8");
@@ -67,6 +67,12 @@ function createAnyOneOf(schema) {
67
67
  ? anyOneSchema.title
68
68
  : `MOD${index + 1}`;
69
69
  const anyOneChildren = [];
70
+ if (anyOneSchema.type === "object" &&
71
+ !anyOneSchema.properties &&
72
+ !anyOneSchema.allOf &&
73
+ !anyOneSchema.items) {
74
+ anyOneChildren.push(createNodes(anyOneSchema, SCHEMA_TYPE));
75
+ }
70
76
  if (anyOneSchema.properties !== undefined) {
71
77
  anyOneChildren.push(createProperties(anyOneSchema));
72
78
  delete anyOneSchema.properties;
@@ -434,8 +440,9 @@ function createPropertyDiscriminator(name, schemaName, schema, discriminator, re
434
440
  if (schema === undefined) {
435
441
  return undefined;
436
442
  }
443
+ // render as a simple property if there's no mapping
437
444
  if (discriminator.mapping === undefined) {
438
- return undefined;
445
+ return createEdges({ name, schema, required });
439
446
  }
440
447
  return (0, utils_1.create)("div", {
441
448
  className: "openapi-discriminator__item openapi-schema__list-item",
@@ -510,6 +517,19 @@ function createEdges({ name, schema, required, discriminator, }) {
510
517
  if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
511
518
  return createAnyOneOfProperty(name, schemaName, schema, required, schema.nullable);
512
519
  }
520
+ if (schema.properties !== undefined) {
521
+ return createDetailsNode(name, schemaName, schema, required, schema.nullable);
522
+ }
523
+ if (schema.additionalProperties !== undefined) {
524
+ return createDetailsNode(name, schemaName, schema, required, schema.nullable);
525
+ }
526
+ // array of objects
527
+ if (((_a = schema.items) === null || _a === void 0 ? void 0 : _a.properties) !== undefined) {
528
+ return createDetailsNode(name, schemaName, schema, required, schema.nullable);
529
+ }
530
+ if (((_b = schema.items) === null || _b === void 0 ? void 0 : _b.anyOf) !== undefined || ((_c = schema.items) === null || _c === void 0 ? void 0 : _c.oneOf) !== undefined) {
531
+ return createDetailsNode(name, schemaName, schema, required, schema.nullable);
532
+ }
513
533
  if (schema.allOf !== undefined) {
514
534
  const { mergedSchemas } = mergeAllOf(schema.allOf);
515
535
  if (SCHEMA_TYPE === "request") {
@@ -534,7 +554,7 @@ function createEdges({ name, schema, required, discriminator, }) {
534
554
  return createDetailsNode(name, mergedSchemaName, mergedSchemas, required, schema.nullable);
535
555
  }
536
556
  // array of objects
537
- if (((_a = mergedSchemas.items) === null || _a === void 0 ? void 0 : _a.properties) !== undefined) {
557
+ if (((_d = mergedSchemas.items) === null || _d === void 0 ? void 0 : _d.properties) !== undefined) {
538
558
  return createDetailsNode(name, mergedSchemaName, mergedSchemas, required, schema.nullable);
539
559
  }
540
560
  return (0, utils_1.create)("SchemaItem", {
@@ -546,19 +566,6 @@ function createEdges({ name, schema, required, discriminator, }) {
546
566
  schema: mergedSchemas,
547
567
  });
548
568
  }
549
- if (schema.properties !== undefined) {
550
- return createDetailsNode(name, schemaName, schema, required, schema.nullable);
551
- }
552
- if (schema.additionalProperties !== undefined) {
553
- return createDetailsNode(name, schemaName, schema, required, schema.nullable);
554
- }
555
- // array of objects
556
- if (((_b = schema.items) === null || _b === void 0 ? void 0 : _b.properties) !== undefined) {
557
- return createDetailsNode(name, schemaName, schema, required, schema.nullable);
558
- }
559
- if (((_c = schema.items) === null || _c === void 0 ? void 0 : _c.anyOf) !== undefined || ((_d = schema.items) === null || _d === void 0 ? void 0 : _d.oneOf) !== undefined) {
560
- return createDetailsNode(name, schemaName, schema, required, schema.nullable);
561
- }
562
569
  // primitives and array of non-objects
563
570
  return (0, utils_1.create)("SchemaItem", {
564
571
  collapsible: false,
@@ -591,13 +598,6 @@ function createNodes(schema, schemaType) {
591
598
  if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
592
599
  nodes.push(createAnyOneOf(schema));
593
600
  }
594
- if (schema.allOf !== undefined) {
595
- const { mergedSchemas } = mergeAllOf(schema.allOf);
596
- // allOf seems to always result in properties
597
- if (mergedSchemas.properties !== undefined) {
598
- nodes.push(createProperties(mergedSchemas));
599
- }
600
- }
601
601
  if (schema.properties !== undefined) {
602
602
  nodes.push(createProperties(schema));
603
603
  }
@@ -608,6 +608,13 @@ function createNodes(schema, schemaType) {
608
608
  if (schema.items !== undefined) {
609
609
  nodes.push(createItems(schema));
610
610
  }
611
+ if (schema.allOf !== undefined) {
612
+ const { mergedSchemas } = mergeAllOf(schema.allOf);
613
+ // allOf seems to always result in properties
614
+ if (mergedSchemas.properties !== undefined) {
615
+ nodes.push(createProperties(mergedSchemas));
616
+ }
617
+ }
611
618
  if (nodes.length && nodes.length > 0) {
612
619
  return nodes.filter(Boolean).flat();
613
620
  }
@@ -32,43 +32,261 @@ Object.defineProperty(exports, "__esModule", { value: true });
32
32
  const prettier = __importStar(require("prettier"));
33
33
  const createSchema_1 = require("./createSchema");
34
34
  describe("createNodes", () => {
35
- it("should create readable MODs for oneOf primitive properties", () => {
36
- const schema = {
37
- "x-tags": ["clown"],
38
- type: "object",
39
- properties: {
40
- oneOfProperty: {
41
- oneOf: [
42
- {
43
- type: "object",
44
- properties: {
45
- noseLength: {
46
- type: "number",
35
+ describe("oneOf", () => {
36
+ it("should create readable MODs for oneOf primitive properties", async () => {
37
+ const schema = {
38
+ "x-tags": ["clown"],
39
+ type: "object",
40
+ properties: {
41
+ oneOfProperty: {
42
+ oneOf: [
43
+ {
44
+ type: "object",
45
+ properties: {
46
+ noseLength: {
47
+ type: "number",
48
+ },
47
49
  },
50
+ required: ["noseLength"],
51
+ description: "Clown's nose length",
52
+ },
53
+ {
54
+ type: "array",
55
+ items: {
56
+ type: "string",
57
+ },
58
+ description: "Array of strings",
59
+ },
60
+ {
61
+ type: "boolean",
62
+ },
63
+ {
64
+ type: "number",
65
+ },
66
+ {
67
+ type: "string",
68
+ },
69
+ ],
70
+ },
71
+ },
72
+ };
73
+ expect(await Promise.all((0, createSchema_1.createNodes)(schema, "request").map(async (md) => await prettier.format(md, { parser: "babel" })))).toMatchSnapshot();
74
+ });
75
+ });
76
+ describe("allOf", () => {
77
+ it("should render same-level properties with allOf", async () => {
78
+ const schema = {
79
+ allOf: [
80
+ {
81
+ type: "object",
82
+ properties: {
83
+ allOfProp1: {
84
+ type: "string",
85
+ },
86
+ allOfProp2: {
87
+ type: "string",
48
88
  },
49
- required: ["noseLength"],
50
- description: "Clown's nose length",
51
89
  },
52
- {
53
- type: "array",
54
- items: {
90
+ },
91
+ ],
92
+ properties: {
93
+ parentProp1: {
94
+ type: "string",
95
+ },
96
+ parentProp2: {
97
+ type: "string",
98
+ },
99
+ },
100
+ };
101
+ expect(await Promise.all((0, createSchema_1.createNodes)(schema, "response").map(async (md) => await prettier.format(md, { parser: "babel" })))).toMatchSnapshot();
102
+ });
103
+ it("should correctly merge nested properties from multiple allOf schemas", async () => {
104
+ const schema = {
105
+ allOf: [
106
+ {
107
+ type: "object",
108
+ properties: {
109
+ outerProp1: {
110
+ type: "object",
111
+ properties: {
112
+ innerProp1: {
113
+ type: "string",
114
+ },
115
+ },
116
+ },
117
+ },
118
+ },
119
+ {
120
+ type: "object",
121
+ properties: {
122
+ outerProp2: {
123
+ type: "object",
124
+ properties: {
125
+ innerProp2: {
126
+ type: "number",
127
+ },
128
+ },
129
+ },
130
+ },
131
+ },
132
+ ],
133
+ };
134
+ expect(await Promise.all((0, createSchema_1.createNodes)(schema, "response").map(async (md) => await prettier.format(md, { parser: "babel" })))).toMatchSnapshot();
135
+ });
136
+ it("should correctly handle shared required properties across allOf schemas", async () => {
137
+ const schema = {
138
+ allOf: [
139
+ {
140
+ type: "object",
141
+ properties: {
142
+ sharedProp: {
55
143
  type: "string",
56
144
  },
57
- description: "Array of strings",
58
145
  },
59
- {
60
- type: "boolean",
146
+ required: ["sharedProp"],
147
+ },
148
+ {
149
+ type: "object",
150
+ properties: {
151
+ anotherProp: {
152
+ type: "number",
153
+ },
61
154
  },
62
- {
63
- type: "number",
155
+ required: ["anotherProp"],
156
+ },
157
+ ],
158
+ };
159
+ expect(await Promise.all((0, createSchema_1.createNodes)(schema, "response").map(async (md) => await prettier.format(md, { parser: "babel" })))).toMatchSnapshot();
160
+ });
161
+ // Could not resolve values for path:"properties.conflictingProp.type". They are probably incompatible. Values:
162
+ // "string"
163
+ // "number"
164
+ // eslint-disable-next-line jest/no-commented-out-tests
165
+ // it("should handle conflicting properties in allOf schemas", async () => {
166
+ // const schema: SchemaObject = {
167
+ // allOf: [
168
+ // {
169
+ // type: "object",
170
+ // properties: {
171
+ // conflictingProp: {
172
+ // type: "string",
173
+ // },
174
+ // },
175
+ // },
176
+ // {
177
+ // type: "object",
178
+ // properties: {
179
+ // conflictingProp: {
180
+ // type: "number",
181
+ // },
182
+ // },
183
+ // },
184
+ // ],
185
+ // };
186
+ // expect(
187
+ // await Promise.all(
188
+ // createNodes(schema, "response").map(
189
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
190
+ // )
191
+ // )
192
+ // ).toMatchSnapshot();
193
+ // });
194
+ // Could not resolve values for path:"type". They are probably incompatible. Values:
195
+ // "object"
196
+ // "array"
197
+ // eslint-disable-next-line jest/no-commented-out-tests
198
+ // it("should handle mixed data types in allOf schemas", async () => {
199
+ // const schema: SchemaObject = {
200
+ // allOf: [
201
+ // {
202
+ // type: "object",
203
+ // properties: {
204
+ // mixedTypeProp1: {
205
+ // type: "string",
206
+ // },
207
+ // },
208
+ // },
209
+ // {
210
+ // type: "array",
211
+ // items: {
212
+ // type: "number",
213
+ // },
214
+ // },
215
+ // ],
216
+ // };
217
+ // expect(
218
+ // await Promise.all(
219
+ // createNodes(schema, "response").map(
220
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
221
+ // )
222
+ // )
223
+ // ).toMatchSnapshot();
224
+ // });
225
+ it("should correctly deep merge properties in allOf schemas", async () => {
226
+ const schema = {
227
+ allOf: [
228
+ {
229
+ type: "object",
230
+ properties: {
231
+ deepProp: {
232
+ type: "object",
233
+ properties: {
234
+ innerProp1: {
235
+ type: "string",
236
+ },
237
+ },
238
+ },
64
239
  },
65
- {
66
- type: "string",
240
+ },
241
+ {
242
+ type: "object",
243
+ properties: {
244
+ deepProp: {
245
+ type: "object",
246
+ properties: {
247
+ innerProp2: {
248
+ type: "number",
249
+ },
250
+ },
251
+ },
67
252
  },
68
- ],
69
- },
70
- },
71
- };
72
- expect((0, createSchema_1.createNodes)(schema, "request").map((md) => prettier.format(md, { parser: "babel" }))).toMatchSnapshot();
253
+ },
254
+ ],
255
+ };
256
+ expect(await Promise.all((0, createSchema_1.createNodes)(schema, "response").map(async (md) => await prettier.format(md, { parser: "babel" })))).toMatchSnapshot();
257
+ });
258
+ // eslint-disable-next-line jest/no-commented-out-tests
259
+ // it("should handle discriminator with allOf schemas", async () => {
260
+ // const schema: SchemaObject = {
261
+ // allOf: [
262
+ // {
263
+ // type: "object",
264
+ // discriminator: {
265
+ // propertyName: "type",
266
+ // },
267
+ // properties: {
268
+ // type: {
269
+ // type: "string",
270
+ // },
271
+ // },
272
+ // },
273
+ // {
274
+ // type: "object",
275
+ // properties: {
276
+ // specificProp: {
277
+ // type: "string",
278
+ // },
279
+ // },
280
+ // },
281
+ // ],
282
+ // };
283
+ // expect(
284
+ // await Promise.all(
285
+ // createNodes(schema, "response").map(
286
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
287
+ // )
288
+ // )
289
+ // ).toMatchSnapshot();
290
+ // });
73
291
  });
74
292
  });
@@ -118,8 +118,9 @@ function createResponseExamples(responseExamples, mimeType) {
118
118
  value: `${exampleName}`,
119
119
  children: [
120
120
  (0, utils_2.guard)(exampleValue.summary, (summary) => [
121
- (0, utils_1.create)("Markdown", {
122
- children: ` ${summary}`,
121
+ (0, utils_1.create)("div", {
122
+ children: `${summary}`,
123
+ className: "openapi-example__summary",
123
124
  }),
124
125
  ]),
125
126
  (0, utils_1.create)("ResponseSamples", {
@@ -134,8 +135,9 @@ function createResponseExamples(responseExamples, mimeType) {
134
135
  value: `${exampleName}`,
135
136
  children: [
136
137
  (0, utils_2.guard)(exampleValue.summary, (summary) => [
137
- (0, utils_1.create)("Markdown", {
138
- children: ` ${summary}`,
138
+ (0, utils_1.create)("div", {
139
+ children: `${summary}`,
140
+ className: "openapi-example__summary",
139
141
  }),
140
142
  ]),
141
143
  (0, utils_1.create)("ResponseSamples", {
@@ -161,8 +163,9 @@ function createResponseExample(responseExample, mimeType) {
161
163
  value: `Example`,
162
164
  children: [
163
165
  (0, utils_2.guard)(responseExample.summary, (summary) => [
164
- (0, utils_1.create)("Markdown", {
165
- children: ` ${summary}`,
166
+ (0, utils_1.create)("div", {
167
+ children: `${summary}`,
168
+ className: "openapi-example__summary",
166
169
  }),
167
170
  ]),
168
171
  (0, utils_1.create)("ResponseSamples", {
@@ -177,8 +180,9 @@ function createResponseExample(responseExample, mimeType) {
177
180
  value: `Example`,
178
181
  children: [
179
182
  (0, utils_2.guard)(responseExample.summary, (summary) => [
180
- (0, utils_1.create)("Markdown", {
181
- children: ` ${summary}`,
183
+ (0, utils_1.create)("div", {
184
+ children: `${summary}`,
185
+ className: "openapi-example__summary",
182
186
  }),
183
187
  ]),
184
188
  (0, utils_1.create)("ResponseSamples", {
@@ -38,7 +38,7 @@ function createApiPageMD({ title, api: { deprecated, "x-deprecated-description":
38
38
  `import ResponseSamples from "@theme/ResponseSamples";\n`,
39
39
  `import SchemaItem from "@theme/SchemaItem";\n`,
40
40
  `import SchemaTabs from "@theme/SchemaTabs";\n`,
41
- `import Markdown from "@theme/Markdown";\n`,
41
+ `import Heading from "@theme/Heading";\n`,
42
42
  `import OperationTabs from "@theme/OperationTabs";\n`,
43
43
  `import TabItem from "@theme/TabItem";\n\n`,
44
44
  (0, createHeading_1.createHeading)(title.replace(utils_1.lessThan, "<").replace(utils_1.greaterThan, ">")),
@@ -17,8 +17,8 @@ const primitives = {
17
17
  string: {
18
18
  default: () => "string",
19
19
  email: () => "user@example.com",
20
- date: () => new Date().toISOString().substring(0, 10),
21
- "date-time": () => new Date().toISOString(),
20
+ date: () => "2024-07-29",
21
+ "date-time": () => "2024-07-29T15:51:28.071Z",
22
22
  uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6",
23
23
  hostname: () => "example.com",
24
24
  ipv4: () => "198.51.100.42",
@@ -17,8 +17,8 @@ const primitives = {
17
17
  string: {
18
18
  default: () => "string",
19
19
  email: () => "user@example.com",
20
- date: () => new Date().toISOString().substring(0, 10),
21
- "date-time": () => new Date().toISOString(),
20
+ date: () => "2024-07-29",
21
+ "date-time": () => "2024-07-29T15:51:28.071Z",
22
22
  uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6",
23
23
  hostname: () => "example.com",
24
24
  ipv4: () => "198.51.100.42",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-plugin-openapi-docs",
3
3
  "description": "OpenAPI plugin for Docusaurus.",
4
- "version": "2.2.1",
4
+ "version": "2.2.3",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -60,5 +60,5 @@
60
60
  "engines": {
61
61
  "node": ">=14"
62
62
  },
63
- "gitHead": "73b212d18343aa7a35ede5136f5353f06b89d25f"
63
+ "gitHead": "59298da210f1720615ac258f7b6b386a3e01ecbd"
64
64
  }
package/src/index.ts CHANGED
@@ -536,6 +536,18 @@ custom_edit_url: null
536
536
  });
537
537
  }
538
538
 
539
+ if (!fs.existsSync(outputDir)) {
540
+ try {
541
+ fs.mkdirSync(outputDir, { recursive: true });
542
+ console.log(chalk.green(`Successfully created "${outputDir}"`));
543
+ } catch (err) {
544
+ console.error(
545
+ chalk.red(`Failed to create "${outputDir}"`),
546
+ chalk.yellow(err)
547
+ );
548
+ }
549
+ }
550
+
539
551
  const versionsJson = JSON.stringify(versionsArray, null, 2);
540
552
  try {
541
553
  fs.writeFileSync(`${outputDir}/versions.json`, versionsJson, "utf8");
@@ -1,6 +1,151 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`createNodes should create readable MODs for oneOf primitive properties 1`] = `
3
+ exports[`createNodes allOf should correctly deep merge properties in allOf schemas 1`] = `
4
+ Array [
5
+ "<SchemaItem collapsible={true} className={\\"schemaItem\\"}>
6
+ <details style={{}} className={\\"openapi-markdown__details\\"}>
7
+ <summary style={{}}>
8
+ <span className={\\"openapi-schema__container\\"}>
9
+ <strong className={\\"openapi-schema__property\\"}>deepProp</strong>
10
+ <span className={\\"openapi-schema__name\\"}> object</span>
11
+ </span>
12
+ </summary>
13
+ <div style={{ marginLeft: \\"1rem\\" }}>
14
+ <SchemaItem
15
+ collapsible={false}
16
+ name={\\"innerProp1\\"}
17
+ required={false}
18
+ schemaName={\\"string\\"}
19
+ qualifierMessage={undefined}
20
+ schema={{ type: \\"string\\" }}
21
+ ></SchemaItem>
22
+ <SchemaItem
23
+ collapsible={false}
24
+ name={\\"innerProp2\\"}
25
+ required={false}
26
+ schemaName={\\"number\\"}
27
+ qualifierMessage={undefined}
28
+ schema={{ type: \\"number\\" }}
29
+ ></SchemaItem>
30
+ </div>
31
+ </details>
32
+ </SchemaItem>;
33
+ ",
34
+ ]
35
+ `;
36
+
37
+ exports[`createNodes allOf should correctly handle shared required properties across allOf schemas 1`] = `
38
+ Array [
39
+ "<SchemaItem
40
+ collapsible={false}
41
+ name={\\"sharedProp\\"}
42
+ required={true}
43
+ schemaName={\\"string\\"}
44
+ qualifierMessage={undefined}
45
+ schema={{ type: \\"string\\" }}
46
+ ></SchemaItem>;
47
+ ",
48
+ "<SchemaItem
49
+ collapsible={false}
50
+ name={\\"anotherProp\\"}
51
+ required={true}
52
+ schemaName={\\"number\\"}
53
+ qualifierMessage={undefined}
54
+ schema={{ type: \\"number\\" }}
55
+ ></SchemaItem>;
56
+ ",
57
+ ]
58
+ `;
59
+
60
+ exports[`createNodes allOf should correctly merge nested properties from multiple allOf schemas 1`] = `
61
+ Array [
62
+ "<SchemaItem collapsible={true} className={\\"schemaItem\\"}>
63
+ <details style={{}} className={\\"openapi-markdown__details\\"}>
64
+ <summary style={{}}>
65
+ <span className={\\"openapi-schema__container\\"}>
66
+ <strong className={\\"openapi-schema__property\\"}>outerProp1</strong>
67
+ <span className={\\"openapi-schema__name\\"}> object</span>
68
+ </span>
69
+ </summary>
70
+ <div style={{ marginLeft: \\"1rem\\" }}>
71
+ <SchemaItem
72
+ collapsible={false}
73
+ name={\\"innerProp1\\"}
74
+ required={false}
75
+ schemaName={\\"string\\"}
76
+ qualifierMessage={undefined}
77
+ schema={{ type: \\"string\\" }}
78
+ ></SchemaItem>
79
+ </div>
80
+ </details>
81
+ </SchemaItem>;
82
+ ",
83
+ "<SchemaItem collapsible={true} className={\\"schemaItem\\"}>
84
+ <details style={{}} className={\\"openapi-markdown__details\\"}>
85
+ <summary style={{}}>
86
+ <span className={\\"openapi-schema__container\\"}>
87
+ <strong className={\\"openapi-schema__property\\"}>outerProp2</strong>
88
+ <span className={\\"openapi-schema__name\\"}> object</span>
89
+ </span>
90
+ </summary>
91
+ <div style={{ marginLeft: \\"1rem\\" }}>
92
+ <SchemaItem
93
+ collapsible={false}
94
+ name={\\"innerProp2\\"}
95
+ required={false}
96
+ schemaName={\\"number\\"}
97
+ qualifierMessage={undefined}
98
+ schema={{ type: \\"number\\" }}
99
+ ></SchemaItem>
100
+ </div>
101
+ </details>
102
+ </SchemaItem>;
103
+ ",
104
+ ]
105
+ `;
106
+
107
+ exports[`createNodes allOf should render same-level properties with allOf 1`] = `
108
+ Array [
109
+ "<SchemaItem
110
+ collapsible={false}
111
+ name={\\"parentProp1\\"}
112
+ required={false}
113
+ schemaName={\\"string\\"}
114
+ qualifierMessage={undefined}
115
+ schema={{ type: \\"string\\" }}
116
+ ></SchemaItem>;
117
+ ",
118
+ "<SchemaItem
119
+ collapsible={false}
120
+ name={\\"parentProp2\\"}
121
+ required={false}
122
+ schemaName={\\"string\\"}
123
+ qualifierMessage={undefined}
124
+ schema={{ type: \\"string\\" }}
125
+ ></SchemaItem>;
126
+ ",
127
+ "<SchemaItem
128
+ collapsible={false}
129
+ name={\\"allOfProp1\\"}
130
+ required={false}
131
+ schemaName={\\"string\\"}
132
+ qualifierMessage={undefined}
133
+ schema={{ type: \\"string\\" }}
134
+ ></SchemaItem>;
135
+ ",
136
+ "<SchemaItem
137
+ collapsible={false}
138
+ name={\\"allOfProp2\\"}
139
+ required={false}
140
+ schemaName={\\"string\\"}
141
+ qualifierMessage={undefined}
142
+ schema={{ type: \\"string\\" }}
143
+ ></SchemaItem>;
144
+ ",
145
+ ]
146
+ `;
147
+
148
+ exports[`createNodes oneOf should create readable MODs for oneOf primitive properties 1`] = `
4
149
  Array [
5
150
  "<SchemaItem collapsible={true} className={\\"schemaItem\\"}>
6
151
  <details style={{}} className={\\"openapi-markdown__details\\"}>
@@ -11,47 +11,305 @@ import { createNodes } from "./createSchema";
11
11
  import { SchemaObject } from "../openapi/types";
12
12
 
13
13
  describe("createNodes", () => {
14
- it("should create readable MODs for oneOf primitive properties", () => {
15
- const schema: SchemaObject = {
16
- "x-tags": ["clown"],
17
- type: "object",
18
- properties: {
19
- oneOfProperty: {
20
- oneOf: [
21
- {
22
- type: "object",
23
- properties: {
24
- noseLength: {
25
- type: "number",
14
+ describe("oneOf", () => {
15
+ it("should create readable MODs for oneOf primitive properties", async () => {
16
+ const schema: SchemaObject = {
17
+ "x-tags": ["clown"],
18
+ type: "object",
19
+ properties: {
20
+ oneOfProperty: {
21
+ oneOf: [
22
+ {
23
+ type: "object",
24
+ properties: {
25
+ noseLength: {
26
+ type: "number",
27
+ },
28
+ },
29
+ required: ["noseLength"],
30
+ description: "Clown's nose length",
31
+ },
32
+ {
33
+ type: "array",
34
+ items: {
35
+ type: "string",
36
+ },
37
+ description: "Array of strings",
38
+ },
39
+ {
40
+ type: "boolean",
41
+ },
42
+ {
43
+ type: "number",
44
+ },
45
+ {
46
+ type: "string",
47
+ },
48
+ ],
49
+ },
50
+ },
51
+ };
52
+ expect(
53
+ await Promise.all(
54
+ createNodes(schema, "request").map(
55
+ async (md: any) => await prettier.format(md, { parser: "babel" })
56
+ )
57
+ )
58
+ ).toMatchSnapshot();
59
+ });
60
+ });
61
+
62
+ describe("allOf", () => {
63
+ it("should render same-level properties with allOf", async () => {
64
+ const schema: SchemaObject = {
65
+ allOf: [
66
+ {
67
+ type: "object",
68
+ properties: {
69
+ allOfProp1: {
70
+ type: "string",
71
+ },
72
+ allOfProp2: {
73
+ type: "string",
74
+ },
75
+ },
76
+ },
77
+ ],
78
+ properties: {
79
+ parentProp1: {
80
+ type: "string",
81
+ },
82
+ parentProp2: {
83
+ type: "string",
84
+ },
85
+ },
86
+ };
87
+
88
+ expect(
89
+ await Promise.all(
90
+ createNodes(schema, "response").map(
91
+ async (md: any) => await prettier.format(md, { parser: "babel" })
92
+ )
93
+ )
94
+ ).toMatchSnapshot();
95
+ });
96
+
97
+ it("should correctly merge nested properties from multiple allOf schemas", async () => {
98
+ const schema: SchemaObject = {
99
+ allOf: [
100
+ {
101
+ type: "object",
102
+ properties: {
103
+ outerProp1: {
104
+ type: "object",
105
+ properties: {
106
+ innerProp1: {
107
+ type: "string",
108
+ },
26
109
  },
27
110
  },
28
- required: ["noseLength"],
29
- description: "Clown's nose length",
30
111
  },
31
- {
32
- type: "array",
33
- items: {
112
+ },
113
+ {
114
+ type: "object",
115
+ properties: {
116
+ outerProp2: {
117
+ type: "object",
118
+ properties: {
119
+ innerProp2: {
120
+ type: "number",
121
+ },
122
+ },
123
+ },
124
+ },
125
+ },
126
+ ],
127
+ };
128
+
129
+ expect(
130
+ await Promise.all(
131
+ createNodes(schema, "response").map(
132
+ async (md: any) => await prettier.format(md, { parser: "babel" })
133
+ )
134
+ )
135
+ ).toMatchSnapshot();
136
+ });
137
+
138
+ it("should correctly handle shared required properties across allOf schemas", async () => {
139
+ const schema: SchemaObject = {
140
+ allOf: [
141
+ {
142
+ type: "object",
143
+ properties: {
144
+ sharedProp: {
34
145
  type: "string",
35
146
  },
36
- description: "Array of strings",
37
147
  },
38
- {
39
- type: "boolean",
148
+ required: ["sharedProp"],
149
+ },
150
+ {
151
+ type: "object",
152
+ properties: {
153
+ anotherProp: {
154
+ type: "number",
155
+ },
40
156
  },
41
- {
42
- type: "number",
157
+ required: ["anotherProp"],
158
+ },
159
+ ],
160
+ };
161
+
162
+ expect(
163
+ await Promise.all(
164
+ createNodes(schema, "response").map(
165
+ async (md: any) => await prettier.format(md, { parser: "babel" })
166
+ )
167
+ )
168
+ ).toMatchSnapshot();
169
+ });
170
+
171
+ // Could not resolve values for path:"properties.conflictingProp.type". They are probably incompatible. Values:
172
+ // "string"
173
+ // "number"
174
+ // eslint-disable-next-line jest/no-commented-out-tests
175
+ // it("should handle conflicting properties in allOf schemas", async () => {
176
+ // const schema: SchemaObject = {
177
+ // allOf: [
178
+ // {
179
+ // type: "object",
180
+ // properties: {
181
+ // conflictingProp: {
182
+ // type: "string",
183
+ // },
184
+ // },
185
+ // },
186
+ // {
187
+ // type: "object",
188
+ // properties: {
189
+ // conflictingProp: {
190
+ // type: "number",
191
+ // },
192
+ // },
193
+ // },
194
+ // ],
195
+ // };
196
+
197
+ // expect(
198
+ // await Promise.all(
199
+ // createNodes(schema, "response").map(
200
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
201
+ // )
202
+ // )
203
+ // ).toMatchSnapshot();
204
+ // });
205
+
206
+ // Could not resolve values for path:"type". They are probably incompatible. Values:
207
+ // "object"
208
+ // "array"
209
+ // eslint-disable-next-line jest/no-commented-out-tests
210
+ // it("should handle mixed data types in allOf schemas", async () => {
211
+ // const schema: SchemaObject = {
212
+ // allOf: [
213
+ // {
214
+ // type: "object",
215
+ // properties: {
216
+ // mixedTypeProp1: {
217
+ // type: "string",
218
+ // },
219
+ // },
220
+ // },
221
+ // {
222
+ // type: "array",
223
+ // items: {
224
+ // type: "number",
225
+ // },
226
+ // },
227
+ // ],
228
+ // };
229
+
230
+ // expect(
231
+ // await Promise.all(
232
+ // createNodes(schema, "response").map(
233
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
234
+ // )
235
+ // )
236
+ // ).toMatchSnapshot();
237
+ // });
238
+
239
+ it("should correctly deep merge properties in allOf schemas", async () => {
240
+ const schema: SchemaObject = {
241
+ allOf: [
242
+ {
243
+ type: "object",
244
+ properties: {
245
+ deepProp: {
246
+ type: "object",
247
+ properties: {
248
+ innerProp1: {
249
+ type: "string",
250
+ },
251
+ },
252
+ },
43
253
  },
44
- {
45
- type: "string",
254
+ },
255
+ {
256
+ type: "object",
257
+ properties: {
258
+ deepProp: {
259
+ type: "object",
260
+ properties: {
261
+ innerProp2: {
262
+ type: "number",
263
+ },
264
+ },
265
+ },
46
266
  },
47
- ],
48
- },
49
- },
50
- };
51
- expect(
52
- createNodes(schema, "request").map((md: any) =>
53
- prettier.format(md, { parser: "babel" })
54
- )
55
- ).toMatchSnapshot();
267
+ },
268
+ ],
269
+ };
270
+
271
+ expect(
272
+ await Promise.all(
273
+ createNodes(schema, "response").map(
274
+ async (md: any) => await prettier.format(md, { parser: "babel" })
275
+ )
276
+ )
277
+ ).toMatchSnapshot();
278
+ });
279
+
280
+ // eslint-disable-next-line jest/no-commented-out-tests
281
+ // it("should handle discriminator with allOf schemas", async () => {
282
+ // const schema: SchemaObject = {
283
+ // allOf: [
284
+ // {
285
+ // type: "object",
286
+ // discriminator: {
287
+ // propertyName: "type",
288
+ // },
289
+ // properties: {
290
+ // type: {
291
+ // type: "string",
292
+ // },
293
+ // },
294
+ // },
295
+ // {
296
+ // type: "object",
297
+ // properties: {
298
+ // specificProp: {
299
+ // type: "string",
300
+ // },
301
+ // },
302
+ // },
303
+ // ],
304
+ // };
305
+
306
+ // expect(
307
+ // await Promise.all(
308
+ // createNodes(schema, "response").map(
309
+ // async (md: any) => await prettier.format(md, { parser: "babel" })
310
+ // )
311
+ // )
312
+ // ).toMatchSnapshot();
313
+ // });
56
314
  });
57
315
  });
@@ -73,6 +73,15 @@ function createAnyOneOf(schema: SchemaObject): any {
73
73
  : `MOD${index + 1}`;
74
74
  const anyOneChildren = [];
75
75
 
76
+ if (
77
+ anyOneSchema.type === "object" &&
78
+ !anyOneSchema.properties &&
79
+ !anyOneSchema.allOf &&
80
+ !anyOneSchema.items
81
+ ) {
82
+ anyOneChildren.push(createNodes(anyOneSchema, SCHEMA_TYPE));
83
+ }
84
+
76
85
  if (anyOneSchema.properties !== undefined) {
77
86
  anyOneChildren.push(createProperties(anyOneSchema));
78
87
  delete anyOneSchema.properties;
@@ -526,8 +535,9 @@ function createPropertyDiscriminator(
526
535
  return undefined;
527
536
  }
528
537
 
538
+ // render as a simple property if there's no mapping
529
539
  if (discriminator.mapping === undefined) {
530
- return undefined;
540
+ return createEdges({ name, schema, required });
531
541
  }
532
542
 
533
543
  return create("div", {
@@ -617,6 +627,7 @@ function createEdges({
617
627
  }
618
628
 
619
629
  const schemaName = getSchemaName(schema);
630
+
620
631
  if (discriminator !== undefined && discriminator.propertyName === name) {
621
632
  return createPropertyDiscriminator(
622
633
  name,
@@ -637,6 +648,47 @@ function createEdges({
637
648
  );
638
649
  }
639
650
 
651
+ if (schema.properties !== undefined) {
652
+ return createDetailsNode(
653
+ name,
654
+ schemaName,
655
+ schema,
656
+ required,
657
+ schema.nullable
658
+ );
659
+ }
660
+
661
+ if (schema.additionalProperties !== undefined) {
662
+ return createDetailsNode(
663
+ name,
664
+ schemaName,
665
+ schema,
666
+ required,
667
+ schema.nullable
668
+ );
669
+ }
670
+
671
+ // array of objects
672
+ if (schema.items?.properties !== undefined) {
673
+ return createDetailsNode(
674
+ name,
675
+ schemaName,
676
+ schema,
677
+ required,
678
+ schema.nullable
679
+ );
680
+ }
681
+
682
+ if (schema.items?.anyOf !== undefined || schema.items?.oneOf !== undefined) {
683
+ return createDetailsNode(
684
+ name,
685
+ schemaName,
686
+ schema,
687
+ required,
688
+ schema.nullable
689
+ );
690
+ }
691
+
640
692
  if (schema.allOf !== undefined) {
641
693
  const { mergedSchemas }: { mergedSchemas: SchemaObject } = mergeAllOf(
642
694
  schema.allOf
@@ -709,47 +761,6 @@ function createEdges({
709
761
  });
710
762
  }
711
763
 
712
- if (schema.properties !== undefined) {
713
- return createDetailsNode(
714
- name,
715
- schemaName,
716
- schema,
717
- required,
718
- schema.nullable
719
- );
720
- }
721
-
722
- if (schema.additionalProperties !== undefined) {
723
- return createDetailsNode(
724
- name,
725
- schemaName,
726
- schema,
727
- required,
728
- schema.nullable
729
- );
730
- }
731
-
732
- // array of objects
733
- if (schema.items?.properties !== undefined) {
734
- return createDetailsNode(
735
- name,
736
- schemaName,
737
- schema,
738
- required,
739
- schema.nullable
740
- );
741
- }
742
-
743
- if (schema.items?.anyOf !== undefined || schema.items?.oneOf !== undefined) {
744
- return createDetailsNode(
745
- name,
746
- schemaName,
747
- schema,
748
- required,
749
- schema.nullable
750
- );
751
- }
752
-
753
764
  // primitives and array of non-objects
754
765
  return create("SchemaItem", {
755
766
  collapsible: false,
@@ -789,15 +800,6 @@ export function createNodes(
789
800
  nodes.push(createAnyOneOf(schema));
790
801
  }
791
802
 
792
- if (schema.allOf !== undefined) {
793
- const { mergedSchemas } = mergeAllOf(schema.allOf);
794
-
795
- // allOf seems to always result in properties
796
- if (mergedSchemas.properties !== undefined) {
797
- nodes.push(createProperties(mergedSchemas));
798
- }
799
- }
800
-
801
803
  if (schema.properties !== undefined) {
802
804
  nodes.push(createProperties(schema));
803
805
  }
@@ -811,6 +813,15 @@ export function createNodes(
811
813
  nodes.push(createItems(schema));
812
814
  }
813
815
 
816
+ if (schema.allOf !== undefined) {
817
+ const { mergedSchemas } = mergeAllOf(schema.allOf);
818
+
819
+ // allOf seems to always result in properties
820
+ if (mergedSchemas.properties !== undefined) {
821
+ nodes.push(createProperties(mergedSchemas));
822
+ }
823
+ }
824
+
814
825
  if (nodes.length && nodes.length > 0) {
815
826
  return nodes.filter(Boolean).flat();
816
827
  }
@@ -127,8 +127,9 @@ export function createResponseExamples(
127
127
  value: `${exampleName}`,
128
128
  children: [
129
129
  guard(exampleValue.summary, (summary) => [
130
- create("Markdown", {
131
- children: ` ${summary}`,
130
+ create("div", {
131
+ children: `${summary}`,
132
+ className: "openapi-example__summary",
132
133
  }),
133
134
  ]),
134
135
  create("ResponseSamples", {
@@ -143,8 +144,9 @@ export function createResponseExamples(
143
144
  value: `${exampleName}`,
144
145
  children: [
145
146
  guard(exampleValue.summary, (summary) => [
146
- create("Markdown", {
147
- children: ` ${summary}`,
147
+ create("div", {
148
+ children: `${summary}`,
149
+ className: "openapi-example__summary",
148
150
  }),
149
151
  ]),
150
152
  create("ResponseSamples", {
@@ -171,8 +173,9 @@ export function createResponseExample(responseExample: any, mimeType: string) {
171
173
  value: `Example`,
172
174
  children: [
173
175
  guard(responseExample.summary, (summary) => [
174
- create("Markdown", {
175
- children: ` ${summary}`,
176
+ create("div", {
177
+ children: `${summary}`,
178
+ className: "openapi-example__summary",
176
179
  }),
177
180
  ]),
178
181
  create("ResponseSamples", {
@@ -187,8 +190,9 @@ export function createResponseExample(responseExample: any, mimeType: string) {
187
190
  value: `Example`,
188
191
  children: [
189
192
  guard(responseExample.summary, (summary) => [
190
- create("Markdown", {
191
- children: ` ${summary}`,
193
+ create("div", {
194
+ children: `${summary}`,
195
+ className: "openapi-example__summary",
192
196
  }),
193
197
  ]),
194
198
  create("ResponseSamples", {
@@ -76,7 +76,7 @@ export function createApiPageMD({
76
76
  `import ResponseSamples from "@theme/ResponseSamples";\n`,
77
77
  `import SchemaItem from "@theme/SchemaItem";\n`,
78
78
  `import SchemaTabs from "@theme/SchemaTabs";\n`,
79
- `import Markdown from "@theme/Markdown";\n`,
79
+ `import Heading from "@theme/Heading";\n`,
80
80
  `import OperationTabs from "@theme/OperationTabs";\n`,
81
81
  `import TabItem from "@theme/TabItem";\n\n`,
82
82
  createHeading(title.replace(lessThan, "&lt;").replace(greaterThan, "&gt;")),
@@ -30,8 +30,8 @@ const primitives: Primitives = {
30
30
  string: {
31
31
  default: () => "string",
32
32
  email: () => "user@example.com",
33
- date: () => new Date().toISOString().substring(0, 10),
34
- "date-time": () => new Date().toISOString(),
33
+ date: () => "2024-07-29",
34
+ "date-time": () => "2024-07-29T15:51:28.071Z",
35
35
  uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6",
36
36
  hostname: () => "example.com",
37
37
  ipv4: () => "198.51.100.42",
@@ -30,8 +30,8 @@ const primitives: Primitives = {
30
30
  string: {
31
31
  default: () => "string",
32
32
  email: () => "user@example.com",
33
- date: () => new Date().toISOString().substring(0, 10),
34
- "date-time": () => new Date().toISOString(),
33
+ date: () => "2024-07-29",
34
+ "date-time": () => "2024-07-29T15:51:28.071Z",
35
35
  uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6",
36
36
  hostname: () => "example.com",
37
37
  ipv4: () => "198.51.100.42",