@camunda8/docusaurus-plugin-openapi-docs 4.5.1

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.
Files changed (170) hide show
  1. package/README.md +386 -0
  2. package/lib/index.d.ts +9 -0
  3. package/lib/index.js +714 -0
  4. package/lib/markdown/createArrayBracket.d.ts +2 -0
  5. package/lib/markdown/createArrayBracket.js +36 -0
  6. package/lib/markdown/createAuthentication.d.ts +2 -0
  7. package/lib/markdown/createAuthentication.js +173 -0
  8. package/lib/markdown/createAuthorization.d.ts +1 -0
  9. package/lib/markdown/createAuthorization.js +15 -0
  10. package/lib/markdown/createCallbackMethodEndpoint.d.ts +1 -0
  11. package/lib/markdown/createCallbackMethodEndpoint.js +20 -0
  12. package/lib/markdown/createCallbacks.d.ts +6 -0
  13. package/lib/markdown/createCallbacks.js +77 -0
  14. package/lib/markdown/createContactInfo.d.ts +2 -0
  15. package/lib/markdown/createContactInfo.js +39 -0
  16. package/lib/markdown/createDeprecationNotice.d.ts +6 -0
  17. package/lib/markdown/createDeprecationNotice.js +20 -0
  18. package/lib/markdown/createDescription.d.ts +1 -0
  19. package/lib/markdown/createDescription.js +13 -0
  20. package/lib/markdown/createDetails.d.ts +2 -0
  21. package/lib/markdown/createDetails.js +17 -0
  22. package/lib/markdown/createDetailsSummary.d.ts +2 -0
  23. package/lib/markdown/createDetailsSummary.js +17 -0
  24. package/lib/markdown/createDownload.d.ts +1 -0
  25. package/lib/markdown/createDownload.js +16 -0
  26. package/lib/markdown/createHeading.d.ts +1 -0
  27. package/lib/markdown/createHeading.js +20 -0
  28. package/lib/markdown/createLicense.d.ts +2 -0
  29. package/lib/markdown/createLicense.js +32 -0
  30. package/lib/markdown/createLogo.d.ts +2 -0
  31. package/lib/markdown/createLogo.js +18 -0
  32. package/lib/markdown/createMethodEndpoint.d.ts +1 -0
  33. package/lib/markdown/createMethodEndpoint.js +20 -0
  34. package/lib/markdown/createParamsDetails.d.ts +6 -0
  35. package/lib/markdown/createParamsDetails.js +18 -0
  36. package/lib/markdown/createRequestBodyDetails.d.ts +13 -0
  37. package/lib/markdown/createRequestBodyDetails.js +13 -0
  38. package/lib/markdown/createRequestHeader.d.ts +1 -0
  39. package/lib/markdown/createRequestHeader.js +23 -0
  40. package/lib/markdown/createRequestSchema.d.ts +14 -0
  41. package/lib/markdown/createRequestSchema.js +20 -0
  42. package/lib/markdown/createResponseSchema.d.ts +14 -0
  43. package/lib/markdown/createResponseSchema.js +20 -0
  44. package/lib/markdown/createSchema.d.ts +9 -0
  45. package/lib/markdown/createSchema.js +668 -0
  46. package/lib/markdown/createSchema.test.d.ts +1 -0
  47. package/lib/markdown/createSchema.test.js +913 -0
  48. package/lib/markdown/createStatusCodes.d.ts +9 -0
  49. package/lib/markdown/createStatusCodes.js +63 -0
  50. package/lib/markdown/createTermsOfService.d.ts +1 -0
  51. package/lib/markdown/createTermsOfService.js +31 -0
  52. package/lib/markdown/createVendorExtensions.d.ts +1 -0
  53. package/lib/markdown/createVendorExtensions.js +24 -0
  54. package/lib/markdown/createVersionBadge.d.ts +1 -0
  55. package/lib/markdown/createVersionBadge.js +19 -0
  56. package/lib/markdown/index.d.ts +5 -0
  57. package/lib/markdown/index.js +93 -0
  58. package/lib/markdown/schema.d.ts +3 -0
  59. package/lib/markdown/schema.js +157 -0
  60. package/lib/markdown/schema.test.d.ts +1 -0
  61. package/lib/markdown/schema.test.js +181 -0
  62. package/lib/markdown/utils.d.ts +20 -0
  63. package/lib/markdown/utils.js +68 -0
  64. package/lib/openapi/createRequestExample.d.ts +2 -0
  65. package/lib/openapi/createRequestExample.js +14 -0
  66. package/lib/openapi/createResponseExample.d.ts +2 -0
  67. package/lib/openapi/createResponseExample.js +14 -0
  68. package/lib/openapi/createSchemaExample.d.ts +7 -0
  69. package/lib/openapi/createSchemaExample.js +231 -0
  70. package/lib/openapi/index.d.ts +1 -0
  71. package/lib/openapi/index.js +12 -0
  72. package/lib/openapi/openapi.d.ts +12 -0
  73. package/lib/openapi/openapi.js +634 -0
  74. package/lib/openapi/openapi.test.d.ts +1 -0
  75. package/lib/openapi/openapi.test.js +33 -0
  76. package/lib/openapi/sdkExamples.d.ts +39 -0
  77. package/lib/openapi/sdkExamples.js +141 -0
  78. package/lib/openapi/types.d.ts +352 -0
  79. package/lib/openapi/types.js +8 -0
  80. package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
  81. package/lib/openapi/utils/loadAndResolveSpec.js +153 -0
  82. package/lib/openapi/utils/services/OpenAPIParser.d.ts +52 -0
  83. package/lib/openapi/utils/services/OpenAPIParser.js +343 -0
  84. package/lib/openapi/utils/services/RedocNormalizedOptions.d.ts +100 -0
  85. package/lib/openapi/utils/services/RedocNormalizedOptions.js +170 -0
  86. package/lib/openapi/utils/types/index.d.ts +2 -0
  87. package/lib/openapi/utils/types/index.js +23 -0
  88. package/lib/openapi/utils/types/open-api.d.ts +305 -0
  89. package/lib/openapi/utils/types/open-api.js +8 -0
  90. package/lib/openapi/utils/types.d.ts +307 -0
  91. package/lib/openapi/utils/types.js +8 -0
  92. package/lib/openapi/utils/utils/JsonPointer.d.ts +51 -0
  93. package/lib/openapi/utils/utils/JsonPointer.js +95 -0
  94. package/lib/openapi/utils/utils/helpers.d.ts +43 -0
  95. package/lib/openapi/utils/utils/helpers.js +230 -0
  96. package/lib/openapi/utils/utils/index.d.ts +3 -0
  97. package/lib/openapi/utils/utils/index.js +25 -0
  98. package/lib/openapi/utils/utils/openapi.d.ts +40 -0
  99. package/lib/openapi/utils/utils/openapi.js +605 -0
  100. package/lib/openapi/webhooks.test.d.ts +1 -0
  101. package/lib/openapi/webhooks.test.js +23 -0
  102. package/lib/options.d.ts +2 -0
  103. package/lib/options.js +70 -0
  104. package/lib/sidebars/index.d.ts +4 -0
  105. package/lib/sidebars/index.js +226 -0
  106. package/lib/sidebars/utils.d.ts +2 -0
  107. package/lib/sidebars/utils.js +30 -0
  108. package/lib/types.d.ts +138 -0
  109. package/lib/types.js +8 -0
  110. package/package.json +68 -0
  111. package/src/index.ts +955 -0
  112. package/src/markdown/__snapshots__/createSchema.test.ts.snap +1605 -0
  113. package/src/markdown/createArrayBracket.ts +35 -0
  114. package/src/markdown/createAuthentication.ts +209 -0
  115. package/src/markdown/createAuthorization.ts +13 -0
  116. package/src/markdown/createCallbackMethodEndpoint.ts +19 -0
  117. package/src/markdown/createCallbacks.ts +101 -0
  118. package/src/markdown/createContactInfo.ts +41 -0
  119. package/src/markdown/createDeprecationNotice.ts +31 -0
  120. package/src/markdown/createDescription.ts +12 -0
  121. package/src/markdown/createDetails.ts +16 -0
  122. package/src/markdown/createDetailsSummary.ts +16 -0
  123. package/src/markdown/createDownload.ts +15 -0
  124. package/src/markdown/createHeading.ts +23 -0
  125. package/src/markdown/createLicense.ts +34 -0
  126. package/src/markdown/createLogo.ts +21 -0
  127. package/src/markdown/createMethodEndpoint.ts +19 -0
  128. package/src/markdown/createParamsDetails.ts +22 -0
  129. package/src/markdown/createRequestBodyDetails.ts +24 -0
  130. package/src/markdown/createRequestHeader.ts +22 -0
  131. package/src/markdown/createRequestSchema.ts +32 -0
  132. package/src/markdown/createResponseSchema.ts +32 -0
  133. package/src/markdown/createSchema.test.ts +1075 -0
  134. package/src/markdown/createSchema.ts +864 -0
  135. package/src/markdown/createStatusCodes.ts +63 -0
  136. package/src/markdown/createTermsOfService.ts +30 -0
  137. package/src/markdown/createVendorExtensions.ts +22 -0
  138. package/src/markdown/createVersionBadge.ts +22 -0
  139. package/src/markdown/index.ts +145 -0
  140. package/src/markdown/schema.test.ts +208 -0
  141. package/src/markdown/schema.ts +188 -0
  142. package/src/markdown/utils.ts +89 -0
  143. package/src/openapi/__fixtures__/examples/openapi.yaml +49 -0
  144. package/src/openapi/__fixtures__/webhook/openapi.yaml +17 -0
  145. package/src/openapi/createRequestExample.ts +13 -0
  146. package/src/openapi/createResponseExample.ts +13 -0
  147. package/src/openapi/createSchemaExample.ts +292 -0
  148. package/src/openapi/index.ts +8 -0
  149. package/src/openapi/openapi.test.ts +40 -0
  150. package/src/openapi/openapi.ts +772 -0
  151. package/src/openapi/sdkExamples.ts +195 -0
  152. package/src/openapi/types.ts +458 -0
  153. package/src/openapi/utils/loadAndResolveSpec.ts +171 -0
  154. package/src/openapi/utils/services/OpenAPIParser.ts +434 -0
  155. package/src/openapi/utils/services/RedocNormalizedOptions.ts +330 -0
  156. package/src/openapi/utils/types/index.ts +10 -0
  157. package/src/openapi/utils/types/open-api.ts +303 -0
  158. package/src/openapi/utils/types.ts +304 -0
  159. package/src/openapi/utils/utils/JsonPointer.ts +99 -0
  160. package/src/openapi/utils/utils/helpers.ts +239 -0
  161. package/src/openapi/utils/utils/index.ts +11 -0
  162. package/src/openapi/utils/utils/openapi.ts +771 -0
  163. package/src/openapi/webhooks.test.ts +30 -0
  164. package/src/openapi-to-postmanv2.d.ts +10 -0
  165. package/src/options.ts +78 -0
  166. package/src/plugin-openapi.d.ts +88 -0
  167. package/src/sidebars/index.ts +322 -0
  168. package/src/sidebars/utils.ts +29 -0
  169. package/src/types.ts +180 -0
  170. package/tsconfig.json +7 -0
package/src/index.ts ADDED
@@ -0,0 +1,955 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import fs from "fs";
9
+ import path from "path";
10
+ import zlib from "zlib";
11
+
12
+ import type { LoadContext, Plugin } from "@docusaurus/types";
13
+ import { Globby, posixPath } from "@docusaurus/utils";
14
+ import chalk from "chalk";
15
+ import JSON5 from "json5";
16
+ import { render } from "mustache";
17
+
18
+ import {
19
+ createApiPageMD,
20
+ createInfoPageMD,
21
+ createSchemaPageMD,
22
+ createTagPageMD,
23
+ } from "./markdown";
24
+ import { processOpenapiFiles, readOpenapiFiles } from "./openapi";
25
+ import { OptionsSchema } from "./options";
26
+ import generateSidebarSlice from "./sidebars";
27
+ import type {
28
+ ApiMetadata,
29
+ APIOptions,
30
+ ApiPageMetadata,
31
+ InfoPageMetadata,
32
+ LoadedContent,
33
+ PluginOptions,
34
+ SchemaPageMetadata,
35
+ TagPageMetadata,
36
+ } from "./types";
37
+
38
+ export function isURL(str: string): boolean {
39
+ return /^(https?:)\/\//m.test(str);
40
+ }
41
+
42
+ export function getDocsPluginConfig(
43
+ presetsPlugins: any[],
44
+ plugin: string,
45
+ pluginId: string
46
+ ): Object | undefined {
47
+ // eslint-disable-next-line array-callback-return
48
+ const filteredConfig = presetsPlugins.filter((data) => {
49
+ // Search presets
50
+ if (Array.isArray(data)) {
51
+ if (typeof data[0] === "string" && data[0].endsWith(pluginId)) {
52
+ return data[1];
53
+ }
54
+
55
+ // Search plugin-content-docs instances
56
+ if (typeof data[0] === "string" && data[0] === plugin) {
57
+ const configPluginId = data[1].id ? data[1].id : "default";
58
+ if (configPluginId === pluginId) {
59
+ return data[1];
60
+ }
61
+ }
62
+ }
63
+ })[0];
64
+
65
+ if (filteredConfig) {
66
+ // Search presets, e.g. "classic"
67
+ if (filteredConfig[0].endsWith(pluginId)) {
68
+ return filteredConfig[1].docs;
69
+ }
70
+
71
+ // Search plugin-content-docs instances
72
+ if (filteredConfig[0] === plugin) {
73
+ const configPluginId = filteredConfig[1].id
74
+ ? filteredConfig[1].id
75
+ : "default";
76
+ if (configPluginId === pluginId) {
77
+ return filteredConfig[1];
78
+ }
79
+ }
80
+ }
81
+ return;
82
+ }
83
+
84
+ function getPluginConfig(plugins: any[], pluginId: string): any {
85
+ return plugins.filter((data) => data[1].id === pluginId)[0][1];
86
+ }
87
+
88
+ function getPluginInstances(plugins: any[]): any {
89
+ return plugins.filter((data) => data[0] === "@camunda8/docusaurus-plugin-openapi-docs");
90
+ }
91
+
92
+ export default function pluginOpenAPIDocs(
93
+ context: LoadContext,
94
+ options: PluginOptions
95
+ ): Plugin<LoadedContent> {
96
+ const {
97
+ config,
98
+ docsPlugin = "@docusaurus/plugin-content-docs",
99
+ docsPluginId,
100
+ } = options;
101
+ const { siteDir, siteConfig } = context;
102
+
103
+ // Get routeBasePath and path from plugin-content-docs or preset
104
+ const presets: any = siteConfig.presets;
105
+ const plugins: any = siteConfig.plugins;
106
+ const presetsPlugins = presets.concat(plugins);
107
+ let docData: any = getDocsPluginConfig(
108
+ presetsPlugins,
109
+ docsPlugin,
110
+ docsPluginId
111
+ );
112
+ let docRouteBasePath = docData ? docData.routeBasePath : undefined;
113
+ let docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
114
+
115
+ async function generateApiDocs(options: APIOptions, pluginId: any) {
116
+ let {
117
+ specPath,
118
+ outputDir,
119
+ template,
120
+ infoTemplate,
121
+ tagTemplate,
122
+ schemaTemplate,
123
+ markdownGenerators,
124
+ downloadUrl,
125
+ sidebarOptions,
126
+ disableCompression,
127
+ } = options;
128
+
129
+ // Remove trailing slash before proceeding
130
+ outputDir = outputDir.replace(/\/$/, "");
131
+
132
+ // Override docPath if pluginId provided
133
+ if (pluginId) {
134
+ docData = getDocsPluginConfig(presetsPlugins, docsPlugin, pluginId);
135
+ docRouteBasePath = docData ? docData.routeBasePath : undefined;
136
+ docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
137
+ }
138
+
139
+ const contentPath = isURL(specPath)
140
+ ? specPath
141
+ : path.resolve(siteDir, specPath);
142
+
143
+ try {
144
+ const openapiFiles = await readOpenapiFiles(contentPath);
145
+ const [loadedApi, tags, tagGroups] = await processOpenapiFiles(
146
+ openapiFiles,
147
+ options,
148
+ sidebarOptions!,
149
+ siteDir
150
+ );
151
+ if (!fs.existsSync(outputDir)) {
152
+ try {
153
+ fs.mkdirSync(outputDir, { recursive: true });
154
+ console.log(chalk.green(`Successfully created "${outputDir}"`));
155
+ } catch (err) {
156
+ console.error(
157
+ chalk.red(`Failed to create "${outputDir}"`),
158
+ chalk.yellow(err)
159
+ );
160
+ }
161
+ }
162
+
163
+ // TODO: figure out better way to set default
164
+ if (Object.keys(sidebarOptions ?? {}).length > 0) {
165
+ const sidebarSlice = generateSidebarSlice(
166
+ sidebarOptions!,
167
+ options,
168
+ loadedApi,
169
+ tags,
170
+ docPath,
171
+ tagGroups
172
+ );
173
+
174
+ let sidebarSliceTemplate = `import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";\n\n`;
175
+ sidebarSliceTemplate += `const sidebar: SidebarsConfig = {{{slice}}};\n\n`;
176
+ sidebarSliceTemplate += `export default sidebar.apisidebar;\n`;
177
+
178
+ const view = render(sidebarSliceTemplate, {
179
+ slice: JSON5.stringify(
180
+ { apisidebar: sidebarSlice },
181
+ { space: 2, quote: '"' }
182
+ ),
183
+ });
184
+
185
+ if (!fs.existsSync(`${outputDir}/sidebar.ts`)) {
186
+ try {
187
+ fs.writeFileSync(`${outputDir}/sidebar.ts`, view, "utf8");
188
+ console.log(
189
+ chalk.green(`Successfully created "${outputDir}/sidebar.ts"`)
190
+ );
191
+ } catch (err) {
192
+ console.error(
193
+ chalk.red(`Failed to write "${outputDir}/sidebar.ts"`),
194
+ chalk.yellow(err)
195
+ );
196
+ }
197
+ }
198
+ }
199
+
200
+ const mdTemplate = template
201
+ ? fs.readFileSync(template).toString()
202
+ : `---
203
+ id: {{{id}}}
204
+ title: "{{{title}}}"
205
+ description: "{{{frontMatter.description}}}"
206
+ {{^api}}
207
+ sidebar_label: Introduction
208
+ {{/api}}
209
+ {{#api}}
210
+ sidebar_label: "{{{title}}}"
211
+ {{/api}}
212
+ {{^api}}
213
+ sidebar_position: 0
214
+ {{/api}}
215
+ hide_title: true
216
+ {{#api}}
217
+ hide_table_of_contents: true
218
+ {{/api}}
219
+ {{#json}}
220
+ api: {{{json}}}
221
+ {{/json}}
222
+ {{#api.method}}
223
+ sidebar_class_name: "{{{api.method}}} api-method"
224
+ {{/api.method}}
225
+ {{#infoPath}}
226
+ info_path: {{{infoPath}}}
227
+ {{/infoPath}}
228
+ custom_edit_url: null
229
+ {{#frontMatter.proxy}}
230
+ proxy: {{{frontMatter.proxy}}}
231
+ {{/frontMatter.proxy}}
232
+ {{#frontMatter.hide_send_button}}
233
+ hide_send_button: true
234
+ {{/frontMatter.hide_send_button}}
235
+ {{#frontMatter.show_extensions}}
236
+ show_extensions: true
237
+ {{/frontMatter.show_extensions}}
238
+ {{#frontMatter.mask_credentials_disabled}}
239
+ mask_credentials: false
240
+ {{/frontMatter.mask_credentials_disabled}}
241
+ ---
242
+
243
+ {{{markdown}}}
244
+ `;
245
+
246
+ const infoMdTemplate = infoTemplate
247
+ ? fs.readFileSync(infoTemplate).toString()
248
+ : `---
249
+ id: {{{id}}}
250
+ title: "{{{title}}}"
251
+ description: "{{{frontMatter.description}}}"
252
+ sidebar_label: "{{{title}}}"
253
+ hide_title: true
254
+ custom_edit_url: null
255
+ ---
256
+
257
+ {{{markdown}}}
258
+
259
+ \`\`\`mdx-code-block
260
+ import DocCardList from '@theme/DocCardList';
261
+ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
262
+
263
+ <DocCardList items={useCurrentSidebarCategory().items}/>
264
+ \`\`\`
265
+ `;
266
+
267
+ const tagMdTemplate = tagTemplate
268
+ ? fs.readFileSync(tagTemplate).toString()
269
+ : `---
270
+ id: {{{id}}}
271
+ title: "{{{frontMatter.description}}}"
272
+ description: "{{{frontMatter.description}}}"
273
+ custom_edit_url: null
274
+ ---
275
+
276
+ {{{markdown}}}
277
+
278
+ \`\`\`mdx-code-block
279
+ import DocCardList from '@theme/DocCardList';
280
+ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
281
+
282
+ <DocCardList items={useCurrentSidebarCategory().items}/>
283
+ \`\`\`
284
+ `;
285
+
286
+ const schemaMdTemplate = schemaTemplate
287
+ ? fs.readFileSync(schemaTemplate).toString()
288
+ : `---
289
+ id: {{{id}}}
290
+ title: "{{{title}}}"
291
+ description: "{{{frontMatter.description}}}"
292
+ sidebar_label: "{{{title}}}"
293
+ hide_title: true
294
+ {{#schema}}
295
+ hide_table_of_contents: true
296
+ {{/schema}}
297
+ schema: true
298
+ sample: {{{frontMatter.sample}}}
299
+ custom_edit_url: null
300
+ ---
301
+
302
+ {{{markdown}}}
303
+ `;
304
+
305
+ const apiPageGenerator =
306
+ markdownGenerators?.createApiPageMD ?? createApiPageMD;
307
+ const infoPageGenerator =
308
+ markdownGenerators?.createInfoPageMD ?? createInfoPageMD;
309
+ const tagPageGenerator =
310
+ markdownGenerators?.createTagPageMD ?? createTagPageMD;
311
+ const schemaPageGenerator =
312
+ markdownGenerators?.createSchemaPageMD ?? createSchemaPageMD;
313
+
314
+ const pageGeneratorByType: {
315
+ [key in ApiMetadata["type"]]: (
316
+ pageData: {
317
+ api: ApiPageMetadata;
318
+ info: InfoPageMetadata;
319
+ tag: TagPageMetadata;
320
+ schema: SchemaPageMetadata;
321
+ }[key]
322
+ ) => string;
323
+ } = {
324
+ api: apiPageGenerator,
325
+ info: infoPageGenerator,
326
+ tag: tagPageGenerator,
327
+ schema: schemaPageGenerator,
328
+ };
329
+
330
+ loadedApi.map(async (item) => {
331
+ if (downloadUrl) {
332
+ item.downloadUrl = downloadUrl;
333
+ }
334
+ const markdown = pageGeneratorByType[item.type](item as any);
335
+ item.markdown = markdown;
336
+ if (item.type === "api") {
337
+ // opportunity to compress JSON
338
+ // const serialize = (o: any) => {
339
+ // return zlib.deflateSync(JSON.stringify(o)).toString("base64");
340
+ // };
341
+ // const deserialize = (s: any) => {
342
+ // return zlib.inflateSync(Buffer.from(s, "base64")).toString();
343
+ // };
344
+ disableCompression === true
345
+ ? (item.json = JSON.stringify(item.api))
346
+ : (item.json = zlib
347
+ .deflateSync(JSON.stringify(item.api))
348
+ .toString("base64"));
349
+ let infoBasePath = `${outputDir}/${item.infoId}`;
350
+ if (docRouteBasePath) {
351
+ infoBasePath = `${docRouteBasePath}/${outputDir
352
+ .split(docPath!)[1]
353
+ .replace(/^\/+/g, "")}/${item.infoId}`.replace(/^\/+/g, "");
354
+ }
355
+ if (item.infoId) item.infoPath = infoBasePath;
356
+ }
357
+
358
+ const view = render(mdTemplate, item);
359
+ const utils = render(infoMdTemplate, item);
360
+ // eslint-disable-next-line testing-library/render-result-naming-convention
361
+ const tagUtils = render(tagMdTemplate, item);
362
+
363
+ if (item.type === "api") {
364
+ if (!fs.existsSync(`${outputDir}/${item.id}.api.mdx`)) {
365
+ try {
366
+ // kebabCase(arg) returns 0-length string when arg is undefined
367
+ if (item.id.length === 0) {
368
+ throw Error(
369
+ "Operation must have summary or operationId defined"
370
+ );
371
+ }
372
+ fs.writeFileSync(`${outputDir}/${item.id}.api.mdx`, view, "utf8");
373
+ console.log(
374
+ chalk.green(
375
+ `Successfully created "${outputDir}/${item.id}.api.mdx"`
376
+ )
377
+ );
378
+ } catch (err) {
379
+ console.error(
380
+ chalk.red(`Failed to write "${outputDir}/${item.id}.api.mdx"`),
381
+ chalk.yellow(err)
382
+ );
383
+ }
384
+ }
385
+ }
386
+
387
+ if (item.type === "info") {
388
+ if (!fs.existsSync(`${outputDir}/${item.id}.info.mdx`)) {
389
+ try {
390
+ sidebarOptions?.categoryLinkSource === "info" || infoTemplate // Only use utils template if set to "info" or if infoTemplate is set
391
+ ? fs.writeFileSync(
392
+ `${outputDir}/${item.id}.info.mdx`,
393
+ utils,
394
+ "utf8"
395
+ )
396
+ : fs.writeFileSync(
397
+ `${outputDir}/${item.id}.info.mdx`,
398
+ view,
399
+ "utf8"
400
+ );
401
+ console.log(
402
+ chalk.green(
403
+ `Successfully created "${outputDir}/${item.id}.info.mdx"`
404
+ )
405
+ );
406
+ } catch (err) {
407
+ console.error(
408
+ chalk.red(`Failed to write "${outputDir}/${item.id}.info.mdx"`),
409
+ chalk.yellow(err)
410
+ );
411
+ }
412
+ }
413
+ }
414
+
415
+ if (item.type === "tag") {
416
+ if (!fs.existsSync(`${outputDir}/${item.id}.tag.mdx`)) {
417
+ try {
418
+ fs.writeFileSync(
419
+ `${outputDir}/${item.id}.tag.mdx`,
420
+ tagUtils,
421
+ "utf8"
422
+ );
423
+ console.log(
424
+ chalk.green(
425
+ `Successfully created "${outputDir}/${item.id}.tag.mdx"`
426
+ )
427
+ );
428
+ } catch (err) {
429
+ console.error(
430
+ chalk.red(`Failed to write "${outputDir}/${item.id}.tag.mdx"`),
431
+ chalk.yellow(err)
432
+ );
433
+ }
434
+ }
435
+ }
436
+
437
+ if (item.type === "schema") {
438
+ if (!fs.existsSync(`${outputDir}/schemas/${item.id}.schema.mdx`)) {
439
+ if (!fs.existsSync(`${outputDir}/schemas`)) {
440
+ try {
441
+ fs.mkdirSync(`${outputDir}/schemas`, { recursive: true });
442
+ console.log(
443
+ chalk.green(`Successfully created "${outputDir}/schemas"`)
444
+ );
445
+ } catch (err) {
446
+ console.error(
447
+ chalk.red(`Failed to create "${outputDir}/schemas"`),
448
+ chalk.yellow(err)
449
+ );
450
+ }
451
+ }
452
+ try {
453
+ // kebabCase(arg) returns 0-length string when arg is undefined
454
+ if (item.id.length === 0) {
455
+ throw Error("Schema must have title defined");
456
+ }
457
+ // eslint-disable-next-line testing-library/render-result-naming-convention
458
+ const schemaView = render(schemaMdTemplate, item);
459
+ fs.writeFileSync(
460
+ `${outputDir}/schemas/${item.id}.schema.mdx`,
461
+ schemaView,
462
+ "utf8"
463
+ );
464
+ console.log(
465
+ chalk.green(
466
+ `Successfully created "${outputDir}/${item.id}.schema.mdx"`
467
+ )
468
+ );
469
+ } catch (err) {
470
+ console.error(
471
+ chalk.red(
472
+ `Failed to write "${outputDir}/${item.id}.schema.mdx"`
473
+ ),
474
+ chalk.yellow(err)
475
+ );
476
+ }
477
+ }
478
+ }
479
+ return;
480
+ });
481
+
482
+ return;
483
+ } catch (e) {
484
+ console.error(chalk.red(`Loading of api failed for "${contentPath}"`));
485
+ throw e;
486
+ }
487
+ }
488
+
489
+ async function cleanApiDocs(options: APIOptions) {
490
+ const { outputDir } = options;
491
+ const apiDir = posixPath(path.join(siteDir, outputDir));
492
+ const apiMdxFiles = await Globby(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
493
+ cwd: path.resolve(apiDir),
494
+ deep: 1,
495
+ });
496
+ const sidebarFile = await Globby(["sidebar.js", "sidebar.ts"], {
497
+ cwd: path.resolve(apiDir),
498
+ deep: 1,
499
+ });
500
+ apiMdxFiles.map((mdx) =>
501
+ fs.unlink(`${apiDir}/${mdx}`, (err) => {
502
+ if (err) {
503
+ console.error(
504
+ chalk.red(`Cleanup failed for "${apiDir}/${mdx}"`),
505
+ chalk.yellow(err)
506
+ );
507
+ } else {
508
+ console.log(chalk.green(`Cleanup succeeded for "${apiDir}/${mdx}"`));
509
+ }
510
+ })
511
+ );
512
+
513
+ try {
514
+ fs.rmSync(`${apiDir}/schemas`, { recursive: true });
515
+ console.log(chalk.green(`Cleanup succeeded for "${apiDir}/schemas"`));
516
+ } catch (err: any) {
517
+ if (err.code !== "ENOENT") {
518
+ console.error(
519
+ chalk.red(`Cleanup failed for "${apiDir}/schemas"`),
520
+ chalk.yellow(err)
521
+ );
522
+ }
523
+ }
524
+
525
+ sidebarFile.map((sidebar) =>
526
+ fs.unlink(`${apiDir}/${sidebar}`, (err) => {
527
+ if (err) {
528
+ console.error(
529
+ chalk.red(`Cleanup failed for "${apiDir}/${sidebar}"`),
530
+ chalk.yellow(err)
531
+ );
532
+ } else {
533
+ console.log(
534
+ chalk.green(`Cleanup succeeded for "${apiDir}/${sidebar}"`)
535
+ );
536
+ }
537
+ })
538
+ );
539
+ }
540
+
541
+ async function generateVersions(versions: object, outputDir: string) {
542
+ let versionsArray = [] as object[];
543
+ for (const [version, metadata] of Object.entries(versions)) {
544
+ versionsArray.push({
545
+ version: version,
546
+ label: metadata.label,
547
+ baseUrl: metadata.baseUrl,
548
+ downloadUrl: metadata.downloadUrl,
549
+ });
550
+ }
551
+
552
+ if (!fs.existsSync(outputDir)) {
553
+ try {
554
+ fs.mkdirSync(outputDir, { recursive: true });
555
+ console.log(chalk.green(`Successfully created "${outputDir}"`));
556
+ } catch (err) {
557
+ console.error(
558
+ chalk.red(`Failed to create "${outputDir}"`),
559
+ chalk.yellow(err)
560
+ );
561
+ }
562
+ }
563
+
564
+ const versionsJson = JSON.stringify(versionsArray, null, 2);
565
+ try {
566
+ fs.writeFileSync(
567
+ `${outputDir}/versions.json`,
568
+ versionsJson + "\n",
569
+ "utf8"
570
+ );
571
+ console.log(
572
+ chalk.green(`Successfully created "${outputDir}/versions.json"`)
573
+ );
574
+ } catch (err) {
575
+ console.error(
576
+ chalk.red(`Failed to write "${outputDir}/versions.json"`),
577
+ chalk.yellow(err)
578
+ );
579
+ }
580
+ }
581
+
582
+ async function cleanVersions(outputDir: string) {
583
+ if (fs.existsSync(`${outputDir}/versions.json`)) {
584
+ fs.unlink(`${outputDir}/versions.json`, (err) => {
585
+ if (err) {
586
+ console.error(
587
+ chalk.red(`Cleanup failed for "${outputDir}/versions.json"`),
588
+ chalk.yellow(err)
589
+ );
590
+ } else {
591
+ console.log(
592
+ chalk.green(`Cleanup succeeded for "${outputDir}/versions.json"`)
593
+ );
594
+ }
595
+ });
596
+ }
597
+ }
598
+
599
+ async function generateAllVersions(options: APIOptions, pluginId: any) {
600
+ const parentOptions = Object.assign({}, options);
601
+ const { versions } = parentOptions as any;
602
+
603
+ if (versions != null && Object.keys(versions).length > 0) {
604
+ const version = parentOptions.version as string;
605
+ const label = parentOptions.label as string;
606
+
607
+ const baseUrl = parentOptions.baseUrl as string;
608
+ let parentVersion = {} as any;
609
+
610
+ parentVersion[version] = { label: label, baseUrl: baseUrl };
611
+ const mergedVersions = Object.assign(parentVersion, versions);
612
+
613
+ // Prepare for merge
614
+ delete parentOptions.versions;
615
+ delete parentOptions.version;
616
+ delete parentOptions.label;
617
+ delete parentOptions.baseUrl;
618
+ delete parentOptions.downloadUrl;
619
+
620
+ await generateVersions(mergedVersions, parentOptions.outputDir);
621
+ Object.keys(versions).forEach(async (key) => {
622
+ if (key === "all") {
623
+ console.error(
624
+ chalk.red(
625
+ "Can't use id 'all' for OpenAPI docs versions configuration key."
626
+ )
627
+ );
628
+ }
629
+ const versionOptions = versions[key];
630
+ const mergedOptions = {
631
+ ...parentOptions,
632
+ ...versionOptions,
633
+ };
634
+ await generateApiDocs(mergedOptions, pluginId);
635
+ });
636
+ }
637
+ }
638
+
639
+ async function cleanAllVersions(options: APIOptions) {
640
+ const parentOptions = Object.assign({}, options);
641
+
642
+ const { versions } = parentOptions as any;
643
+
644
+ delete parentOptions.versions;
645
+
646
+ if (versions != null && Object.keys(versions).length > 0) {
647
+ await cleanVersions(parentOptions.outputDir);
648
+ Object.keys(versions).forEach(async (key) => {
649
+ const versionOptions = versions[key];
650
+ const mergedOptions = {
651
+ ...parentOptions,
652
+ ...versionOptions,
653
+ };
654
+ await cleanApiDocs(mergedOptions);
655
+ });
656
+ }
657
+ }
658
+
659
+ return {
660
+ name: `@camunda8/docusaurus-plugin-openapi-docs`,
661
+
662
+ extendCli(cli): void {
663
+ cli
664
+ .command(`gen-api-docs`)
665
+ .description(
666
+ `Generates OpenAPI docs in MDX file format and sidebar.ts (if enabled).`
667
+ )
668
+ .usage("<id>")
669
+ .arguments("<id>")
670
+ .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
671
+ .option("--all-versions", "Generate all versions.")
672
+ .action(async (id, instance) => {
673
+ const options = instance.opts();
674
+ const pluginId = options.pluginId;
675
+ const allVersions = options.allVersions;
676
+ const pluginInstances = getPluginInstances(plugins);
677
+ let targetConfig: any;
678
+ let targetDocsPluginId: any;
679
+ if (pluginId) {
680
+ try {
681
+ const pluginConfig = getPluginConfig(plugins, pluginId);
682
+ targetConfig = pluginConfig.config ?? {};
683
+ targetDocsPluginId = pluginConfig.docsPluginId;
684
+ } catch {
685
+ console.error(
686
+ chalk.red(`OpenAPI docs plugin ID '${pluginId}' not found.`)
687
+ );
688
+ return;
689
+ }
690
+ } else {
691
+ if (pluginInstances.length > 1) {
692
+ console.error(
693
+ chalk.red(
694
+ "OpenAPI docs plugin ID must be specified when more than one plugin instance exists."
695
+ )
696
+ );
697
+ return;
698
+ }
699
+ targetConfig = config;
700
+ }
701
+
702
+ if (id === "all") {
703
+ if (targetConfig[id]) {
704
+ console.error(
705
+ chalk.red(
706
+ "Can't use id 'all' for OpenAPI docs configuration key."
707
+ )
708
+ );
709
+ } else {
710
+ Object.keys(targetConfig).forEach(async function (key) {
711
+ await generateApiDocs(targetConfig[key], targetDocsPluginId);
712
+ if (allVersions) {
713
+ await generateAllVersions(
714
+ targetConfig[key],
715
+ targetDocsPluginId
716
+ );
717
+ }
718
+ });
719
+ }
720
+ } else if (!targetConfig[id]) {
721
+ console.error(
722
+ chalk.red(`ID '${id}' does not exist in OpenAPI docs config.`)
723
+ );
724
+ } else {
725
+ await generateApiDocs(targetConfig[id], targetDocsPluginId);
726
+ if (allVersions) {
727
+ await generateAllVersions(targetConfig[id], targetDocsPluginId);
728
+ }
729
+ }
730
+ });
731
+
732
+ cli
733
+ .command(`gen-api-docs:version`)
734
+ .description(
735
+ `Generates versioned OpenAPI docs in MDX file format, versions.js and sidebar.ts (if enabled).`
736
+ )
737
+ .usage("<id:version>")
738
+ .arguments("<id:version>")
739
+ .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
740
+ .action(async (id, instance) => {
741
+ const options = instance.opts();
742
+ const pluginId = options.pluginId;
743
+ const pluginInstances = getPluginInstances(plugins);
744
+ let targetConfig: any;
745
+ let targetDocsPluginId: any;
746
+ if (pluginId) {
747
+ try {
748
+ const pluginConfig = getPluginConfig(plugins, pluginId);
749
+ targetConfig = pluginConfig.config ?? {};
750
+ targetDocsPluginId = pluginConfig.docsPluginId;
751
+ } catch {
752
+ console.error(
753
+ chalk.red(`OpenAPI docs plugin ID '${pluginId}' not found.`)
754
+ );
755
+ return;
756
+ }
757
+ } else {
758
+ if (pluginInstances.length > 1) {
759
+ console.error(
760
+ chalk.red(
761
+ "OpenAPI docs plugin ID must be specified when more than one plugin instance exists."
762
+ )
763
+ );
764
+ return;
765
+ }
766
+ targetConfig = config;
767
+ }
768
+ const [parentId, versionId] = id.split(":");
769
+ const parentConfig = Object.assign({}, targetConfig[parentId]);
770
+
771
+ const version = parentConfig.version as string;
772
+ const label = parentConfig.label as string;
773
+ const baseUrl = parentConfig.baseUrl as string;
774
+
775
+ let parentVersion = {} as any;
776
+ parentVersion[version] = { label: label, baseUrl: baseUrl };
777
+
778
+ const { versions } = targetConfig[parentId] as any;
779
+ const mergedVersions = Object.assign(parentVersion, versions);
780
+
781
+ // Prepare for merge
782
+ delete parentConfig.versions;
783
+ delete parentConfig.version;
784
+ delete parentConfig.label;
785
+ delete parentConfig.baseUrl;
786
+ delete parentConfig.downloadUrl;
787
+
788
+ // TODO: handle when no versions are defined by version command is passed
789
+ if (versionId === "all") {
790
+ if (versions[id]) {
791
+ console.error(
792
+ chalk.red(
793
+ "Can't use id 'all' for OpenAPI docs versions configuration key."
794
+ )
795
+ );
796
+ } else {
797
+ await generateVersions(mergedVersions, parentConfig.outputDir);
798
+ Object.keys(versions).forEach(async (key) => {
799
+ const versionConfig = versions[key];
800
+ const mergedConfig = {
801
+ ...parentConfig,
802
+ ...versionConfig,
803
+ };
804
+ await generateApiDocs(mergedConfig, targetDocsPluginId);
805
+ });
806
+ }
807
+ } else if (!versions[versionId]) {
808
+ console.error(
809
+ chalk.red(
810
+ `Version ID '${versionId}' does not exist in OpenAPI docs versions config.`
811
+ )
812
+ );
813
+ } else {
814
+ const versionConfig = versions[versionId];
815
+ const mergedConfig = {
816
+ ...parentConfig,
817
+ ...versionConfig,
818
+ };
819
+ await generateVersions(mergedVersions, parentConfig.outputDir);
820
+ await generateApiDocs(mergedConfig, targetDocsPluginId);
821
+ }
822
+ });
823
+
824
+ cli
825
+ .command(`clean-api-docs`)
826
+ .description(
827
+ `Clears the generated OpenAPI docs MDX files and sidebar.ts (if enabled).`
828
+ )
829
+ .usage("<id>")
830
+ .arguments("<id>")
831
+ .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
832
+ .option("--all-versions", "Clean all versions.")
833
+ .action(async (id, instance) => {
834
+ const options = instance.opts();
835
+ const pluginId = options.pluginId;
836
+ const allVersions = options.allVersions;
837
+ const pluginInstances = getPluginInstances(plugins);
838
+ let targetConfig: any;
839
+ if (pluginId) {
840
+ try {
841
+ const pluginConfig = getPluginConfig(plugins, pluginId);
842
+ targetConfig = pluginConfig.config ?? {};
843
+ } catch {
844
+ console.error(
845
+ chalk.red(`OpenAPI docs plugin ID '${pluginId}' not found.`)
846
+ );
847
+ return;
848
+ }
849
+ } else {
850
+ if (pluginInstances.length > 1) {
851
+ console.error(
852
+ chalk.red(
853
+ "OpenAPI docs plugin ID must be specified when more than one plugin instance exists."
854
+ )
855
+ );
856
+ return;
857
+ }
858
+ targetConfig = config;
859
+ }
860
+ if (id === "all") {
861
+ if (targetConfig[id]) {
862
+ console.error(
863
+ chalk.red(
864
+ "Can't use id 'all' for OpenAPI docs configuration key."
865
+ )
866
+ );
867
+ } else {
868
+ Object.keys(targetConfig).forEach(async function (key) {
869
+ await cleanApiDocs(targetConfig[key]);
870
+ if (allVersions) {
871
+ await cleanAllVersions(targetConfig[key]);
872
+ }
873
+ });
874
+ }
875
+ } else {
876
+ await cleanApiDocs(targetConfig[id]);
877
+ if (allVersions) {
878
+ await cleanAllVersions(targetConfig[id]);
879
+ }
880
+ }
881
+ });
882
+
883
+ cli
884
+ .command(`clean-api-docs:version`)
885
+ .description(
886
+ `Clears the versioned, generated OpenAPI docs MDX files, versions.json and sidebar.ts (if enabled).`
887
+ )
888
+ .usage("<id:version>")
889
+ .arguments("<id:version>")
890
+ .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
891
+ .action(async (id, instance) => {
892
+ const options = instance.opts();
893
+ const pluginId = options.pluginId;
894
+ const pluginInstances = getPluginInstances(plugins);
895
+ let targetConfig: any;
896
+ if (pluginId) {
897
+ try {
898
+ const pluginConfig = getPluginConfig(plugins, pluginId);
899
+ targetConfig = pluginConfig.config ?? {};
900
+ } catch {
901
+ console.error(
902
+ chalk.red(`OpenAPI docs plugin ID '${pluginId}' not found.`)
903
+ );
904
+ return;
905
+ }
906
+ } else {
907
+ if (pluginInstances.length > 1) {
908
+ console.error(
909
+ chalk.red(
910
+ "OpenAPI docs plugin ID must be specified when more than one plugin instance exists."
911
+ )
912
+ );
913
+ return;
914
+ }
915
+ targetConfig = config;
916
+ }
917
+ const [parentId, versionId] = id.split(":");
918
+ const { versions } = targetConfig[parentId] as any;
919
+
920
+ const parentConfig = Object.assign({}, targetConfig[parentId]);
921
+ delete parentConfig.versions;
922
+
923
+ if (versionId === "all") {
924
+ if (versions[id]) {
925
+ chalk.red(
926
+ "Can't use id 'all' for OpenAPI docs versions configuration key."
927
+ );
928
+ } else {
929
+ await cleanVersions(parentConfig.outputDir);
930
+ Object.keys(versions).forEach(async (key) => {
931
+ const versionConfig = versions[key];
932
+ const mergedConfig = {
933
+ ...parentConfig,
934
+ ...versionConfig,
935
+ };
936
+ await cleanApiDocs(mergedConfig);
937
+ });
938
+ }
939
+ } else {
940
+ const versionConfig = versions[versionId];
941
+ const mergedConfig = {
942
+ ...parentConfig,
943
+ ...versionConfig,
944
+ };
945
+ await cleanApiDocs(mergedConfig);
946
+ }
947
+ });
948
+ },
949
+ };
950
+ }
951
+
952
+ pluginOpenAPIDocs.validateOptions = ({ options, validate }: any) => {
953
+ const validatedOptions = validate(OptionsSchema, options);
954
+ return validatedOptions;
955
+ };