docusaurus-plugin-openapi-docs 1.0.3 → 1.0.4
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 +103 -15
- package/lib/index.d.ts +6 -1
- package/lib/index.js +180 -16
- package/lib/markdown/createParamsDetails.js +2 -0
- package/lib/markdown/createSchemaDetails.js +2 -0
- package/lib/markdown/index.js +1 -1
- package/lib/options.d.ts +0 -2
- package/lib/options.js +36 -7
- package/lib/sidebars/index.d.ts +1 -1
- package/lib/sidebars/index.js +7 -8
- package/lib/sidebars/utils.d.ts +2 -0
- package/lib/sidebars/utils.js +31 -0
- package/lib/types.d.ts +22 -9
- package/package.json +2 -2
- package/src/index.ts +226 -16
- package/src/markdown/createParamsDetails.ts +2 -0
- package/src/markdown/createSchemaDetails.ts +2 -0
- package/src/markdown/index.ts +1 -1
- package/src/options.ts +41 -8
- package/src/sidebars/index.ts +11 -8
- package/src/sidebars/utils.ts +29 -0
- package/src/types.ts +22 -8
package/README.md
CHANGED
|
@@ -20,6 +20,13 @@ OpenAPI plugin for generating API reference docs in Docusaurus v2.
|
|
|
20
20
|
|
|
21
21
|
The `docusaurus-plugin-openapi-docs` package extends the Docusaurus CLI with commands for generating MDX using the OpenAPI specification as the source. The resulting MDX is fully compatible with [plugin-content-docs](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-docs) and can be used to render beautiful reference API docs by setting `docItemComponent` to `@theme/ApiItem`, a custom component included in the `docusaurus-theme-openapi-docs` theme.
|
|
22
22
|
|
|
23
|
+
Key Features:
|
|
24
|
+
|
|
25
|
+
- **Compatible:** Works with Swagger 2.0 and OpenAPI 3.x.
|
|
26
|
+
- **Fast:** Convert large OpenAPI specs into MDX docs in seconds. 🔥
|
|
27
|
+
- **Stylish:** Based on the same [Infima styling framework](https://infima.dev/) that powers the Docusaurus UI.
|
|
28
|
+
- **Capable:** Supports single, multi and _even micro_ OpenAPI specs.
|
|
29
|
+
|
|
23
30
|
## Installation
|
|
24
31
|
|
|
25
32
|
Plugin:
|
|
@@ -71,7 +78,6 @@ Here is an example of properly configuring your `docusaurus.config.js` file for
|
|
|
71
78
|
],
|
|
72
79
|
|
|
73
80
|
plugins: [
|
|
74
|
-
[
|
|
75
81
|
'docusaurus-plugin-openapi-docs',
|
|
76
82
|
{
|
|
77
83
|
id: "apiDocs",
|
|
@@ -97,28 +103,53 @@ Here is an example of properly configuring your `docusaurus.config.js` file for
|
|
|
97
103
|
|
|
98
104
|
> Note: You may optionally configure a dedicated `@docusaurus/plugin-content-docs` instance for use with `docusaurus-theme-openapi-docs` by setting `docItemComponent` to `@theme/ApiItem`.
|
|
99
105
|
|
|
100
|
-
|
|
106
|
+
## Plugin Configuration Options
|
|
107
|
+
|
|
108
|
+
The `docusaurus-plugin-openapi-docs` plugin can be configured with the following options:
|
|
109
|
+
|
|
110
|
+
| Name | Type | Default | Description |
|
|
111
|
+
| ------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| `id` | `string` | `null` | A unique document id. |
|
|
113
|
+
| `docPluginId` | `string` | `null` | The ID associated with the `plugin-content-docs` or `preset` instance used to render the OpenAPI docs (e.g. "your-plugin-id", "classic", "default"). |
|
|
114
|
+
|
|
115
|
+
### config
|
|
101
116
|
|
|
102
|
-
`
|
|
117
|
+
`config` can be configured with the following options:
|
|
103
118
|
|
|
104
|
-
| Name | Type | Default | Description
|
|
105
|
-
| ---------------- | -------- | ------- |
|
|
106
|
-
| `specPath` | `string` | `null` | Designated path to the source of an OpenAPI specification file or directory of multiple OpenAPI specification files. |
|
|
107
|
-
| `ouputDir` | `string` | `null` | Desired output path for generated MDX files.
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
119
|
+
| Name | Type | Default | Description |
|
|
120
|
+
| ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `specPath` | `string` | `null` | Designated URL or path to the source of an OpenAPI specification file or directory of multiple OpenAPI specification files. |
|
|
122
|
+
| `ouputDir` | `string` | `null` | Desired output path for generated MDX files. |
|
|
123
|
+
| `ouputDir` | `string` | `null` | Desired output path for generated MDX files. |
|
|
124
|
+
| `template` | `string` | `null` | _Optional:_ Customize MDX content with a desired template. |
|
|
125
|
+
| `sidebarOptions` | `object` | `null` | _Optional:_ Set of options for sidebar configuration. See below for a list of supported options. |
|
|
126
|
+
| `version` | `string` | `null` | _Optional:_ Version assigned to single or micro-spec API specified in `specPath`. |
|
|
127
|
+
| `label` | `string` | `null` | _Optional:_ Version label used when generating version selector dropdown menu. |
|
|
128
|
+
| `baseUrl` | `string` | `null` | _Optional:_ Version base URL used when generating version selector dropdown menu. |
|
|
129
|
+
| `versions` | `object` | `null` | _Optional:_ Set of options for versioning configuration. See below for a list of supported options. |
|
|
110
130
|
|
|
111
131
|
`sidebarOptions` can be configured with the following options:
|
|
112
132
|
|
|
113
133
|
| Name | Type | Default | Description |
|
|
114
134
|
| -------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
115
135
|
| `groupPathsBy` | `string` | `null` | Organize and group sidebar slice by specified option. Note: Currently, `groupPathsBy` only contains support for grouping by `tag`. |
|
|
116
|
-
| `categoryLinkSource` | `string` | `null` | Defines what source to use for rendering category link pages when grouping paths by tag. <br
|
|
136
|
+
| `categoryLinkSource` | `string` | `null` | Defines what source to use for rendering category link pages when grouping paths by tag. <br/><br/>The supported options are as follows: <br/><br/> `tag`: Sets the category link config type to `generated-index` and uses the tag description as the link config description. <br/><br/>`info`: Sets the category link config type to `doc` and renders the `info` section as the category link (recommended only for multi/micro-spec scenarios). |
|
|
117
137
|
| `sidebarCollapsible` | `boolean` | `true` | Whether sidebar categories are collapsible by default. |
|
|
118
138
|
| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. |
|
|
119
139
|
| `customProps` | `object` | `null` | Additional props for customizing a sidebar item. |
|
|
120
140
|
|
|
121
|
-
>
|
|
141
|
+
> You may optionally configure a `sidebarOptions`. In doing so, an individual `sidebar.js` slice with the configured options will be generated within the respective `outputDir`.
|
|
142
|
+
|
|
143
|
+
`versions` can be configured with the following options:
|
|
144
|
+
|
|
145
|
+
| Name | Type | Default | Description |
|
|
146
|
+
| ---------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
147
|
+
| `specPath` | `string` | `null` | Designated URL or path to the source of an OpenAPI specification file or directory of micro OpenAPI specification files. |
|
|
148
|
+
| `ouputDir` | `string` | `null` | Desired output path for versioned, generated MDX files. |
|
|
149
|
+
| `label` | `string` | `null` | _Optional:_ Version label used when generating version selector dropdown menu. |
|
|
150
|
+
| `baseUrl` | `string` | `null` | _Optional:_ Version base URL used when generating version selector dropdown menu. |
|
|
151
|
+
|
|
152
|
+
> All versions will automatically inherit `sidebarOptions` from the parent/base config.
|
|
122
153
|
|
|
123
154
|
## CLI Usage
|
|
124
155
|
|
|
@@ -139,9 +170,11 @@ Commands:
|
|
|
139
170
|
write-translations [options] [siteDir] Extract required translations of your site.
|
|
140
171
|
write-heading-ids [options] [siteDir] [files...] Generate heading ids in Markdown content.
|
|
141
172
|
docs:version <version> Tag a new docs version
|
|
142
|
-
gen-api-docs <id> Generates
|
|
143
|
-
|
|
144
|
-
docs
|
|
173
|
+
gen-api-docs <id> Generates OpenAPI docs in MDX file format and sidebar.js (if enabled).
|
|
174
|
+
gen-api-docs:version <id:version> Generates versioned OpenAPI docs in MDX file format, versions.js and sidebar.js (if enabled).
|
|
175
|
+
clean-api-docs <id> Clears the generated OpenAPI docs MDX files and sidebar.js (if enabled).
|
|
176
|
+
clean-api-docs:version <id:version> Clears the versioned, generated OpenAPI docs MDX files, versions.json and sidebar.js (if
|
|
177
|
+
enabled).
|
|
145
178
|
```
|
|
146
179
|
|
|
147
180
|
### Generating OpenAPI Docs
|
|
@@ -190,6 +223,61 @@ yarn docusaurus clean-api-docs burgers
|
|
|
190
223
|
|
|
191
224
|
> The example above will remove all API docs relative to `burgers`.
|
|
192
225
|
|
|
226
|
+
### Versioning OpenAPI docs
|
|
227
|
+
|
|
228
|
+
To generate _all_ versioned OpenAPI docs, run the following command from the root directory of your project:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
yarn docusaurus gen-api-docs:version <id>:all
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
yarn docusaurus gen-api-docs:version petstore:all
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> This will generate API docs for all of the OpenAPI specification (OAS) files referenced in your `versions` config and will also generate a `versions.json` file.
|
|
241
|
+
|
|
242
|
+
> Substitue `all` with a specific version ID to generate/clean a specific version. Generating for `all` or a specific version ID will automatically update the `versions.json` file.
|
|
243
|
+
|
|
244
|
+
## Installing from Template
|
|
245
|
+
|
|
246
|
+
Run the following to bootstrap a Docsaurus v2 site (classic theme) with `docusaurus-openapi-docs`:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
npx create-docusaurus@2.0.0-beta.21 my-website --package-manager yarn
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
> When prompted to select a template choose `Git repository`.
|
|
253
|
+
|
|
254
|
+
Template Repository URL:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
https://github.com/PaloAltoNetworks/docusaurus-template-openapi-docs.git
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
> When asked how the template repo should be cloned choose "copy" (unless you know better).
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
cd my-website
|
|
264
|
+
yarn
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Developer Quick Start
|
|
268
|
+
|
|
269
|
+
> Looking to make a contribution? Make sure to checkout out our contributing guide.
|
|
270
|
+
|
|
271
|
+
After [forking](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/fork) the main repository, run the following:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
git clone https://github.com/<your account>/docusaurus-openapi-docs.git
|
|
275
|
+
cd docusaurus-openapi-docs
|
|
276
|
+
yarn
|
|
277
|
+
yarn build-packages
|
|
278
|
+
yarn watch:demo
|
|
279
|
+
```
|
|
280
|
+
|
|
193
281
|
## Support
|
|
194
282
|
|
|
195
|
-
Please read [SUPPORT.md](SUPPORT.md) for details on how to get support for this project.
|
|
283
|
+
Please read [SUPPORT.md](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/blob/main/SUPPORT.md) for details on how to get support for this project.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { LoadContext, Plugin } from "@docusaurus/types";
|
|
2
2
|
import type { PluginOptions, LoadedContent } from "./types";
|
|
3
3
|
export declare function isURL(str: string): boolean;
|
|
4
|
-
export
|
|
4
|
+
export declare function getDocsData(dataArray: any[], filter: string): Object | undefined;
|
|
5
|
+
declare function pluginOpenAPIDocs(context: LoadContext, options: PluginOptions): Plugin<LoadedContent>;
|
|
6
|
+
declare namespace pluginOpenAPIDocs {
|
|
7
|
+
var validateOptions: ({ options, validate }: any) => any;
|
|
8
|
+
}
|
|
9
|
+
export default pluginOpenAPIDocs;
|
package/lib/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.isURL = void 0;
|
|
12
|
+
exports.getDocsData = exports.isURL = void 0;
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const utils_1 = require("@docusaurus/utils");
|
|
@@ -17,14 +17,52 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
17
17
|
const mustache_1 = require("mustache");
|
|
18
18
|
const markdown_1 = require("./markdown");
|
|
19
19
|
const openapi_1 = require("./openapi");
|
|
20
|
+
const options_1 = require("./options");
|
|
20
21
|
const sidebars_1 = __importDefault(require("./sidebars"));
|
|
21
22
|
function isURL(str) {
|
|
22
23
|
return /^(https?:)\/\//m.test(str);
|
|
23
24
|
}
|
|
24
25
|
exports.isURL = isURL;
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
function getDocsData(dataArray, filter) {
|
|
27
|
+
// eslint-disable-next-line array-callback-return
|
|
28
|
+
const filteredData = dataArray.filter((data) => {
|
|
29
|
+
if (data[0] === filter) {
|
|
30
|
+
return data[1];
|
|
31
|
+
}
|
|
32
|
+
// Search plugin-content-docs instances
|
|
33
|
+
if (data[0] === "@docusaurus/plugin-content-docs") {
|
|
34
|
+
const pluginId = data[1].id ? data[1].id : "default";
|
|
35
|
+
if (pluginId === filter) {
|
|
36
|
+
return data[1];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})[0];
|
|
40
|
+
if (filteredData) {
|
|
41
|
+
// Search presets, e.g. "classic"
|
|
42
|
+
if (filteredData[0] === filter) {
|
|
43
|
+
return filteredData[1].docs;
|
|
44
|
+
}
|
|
45
|
+
// Search plugin-content-docs instances
|
|
46
|
+
if (filteredData[0] === "@docusaurus/plugin-content-docs") {
|
|
47
|
+
const pluginId = filteredData[1].id ? filteredData[1].id : "default";
|
|
48
|
+
if (pluginId === filter) {
|
|
49
|
+
return filteredData[1];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
exports.getDocsData = getDocsData;
|
|
56
|
+
function pluginOpenAPIDocs(context, options) {
|
|
57
|
+
const { config, docPluginId } = options;
|
|
58
|
+
const { siteDir, siteConfig } = context;
|
|
59
|
+
// Get routeBasePath and path from plugin-content-docs or preset
|
|
60
|
+
const presets = siteConfig.presets;
|
|
61
|
+
const plugins = siteConfig.plugins;
|
|
62
|
+
const presetsPlugins = presets.concat(plugins);
|
|
63
|
+
const docData = getDocsData(presetsPlugins, docPluginId);
|
|
64
|
+
const docRouteBasePath = docData ? docData.routeBasePath : undefined;
|
|
65
|
+
const docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
|
|
28
66
|
async function generateApiDocs(options) {
|
|
29
67
|
let { specPath, outputDir, template, sidebarOptions } = options;
|
|
30
68
|
const contentPath = isURL(specPath)
|
|
@@ -44,8 +82,7 @@ function pluginOpenAPI(context, options) {
|
|
|
44
82
|
}
|
|
45
83
|
// TODO: figure out better way to set default
|
|
46
84
|
if (Object.keys(sidebarOptions !== null && sidebarOptions !== void 0 ? sidebarOptions : {}).length > 0) {
|
|
47
|
-
const sidebarSlice = (0, sidebars_1.default)(sidebarOptions,
|
|
48
|
-
options, loadedApi, tags);
|
|
85
|
+
const sidebarSlice = (0, sidebars_1.default)(sidebarOptions, options, loadedApi, tags, docPath);
|
|
49
86
|
const sidebarSliceTemplate = template
|
|
50
87
|
? fs_1.default.readFileSync(template).toString()
|
|
51
88
|
: `module.exports = {{{slice}}};`;
|
|
@@ -135,8 +172,14 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
135
172
|
item.markdown = markdown;
|
|
136
173
|
if (item.type === "api") {
|
|
137
174
|
item.json = JSON.stringify(item.api);
|
|
175
|
+
let infoBasePath = `${outputDir}/${item.infoId}`;
|
|
176
|
+
if (docRouteBasePath) {
|
|
177
|
+
infoBasePath = `${docRouteBasePath}/${outputDir
|
|
178
|
+
.split(docPath)[1]
|
|
179
|
+
.replace(/^\/+/g, "")}/${item.infoId}`.replace(/^\/+/g, "");
|
|
180
|
+
}
|
|
138
181
|
if (item.infoId)
|
|
139
|
-
item.infoPath =
|
|
182
|
+
item.infoPath = infoBasePath;
|
|
140
183
|
}
|
|
141
184
|
const view = (0, mustache_1.render)(mdTemplate, item);
|
|
142
185
|
const utils = (0, mustache_1.render)(infoMdTemplate, item);
|
|
@@ -192,9 +235,11 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
192
235
|
const apiDir = path_1.default.join(siteDir, outputDir);
|
|
193
236
|
const apiMdxFiles = await (0, utils_1.Globby)(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
|
|
194
237
|
cwd: path_1.default.resolve(apiDir),
|
|
238
|
+
deep: 1,
|
|
195
239
|
});
|
|
196
240
|
const sidebarFile = await (0, utils_1.Globby)(["sidebar.js"], {
|
|
197
241
|
cwd: path_1.default.resolve(apiDir),
|
|
242
|
+
deep: 1,
|
|
198
243
|
});
|
|
199
244
|
apiMdxFiles.map((mdx) => fs_1.default.unlink(`${apiDir}/${mdx}`, (err) => {
|
|
200
245
|
if (err) {
|
|
@@ -213,18 +258,48 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
213
258
|
}
|
|
214
259
|
}));
|
|
215
260
|
}
|
|
261
|
+
async function generateVersions(versions, outputDir) {
|
|
262
|
+
let versionsArray = [];
|
|
263
|
+
for (const [version, metadata] of Object.entries(versions)) {
|
|
264
|
+
versionsArray.push({
|
|
265
|
+
version: version,
|
|
266
|
+
label: metadata.label,
|
|
267
|
+
baseUrl: metadata.baseUrl,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const versionsJson = JSON.stringify(versionsArray, null, 2);
|
|
271
|
+
try {
|
|
272
|
+
fs_1.default.writeFileSync(`${outputDir}/versions.json`, versionsJson, "utf8");
|
|
273
|
+
console.log(chalk_1.default.green(`Successfully created "${outputDir}/versions.json"`));
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
console.error(chalk_1.default.red(`Failed to write "${outputDir}/versions.json"`), chalk_1.default.yellow(err));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function cleanVersions(outputDir) {
|
|
280
|
+
if (fs_1.default.existsSync(`${outputDir}/versions.json`)) {
|
|
281
|
+
fs_1.default.unlink(`${outputDir}/versions.json`, (err) => {
|
|
282
|
+
if (err) {
|
|
283
|
+
console.error(chalk_1.default.red(`Cleanup failed for "${outputDir}/versions.json"`), chalk_1.default.yellow(err));
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
console.log(chalk_1.default.green(`Cleanup succeeded for "${outputDir}/versions.json"`));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
216
291
|
return {
|
|
217
|
-
name: `docusaurus-plugin-openapi`,
|
|
292
|
+
name: `docusaurus-plugin-openapi-docs`,
|
|
218
293
|
extendCli(cli) {
|
|
219
294
|
cli
|
|
220
295
|
.command(`gen-api-docs`)
|
|
221
|
-
.description(`Generates
|
|
222
|
-
.usage("
|
|
296
|
+
.description(`Generates OpenAPI docs in MDX file format and sidebar.js (if enabled).`)
|
|
297
|
+
.usage("<id>")
|
|
223
298
|
.arguments("<id>")
|
|
224
299
|
.action(async (id) => {
|
|
225
300
|
if (id === "all") {
|
|
226
301
|
if (config[id]) {
|
|
227
|
-
console.error(chalk_1.default.red("Can't use id 'all' for
|
|
302
|
+
console.error(chalk_1.default.red("Can't use id 'all' for OpenAPI docs configuration key."));
|
|
228
303
|
}
|
|
229
304
|
else {
|
|
230
305
|
Object.keys(config).forEach(async function (key) {
|
|
@@ -233,21 +308,71 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
233
308
|
}
|
|
234
309
|
}
|
|
235
310
|
else if (!config[id]) {
|
|
236
|
-
console.error(chalk_1.default.red(`ID ${id} does not exist in
|
|
311
|
+
console.error(chalk_1.default.red(`ID '${id}' does not exist in OpenAPI docs config.`));
|
|
237
312
|
}
|
|
238
313
|
else {
|
|
239
314
|
await generateApiDocs(config[id]);
|
|
240
315
|
}
|
|
241
316
|
});
|
|
317
|
+
cli
|
|
318
|
+
.command(`gen-api-docs:version`)
|
|
319
|
+
.description(`Generates versioned OpenAPI docs in MDX file format, versions.js and sidebar.js (if enabled).`)
|
|
320
|
+
.usage("<id:version>")
|
|
321
|
+
.arguments("<id:version>")
|
|
322
|
+
.action(async (id) => {
|
|
323
|
+
const [parentId, versionId] = id.split(":");
|
|
324
|
+
const parentConfig = Object.assign({}, config[parentId]);
|
|
325
|
+
const version = parentConfig.version;
|
|
326
|
+
const label = parentConfig.label;
|
|
327
|
+
const baseUrl = parentConfig.baseUrl;
|
|
328
|
+
let parentVersion = {};
|
|
329
|
+
parentVersion[version] = { label: label, baseUrl: baseUrl };
|
|
330
|
+
const { versions } = config[parentId];
|
|
331
|
+
const mergedVersions = Object.assign(parentVersion, versions);
|
|
332
|
+
// Prepare for merge
|
|
333
|
+
delete parentConfig.versions;
|
|
334
|
+
delete parentConfig.version;
|
|
335
|
+
delete parentConfig.label;
|
|
336
|
+
delete parentConfig.baseUrl;
|
|
337
|
+
// TODO: handle when no versions are defined by version command is passed
|
|
338
|
+
if (versionId === "all") {
|
|
339
|
+
if (versions[id]) {
|
|
340
|
+
console.error(chalk_1.default.red("Can't use id 'all' for OpenAPI docs versions configuration key."));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
await generateVersions(mergedVersions, parentConfig.outputDir);
|
|
344
|
+
Object.keys(versions).forEach(async (key) => {
|
|
345
|
+
const versionConfig = versions[key];
|
|
346
|
+
const mergedConfig = {
|
|
347
|
+
...parentConfig,
|
|
348
|
+
...versionConfig,
|
|
349
|
+
};
|
|
350
|
+
await generateApiDocs(mergedConfig);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else if (!versions[versionId]) {
|
|
355
|
+
console.error(chalk_1.default.red(`Version ID '${versionId}' does not exist in OpenAPI docs versions config.`));
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
const versionConfig = versions[versionId];
|
|
359
|
+
const mergedConfig = {
|
|
360
|
+
...parentConfig,
|
|
361
|
+
...versionConfig,
|
|
362
|
+
};
|
|
363
|
+
await generateVersions(mergedVersions, parentConfig.outputDir);
|
|
364
|
+
await generateApiDocs(mergedConfig);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
242
367
|
cli
|
|
243
368
|
.command(`clean-api-docs`)
|
|
244
|
-
.description(`Clears the
|
|
245
|
-
.usage("
|
|
369
|
+
.description(`Clears the generated OpenAPI docs MDX files and sidebar.js (if enabled).`)
|
|
370
|
+
.usage("<id>")
|
|
246
371
|
.arguments("<id>")
|
|
247
372
|
.action(async (id) => {
|
|
248
373
|
if (id === "all") {
|
|
249
374
|
if (config[id]) {
|
|
250
|
-
console.error(chalk_1.default.red("Can't use id 'all' for
|
|
375
|
+
console.error(chalk_1.default.red("Can't use id 'all' for OpenAPI docs configuration key."));
|
|
251
376
|
}
|
|
252
377
|
else {
|
|
253
378
|
Object.keys(config).forEach(async function (key) {
|
|
@@ -259,7 +384,46 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
259
384
|
await cleanApiDocs(config[id]);
|
|
260
385
|
}
|
|
261
386
|
});
|
|
387
|
+
cli
|
|
388
|
+
.command(`clean-api-docs:version`)
|
|
389
|
+
.description(`Clears the versioned, generated OpenAPI docs MDX files, versions.json and sidebar.js (if enabled).`)
|
|
390
|
+
.usage("<id:version>")
|
|
391
|
+
.arguments("<id:version>")
|
|
392
|
+
.action(async (id) => {
|
|
393
|
+
const [parentId, versionId] = id.split(":");
|
|
394
|
+
const { versions } = config[parentId];
|
|
395
|
+
const parentConfig = Object.assign({}, config[parentId]);
|
|
396
|
+
delete parentConfig.versions;
|
|
397
|
+
if (versionId === "all") {
|
|
398
|
+
if (versions[id]) {
|
|
399
|
+
chalk_1.default.red("Can't use id 'all' for OpenAPI docs versions configuration key.");
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
await cleanVersions(parentConfig.outputDir);
|
|
403
|
+
Object.keys(versions).forEach(async (key) => {
|
|
404
|
+
const versionConfig = versions[key];
|
|
405
|
+
const mergedConfig = {
|
|
406
|
+
...parentConfig,
|
|
407
|
+
...versionConfig,
|
|
408
|
+
};
|
|
409
|
+
await cleanApiDocs(mergedConfig);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
const versionConfig = versions[versionId];
|
|
415
|
+
const mergedConfig = {
|
|
416
|
+
...parentConfig,
|
|
417
|
+
...versionConfig,
|
|
418
|
+
};
|
|
419
|
+
await cleanApiDocs(mergedConfig);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
262
422
|
},
|
|
263
423
|
};
|
|
264
424
|
}
|
|
265
|
-
exports.default =
|
|
425
|
+
exports.default = pluginOpenAPIDocs;
|
|
426
|
+
pluginOpenAPIDocs.validateOptions = ({ options, validate }) => {
|
|
427
|
+
const validatedOptions = validate(options_1.OptionsSchema, options);
|
|
428
|
+
return validatedOptions;
|
|
429
|
+
};
|
|
@@ -19,6 +19,8 @@ function createParamsDetails({ parameters, type }) {
|
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
21
21
|
return (0, createDetails_1.createDetails)({
|
|
22
|
+
"data-collapsed": false,
|
|
23
|
+
open: true,
|
|
22
24
|
style: { marginBottom: "1rem" },
|
|
23
25
|
children: [
|
|
24
26
|
(0, createDetailsSummary_1.createDetailsSummary)({
|
package/lib/markdown/index.js
CHANGED
|
@@ -40,7 +40,7 @@ exports.createApiPageMD = createApiPageMD;
|
|
|
40
40
|
function createInfoPageMD({ info: { title, version, description, contact, license, termsOfService }, securitySchemes, }) {
|
|
41
41
|
return (0, utils_1.render)([
|
|
42
42
|
`import Tabs from "@theme/Tabs";\n`,
|
|
43
|
-
`import TabItem from "@theme/TabItem";\n`,
|
|
43
|
+
`import TabItem from "@theme/TabItem";\n\n`,
|
|
44
44
|
(0, createVersionBadge_1.createVersionBadge)(version),
|
|
45
45
|
`# ${(0, lodash_1.escape)(title)}\n\n`,
|
|
46
46
|
(0, createDescription_1.createDescription)(description),
|
package/lib/options.d.ts
CHANGED
package/lib/options.js
CHANGED
|
@@ -6,13 +6,42 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
* ========================================================================== */
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.OptionsSchema =
|
|
9
|
+
exports.OptionsSchema = void 0;
|
|
10
10
|
const utils_validation_1 = require("@docusaurus/utils-validation");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const sidebarOptions = utils_validation_1.Joi.object({
|
|
12
|
+
groupPathsBy: utils_validation_1.Joi.string().valid("tag"),
|
|
13
|
+
categoryLinkSource: utils_validation_1.Joi.string().valid("tag", "info"),
|
|
14
|
+
customProps: utils_validation_1.Joi.object(),
|
|
15
|
+
sidebarCollapsible: utils_validation_1.Joi.boolean(),
|
|
16
|
+
sidebarCollapsed: utils_validation_1.Joi.boolean(),
|
|
17
|
+
});
|
|
15
18
|
exports.OptionsSchema = utils_validation_1.Joi.object({
|
|
16
|
-
id: utils_validation_1.Joi.string().
|
|
17
|
-
|
|
19
|
+
id: utils_validation_1.Joi.string().required(),
|
|
20
|
+
docPluginId: utils_validation_1.Joi.string().required(),
|
|
21
|
+
config: utils_validation_1.Joi.object()
|
|
22
|
+
.pattern(/^/, utils_validation_1.Joi.object({
|
|
23
|
+
specPath: utils_validation_1.Joi.string().required(),
|
|
24
|
+
outputDir: utils_validation_1.Joi.string().required(),
|
|
25
|
+
template: utils_validation_1.Joi.string(),
|
|
26
|
+
sidebarOptions: sidebarOptions,
|
|
27
|
+
version: utils_validation_1.Joi.string().when("versions", {
|
|
28
|
+
is: utils_validation_1.Joi.exist(),
|
|
29
|
+
then: utils_validation_1.Joi.required(),
|
|
30
|
+
}),
|
|
31
|
+
label: utils_validation_1.Joi.string().when("versions", {
|
|
32
|
+
is: utils_validation_1.Joi.exist(),
|
|
33
|
+
then: utils_validation_1.Joi.required(),
|
|
34
|
+
}),
|
|
35
|
+
baseUrl: utils_validation_1.Joi.string().when("versions", {
|
|
36
|
+
is: utils_validation_1.Joi.exist(),
|
|
37
|
+
then: utils_validation_1.Joi.required(),
|
|
38
|
+
}),
|
|
39
|
+
versions: utils_validation_1.Joi.object().pattern(/^/, utils_validation_1.Joi.object({
|
|
40
|
+
specPath: utils_validation_1.Joi.string().required(),
|
|
41
|
+
outputDir: utils_validation_1.Joi.string().required(),
|
|
42
|
+
label: utils_validation_1.Joi.string().required(),
|
|
43
|
+
baseUrl: utils_validation_1.Joi.string().required(),
|
|
44
|
+
})),
|
|
45
|
+
}))
|
|
46
|
+
.required(),
|
|
18
47
|
});
|
package/lib/sidebars/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ProcessedSidebar } from "@docusaurus/plugin-content-docs/src/sidebars/types";
|
|
2
2
|
import { TagObject } from "../openapi/types";
|
|
3
3
|
import type { SidebarOptions, APIOptions, ApiMetadata } from "../types";
|
|
4
|
-
export default function generateSidebarSlice(sidebarOptions: SidebarOptions, options: APIOptions, api: ApiMetadata[], tags: TagObject[]): ProcessedSidebar;
|
|
4
|
+
export default function generateSidebarSlice(sidebarOptions: SidebarOptions, options: APIOptions, api: ApiMetadata[], tags: TagObject[], docPath: string): ProcessedSidebar;
|
package/lib/sidebars/index.js
CHANGED
|
@@ -18,7 +18,7 @@ function isApiItem(item) {
|
|
|
18
18
|
function isInfoItem(item) {
|
|
19
19
|
return item.type === "info";
|
|
20
20
|
}
|
|
21
|
-
function groupByTags(items, sidebarOptions, options, tags) {
|
|
21
|
+
function groupByTags(items, sidebarOptions, options, tags, docPath) {
|
|
22
22
|
const { outputDir } = options;
|
|
23
23
|
const { sidebarCollapsed, sidebarCollapsible, customProps, categoryLinkSource, } = sidebarOptions;
|
|
24
24
|
const apiItems = items.filter(isApiItem);
|
|
@@ -35,10 +35,9 @@ function groupByTags(items, sidebarOptions, options, tags) {
|
|
|
35
35
|
const apiTags = (0, uniq_1.default)(apiItems
|
|
36
36
|
.flatMap((item) => item.api.tags)
|
|
37
37
|
.filter((item) => !!item));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.slice(outputDir.indexOf("/", 1))
|
|
41
|
-
.replace(/^\/+/g, "");
|
|
38
|
+
const basePath = docPath
|
|
39
|
+
? outputDir.split(docPath)[1].replace(/^\/+/g, "")
|
|
40
|
+
: outputDir.slice(outputDir.indexOf("/", 1)).replace(/^\/+/g, "");
|
|
42
41
|
function createDocItem(item) {
|
|
43
42
|
var _a, _b;
|
|
44
43
|
const sidebar_label = item.frontMatter.sidebar_label;
|
|
@@ -46,7 +45,7 @@ function groupByTags(items, sidebarOptions, options, tags) {
|
|
|
46
45
|
const id = item.id;
|
|
47
46
|
return {
|
|
48
47
|
type: "doc",
|
|
49
|
-
id: `${basePath}/${item.id}`,
|
|
48
|
+
id: basePath === "" || undefined ? `${item.id}` : `${basePath}/${item.id}`,
|
|
50
49
|
label: (_b = (_a = sidebar_label) !== null && _a !== void 0 ? _a : title) !== null && _b !== void 0 ? _b : id,
|
|
51
50
|
customProps: customProps,
|
|
52
51
|
className: (0, clsx_1.default)({
|
|
@@ -136,10 +135,10 @@ function groupByTags(items, sidebarOptions, options, tags) {
|
|
|
136
135
|
}
|
|
137
136
|
return [...tagged, ...untagged];
|
|
138
137
|
}
|
|
139
|
-
function generateSidebarSlice(sidebarOptions, options, api, tags) {
|
|
138
|
+
function generateSidebarSlice(sidebarOptions, options, api, tags, docPath) {
|
|
140
139
|
let sidebarSlice = [];
|
|
141
140
|
if (sidebarOptions.groupPathsBy === "tag") {
|
|
142
|
-
sidebarSlice = groupByTags(api, sidebarOptions, options, tags);
|
|
141
|
+
sidebarSlice = groupByTags(api, sidebarOptions, options, tags, docPath);
|
|
143
142
|
}
|
|
144
143
|
return sidebarSlice;
|
|
145
144
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* ============================================================================
|
|
3
|
+
* Copyright (c) Palo Alto Networks
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
* ========================================================================== */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.versionCrumb = exports.versionSelector = void 0;
|
|
10
|
+
const mustache_1 = require("mustache");
|
|
11
|
+
function versionSelector(versions) {
|
|
12
|
+
const template = `<div class="dropdown dropdown--hoverable dropdown--right">
|
|
13
|
+
<button class="button button--block button--sm button--secondary"><span>Select API Version</span></button>
|
|
14
|
+
<ul class="dropdown__menu">
|
|
15
|
+
{{#.}}<li><a class="dropdown__link" href="{{{baseUrl}}}">{{{label}}}</a></li>{{/.}}
|
|
16
|
+
</ul>
|
|
17
|
+
</div>
|
|
18
|
+
`;
|
|
19
|
+
const view = (0, mustache_1.render)(template, versions);
|
|
20
|
+
return view;
|
|
21
|
+
}
|
|
22
|
+
exports.versionSelector = versionSelector;
|
|
23
|
+
function versionCrumb(version) {
|
|
24
|
+
const template = `<ul style="display: flex;" class="breadcrumbs breadcrumbs--sm">
|
|
25
|
+
<li style="margin-left: auto; margin-right: 0;" class="breadcrumbs__item breadcrumbs__item--active">
|
|
26
|
+
<a class="breadcrumbs__link"><span>{{{.}}}</span></a></li></ul>
|
|
27
|
+
`;
|
|
28
|
+
const view = (0, mustache_1.render)(template, version);
|
|
29
|
+
return view;
|
|
30
|
+
}
|
|
31
|
+
exports.versionCrumb = versionCrumb;
|
package/lib/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { InfoObject, OperationObject, SecuritySchemeObject, TagObject } from "./
|
|
|
3
3
|
export type { PropSidebarItemCategory, SidebarItemLink, PropSidebar, PropSidebarItem, } from "@docusaurus/plugin-content-docs-types";
|
|
4
4
|
export interface PluginOptions {
|
|
5
5
|
id?: string;
|
|
6
|
+
docPluginId: string;
|
|
6
7
|
config: {
|
|
7
8
|
[key: string]: APIOptions;
|
|
8
9
|
};
|
|
@@ -12,6 +13,27 @@ export interface APIOptions {
|
|
|
12
13
|
outputDir: string;
|
|
13
14
|
template?: string;
|
|
14
15
|
sidebarOptions?: SidebarOptions;
|
|
16
|
+
version?: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
versions?: {
|
|
20
|
+
[key: string]: APIVersionOptions;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface SidebarOptions {
|
|
24
|
+
groupPathsBy?: string;
|
|
25
|
+
categoryLinkSource?: string;
|
|
26
|
+
customProps?: {
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
sidebarCollapsible?: boolean;
|
|
30
|
+
sidebarCollapsed?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface APIVersionOptions {
|
|
33
|
+
specPath: string;
|
|
34
|
+
outputDir: string;
|
|
35
|
+
label: string;
|
|
36
|
+
baseUrl: string;
|
|
15
37
|
}
|
|
16
38
|
export interface LoadedContent {
|
|
17
39
|
loadedApi: ApiMetadata[];
|
|
@@ -68,12 +90,3 @@ export interface ApiNavLink {
|
|
|
68
90
|
title: string;
|
|
69
91
|
permalink: string;
|
|
70
92
|
}
|
|
71
|
-
export interface SidebarOptions {
|
|
72
|
-
groupPathsBy?: string;
|
|
73
|
-
categoryLinkSource?: string;
|
|
74
|
-
customProps?: {
|
|
75
|
-
[key: string]: unknown;
|
|
76
|
-
};
|
|
77
|
-
sidebarCollapsible?: boolean;
|
|
78
|
-
sidebarCollapsed?: boolean;
|
|
79
|
-
}
|
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": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"openapi",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=14"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "0a590e8aaaff087d13adb7b4be75aa0112b9a2b5"
|
|
66
66
|
}
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { render } from "mustache";
|
|
|
15
15
|
|
|
16
16
|
import { createApiPageMD, createInfoPageMD, createTagPageMD } from "./markdown";
|
|
17
17
|
import { readOpenapiFiles, processOpenapiFiles } from "./openapi";
|
|
18
|
+
import { OptionsSchema } from "./options";
|
|
18
19
|
import generateSidebarSlice from "./sidebars";
|
|
19
20
|
import type { PluginOptions, LoadedContent, APIOptions } from "./types";
|
|
20
21
|
|
|
@@ -22,12 +23,55 @@ export function isURL(str: string): boolean {
|
|
|
22
23
|
return /^(https?:)\/\//m.test(str);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
export
|
|
26
|
+
export function getDocsData(
|
|
27
|
+
dataArray: any[],
|
|
28
|
+
filter: string
|
|
29
|
+
): Object | undefined {
|
|
30
|
+
// eslint-disable-next-line array-callback-return
|
|
31
|
+
const filteredData = dataArray.filter((data) => {
|
|
32
|
+
if (data[0] === filter) {
|
|
33
|
+
return data[1];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Search plugin-content-docs instances
|
|
37
|
+
if (data[0] === "@docusaurus/plugin-content-docs") {
|
|
38
|
+
const pluginId = data[1].id ? data[1].id : "default";
|
|
39
|
+
if (pluginId === filter) {
|
|
40
|
+
return data[1];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})[0];
|
|
44
|
+
if (filteredData) {
|
|
45
|
+
// Search presets, e.g. "classic"
|
|
46
|
+
if (filteredData[0] === filter) {
|
|
47
|
+
return filteredData[1].docs;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Search plugin-content-docs instances
|
|
51
|
+
if (filteredData[0] === "@docusaurus/plugin-content-docs") {
|
|
52
|
+
const pluginId = filteredData[1].id ? filteredData[1].id : "default";
|
|
53
|
+
if (pluginId === filter) {
|
|
54
|
+
return filteredData[1];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default function pluginOpenAPIDocs(
|
|
26
62
|
context: LoadContext,
|
|
27
63
|
options: PluginOptions
|
|
28
64
|
): Plugin<LoadedContent> {
|
|
29
|
-
|
|
30
|
-
|
|
65
|
+
const { config, docPluginId } = options;
|
|
66
|
+
const { siteDir, siteConfig } = context;
|
|
67
|
+
|
|
68
|
+
// Get routeBasePath and path from plugin-content-docs or preset
|
|
69
|
+
const presets: any = siteConfig.presets;
|
|
70
|
+
const plugins: any = siteConfig.plugins;
|
|
71
|
+
const presetsPlugins = presets.concat(plugins);
|
|
72
|
+
const docData: any = getDocsData(presetsPlugins, docPluginId);
|
|
73
|
+
const docRouteBasePath = docData ? docData.routeBasePath : undefined;
|
|
74
|
+
const docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
|
|
31
75
|
|
|
32
76
|
async function generateApiDocs(options: APIOptions) {
|
|
33
77
|
let { specPath, outputDir, template, sidebarOptions } = options;
|
|
@@ -57,10 +101,11 @@ export default function pluginOpenAPI(
|
|
|
57
101
|
// TODO: figure out better way to set default
|
|
58
102
|
if (Object.keys(sidebarOptions ?? {}).length > 0) {
|
|
59
103
|
const sidebarSlice = generateSidebarSlice(
|
|
60
|
-
sidebarOptions!,
|
|
104
|
+
sidebarOptions!,
|
|
61
105
|
options,
|
|
62
106
|
loadedApi,
|
|
63
|
-
tags
|
|
107
|
+
tags,
|
|
108
|
+
docPath
|
|
64
109
|
);
|
|
65
110
|
|
|
66
111
|
const sidebarSliceTemplate = template
|
|
@@ -163,7 +208,13 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
163
208
|
item.markdown = markdown;
|
|
164
209
|
if (item.type === "api") {
|
|
165
210
|
item.json = JSON.stringify(item.api);
|
|
166
|
-
|
|
211
|
+
let infoBasePath = `${outputDir}/${item.infoId}`;
|
|
212
|
+
if (docRouteBasePath) {
|
|
213
|
+
infoBasePath = `${docRouteBasePath}/${outputDir
|
|
214
|
+
.split(docPath!)[1]
|
|
215
|
+
.replace(/^\/+/g, "")}/${item.infoId}`.replace(/^\/+/g, "");
|
|
216
|
+
}
|
|
217
|
+
if (item.infoId) item.infoPath = infoBasePath;
|
|
167
218
|
}
|
|
168
219
|
|
|
169
220
|
const view = render(mdTemplate, item);
|
|
@@ -254,9 +305,11 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
254
305
|
const apiDir = path.join(siteDir, outputDir);
|
|
255
306
|
const apiMdxFiles = await Globby(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
|
|
256
307
|
cwd: path.resolve(apiDir),
|
|
308
|
+
deep: 1,
|
|
257
309
|
});
|
|
258
310
|
const sidebarFile = await Globby(["sidebar.js"], {
|
|
259
311
|
cwd: path.resolve(apiDir),
|
|
312
|
+
deep: 1,
|
|
260
313
|
});
|
|
261
314
|
apiMdxFiles.map((mdx) =>
|
|
262
315
|
fs.unlink(`${apiDir}/${mdx}`, (err) => {
|
|
@@ -287,21 +340,66 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
287
340
|
);
|
|
288
341
|
}
|
|
289
342
|
|
|
343
|
+
async function generateVersions(versions: object, outputDir: string) {
|
|
344
|
+
let versionsArray = [] as object[];
|
|
345
|
+
for (const [version, metadata] of Object.entries(versions)) {
|
|
346
|
+
versionsArray.push({
|
|
347
|
+
version: version,
|
|
348
|
+
label: metadata.label,
|
|
349
|
+
baseUrl: metadata.baseUrl,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const versionsJson = JSON.stringify(versionsArray, null, 2);
|
|
354
|
+
try {
|
|
355
|
+
fs.writeFileSync(`${outputDir}/versions.json`, versionsJson, "utf8");
|
|
356
|
+
console.log(
|
|
357
|
+
chalk.green(`Successfully created "${outputDir}/versions.json"`)
|
|
358
|
+
);
|
|
359
|
+
} catch (err) {
|
|
360
|
+
console.error(
|
|
361
|
+
chalk.red(`Failed to write "${outputDir}/versions.json"`),
|
|
362
|
+
chalk.yellow(err)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function cleanVersions(outputDir: string) {
|
|
368
|
+
if (fs.existsSync(`${outputDir}/versions.json`)) {
|
|
369
|
+
fs.unlink(`${outputDir}/versions.json`, (err) => {
|
|
370
|
+
if (err) {
|
|
371
|
+
console.error(
|
|
372
|
+
chalk.red(`Cleanup failed for "${outputDir}/versions.json"`),
|
|
373
|
+
chalk.yellow(err)
|
|
374
|
+
);
|
|
375
|
+
} else {
|
|
376
|
+
console.log(
|
|
377
|
+
chalk.green(`Cleanup succeeded for "${outputDir}/versions.json"`)
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
290
384
|
return {
|
|
291
|
-
name: `docusaurus-plugin-openapi`,
|
|
385
|
+
name: `docusaurus-plugin-openapi-docs`,
|
|
292
386
|
|
|
293
387
|
extendCli(cli): void {
|
|
294
388
|
cli
|
|
295
389
|
.command(`gen-api-docs`)
|
|
296
|
-
.description(
|
|
297
|
-
|
|
298
|
-
"[options] <id key value in plugin config within docusaurus.config.js>"
|
|
390
|
+
.description(
|
|
391
|
+
`Generates OpenAPI docs in MDX file format and sidebar.js (if enabled).`
|
|
299
392
|
)
|
|
393
|
+
.usage("<id>")
|
|
300
394
|
.arguments("<id>")
|
|
301
395
|
.action(async (id) => {
|
|
302
396
|
if (id === "all") {
|
|
303
397
|
if (config[id]) {
|
|
304
|
-
console.error(
|
|
398
|
+
console.error(
|
|
399
|
+
chalk.red(
|
|
400
|
+
"Can't use id 'all' for OpenAPI docs configuration key."
|
|
401
|
+
)
|
|
402
|
+
);
|
|
305
403
|
} else {
|
|
306
404
|
Object.keys(config).forEach(async function (key) {
|
|
307
405
|
await generateApiDocs(config[key]);
|
|
@@ -309,24 +407,91 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
309
407
|
}
|
|
310
408
|
} else if (!config[id]) {
|
|
311
409
|
console.error(
|
|
312
|
-
chalk.red(`ID ${id} does not exist in
|
|
410
|
+
chalk.red(`ID '${id}' does not exist in OpenAPI docs config.`)
|
|
313
411
|
);
|
|
314
412
|
} else {
|
|
315
413
|
await generateApiDocs(config[id]);
|
|
316
414
|
}
|
|
317
415
|
});
|
|
318
416
|
|
|
417
|
+
cli
|
|
418
|
+
.command(`gen-api-docs:version`)
|
|
419
|
+
.description(
|
|
420
|
+
`Generates versioned OpenAPI docs in MDX file format, versions.js and sidebar.js (if enabled).`
|
|
421
|
+
)
|
|
422
|
+
.usage("<id:version>")
|
|
423
|
+
.arguments("<id:version>")
|
|
424
|
+
.action(async (id) => {
|
|
425
|
+
const [parentId, versionId] = id.split(":");
|
|
426
|
+
const parentConfig = Object.assign({}, config[parentId]);
|
|
427
|
+
|
|
428
|
+
const version = parentConfig.version as string;
|
|
429
|
+
const label = parentConfig.label as string;
|
|
430
|
+
const baseUrl = parentConfig.baseUrl as string;
|
|
431
|
+
|
|
432
|
+
let parentVersion = {} as any;
|
|
433
|
+
parentVersion[version] = { label: label, baseUrl: baseUrl };
|
|
434
|
+
|
|
435
|
+
const { versions } = config[parentId] as any;
|
|
436
|
+
const mergedVersions = Object.assign(parentVersion, versions);
|
|
437
|
+
|
|
438
|
+
// Prepare for merge
|
|
439
|
+
delete parentConfig.versions;
|
|
440
|
+
delete parentConfig.version;
|
|
441
|
+
delete parentConfig.label;
|
|
442
|
+
delete parentConfig.baseUrl;
|
|
443
|
+
|
|
444
|
+
// TODO: handle when no versions are defined by version command is passed
|
|
445
|
+
if (versionId === "all") {
|
|
446
|
+
if (versions[id]) {
|
|
447
|
+
console.error(
|
|
448
|
+
chalk.red(
|
|
449
|
+
"Can't use id 'all' for OpenAPI docs versions configuration key."
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
} else {
|
|
453
|
+
await generateVersions(mergedVersions, parentConfig.outputDir);
|
|
454
|
+
Object.keys(versions).forEach(async (key) => {
|
|
455
|
+
const versionConfig = versions[key];
|
|
456
|
+
const mergedConfig = {
|
|
457
|
+
...parentConfig,
|
|
458
|
+
...versionConfig,
|
|
459
|
+
};
|
|
460
|
+
await generateApiDocs(mergedConfig);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
} else if (!versions[versionId]) {
|
|
464
|
+
console.error(
|
|
465
|
+
chalk.red(
|
|
466
|
+
`Version ID '${versionId}' does not exist in OpenAPI docs versions config.`
|
|
467
|
+
)
|
|
468
|
+
);
|
|
469
|
+
} else {
|
|
470
|
+
const versionConfig = versions[versionId];
|
|
471
|
+
const mergedConfig = {
|
|
472
|
+
...parentConfig,
|
|
473
|
+
...versionConfig,
|
|
474
|
+
};
|
|
475
|
+
await generateVersions(mergedVersions, parentConfig.outputDir);
|
|
476
|
+
await generateApiDocs(mergedConfig);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
319
480
|
cli
|
|
320
481
|
.command(`clean-api-docs`)
|
|
321
|
-
.description(
|
|
322
|
-
|
|
323
|
-
"[options] <id key value in plugin config within docusaurus.config.js>"
|
|
482
|
+
.description(
|
|
483
|
+
`Clears the generated OpenAPI docs MDX files and sidebar.js (if enabled).`
|
|
324
484
|
)
|
|
485
|
+
.usage("<id>")
|
|
325
486
|
.arguments("<id>")
|
|
326
487
|
.action(async (id) => {
|
|
327
488
|
if (id === "all") {
|
|
328
489
|
if (config[id]) {
|
|
329
|
-
console.error(
|
|
490
|
+
console.error(
|
|
491
|
+
chalk.red(
|
|
492
|
+
"Can't use id 'all' for OpenAPI docs configuration key."
|
|
493
|
+
)
|
|
494
|
+
);
|
|
330
495
|
} else {
|
|
331
496
|
Object.keys(config).forEach(async function (key) {
|
|
332
497
|
await cleanApiDocs(config[key]);
|
|
@@ -336,6 +501,51 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
|
|
336
501
|
await cleanApiDocs(config[id]);
|
|
337
502
|
}
|
|
338
503
|
});
|
|
504
|
+
|
|
505
|
+
cli
|
|
506
|
+
.command(`clean-api-docs:version`)
|
|
507
|
+
.description(
|
|
508
|
+
`Clears the versioned, generated OpenAPI docs MDX files, versions.json and sidebar.js (if enabled).`
|
|
509
|
+
)
|
|
510
|
+
.usage("<id:version>")
|
|
511
|
+
.arguments("<id:version>")
|
|
512
|
+
.action(async (id) => {
|
|
513
|
+
const [parentId, versionId] = id.split(":");
|
|
514
|
+
const { versions } = config[parentId] as any;
|
|
515
|
+
|
|
516
|
+
const parentConfig = Object.assign({}, config[parentId]);
|
|
517
|
+
delete parentConfig.versions;
|
|
518
|
+
|
|
519
|
+
if (versionId === "all") {
|
|
520
|
+
if (versions[id]) {
|
|
521
|
+
chalk.red(
|
|
522
|
+
"Can't use id 'all' for OpenAPI docs versions configuration key."
|
|
523
|
+
);
|
|
524
|
+
} else {
|
|
525
|
+
await cleanVersions(parentConfig.outputDir);
|
|
526
|
+
Object.keys(versions).forEach(async (key) => {
|
|
527
|
+
const versionConfig = versions[key];
|
|
528
|
+
const mergedConfig = {
|
|
529
|
+
...parentConfig,
|
|
530
|
+
...versionConfig,
|
|
531
|
+
};
|
|
532
|
+
await cleanApiDocs(mergedConfig);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
const versionConfig = versions[versionId];
|
|
537
|
+
const mergedConfig = {
|
|
538
|
+
...parentConfig,
|
|
539
|
+
...versionConfig,
|
|
540
|
+
};
|
|
541
|
+
await cleanApiDocs(mergedConfig);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
339
544
|
},
|
|
340
545
|
};
|
|
341
546
|
}
|
|
547
|
+
|
|
548
|
+
pluginOpenAPIDocs.validateOptions = ({ options, validate }: any) => {
|
|
549
|
+
const validatedOptions = validate(OptionsSchema, options);
|
|
550
|
+
return validatedOptions;
|
|
551
|
+
};
|
package/src/markdown/index.ts
CHANGED
|
@@ -59,7 +59,7 @@ export function createInfoPageMD({
|
|
|
59
59
|
}: InfoPageMetadata) {
|
|
60
60
|
return render([
|
|
61
61
|
`import Tabs from "@theme/Tabs";\n`,
|
|
62
|
-
`import TabItem from "@theme/TabItem";\n`,
|
|
62
|
+
`import TabItem from "@theme/TabItem";\n\n`,
|
|
63
63
|
createVersionBadge(version),
|
|
64
64
|
`# ${escape(title)}\n\n`,
|
|
65
65
|
createDescription(description),
|
package/src/options.ts
CHANGED
|
@@ -7,14 +7,47 @@
|
|
|
7
7
|
|
|
8
8
|
import { Joi } from "@docusaurus/utils-validation";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
const sidebarOptions = Joi.object({
|
|
11
|
+
groupPathsBy: Joi.string().valid("tag"),
|
|
12
|
+
categoryLinkSource: Joi.string().valid("tag", "info"),
|
|
13
|
+
customProps: Joi.object(),
|
|
14
|
+
sidebarCollapsible: Joi.boolean(),
|
|
15
|
+
sidebarCollapsed: Joi.boolean(),
|
|
16
|
+
});
|
|
16
17
|
|
|
17
18
|
export const OptionsSchema = Joi.object({
|
|
18
|
-
id: Joi.string().
|
|
19
|
-
|
|
19
|
+
id: Joi.string().required(),
|
|
20
|
+
docPluginId: Joi.string().required(),
|
|
21
|
+
config: Joi.object()
|
|
22
|
+
.pattern(
|
|
23
|
+
/^/,
|
|
24
|
+
Joi.object({
|
|
25
|
+
specPath: Joi.string().required(),
|
|
26
|
+
outputDir: Joi.string().required(),
|
|
27
|
+
template: Joi.string(),
|
|
28
|
+
sidebarOptions: sidebarOptions,
|
|
29
|
+
version: Joi.string().when("versions", {
|
|
30
|
+
is: Joi.exist(),
|
|
31
|
+
then: Joi.required(),
|
|
32
|
+
}),
|
|
33
|
+
label: Joi.string().when("versions", {
|
|
34
|
+
is: Joi.exist(),
|
|
35
|
+
then: Joi.required(),
|
|
36
|
+
}),
|
|
37
|
+
baseUrl: Joi.string().when("versions", {
|
|
38
|
+
is: Joi.exist(),
|
|
39
|
+
then: Joi.required(),
|
|
40
|
+
}),
|
|
41
|
+
versions: Joi.object().pattern(
|
|
42
|
+
/^/,
|
|
43
|
+
Joi.object({
|
|
44
|
+
specPath: Joi.string().required(),
|
|
45
|
+
outputDir: Joi.string().required(),
|
|
46
|
+
label: Joi.string().required(),
|
|
47
|
+
baseUrl: Joi.string().required(),
|
|
48
|
+
})
|
|
49
|
+
),
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
.required(),
|
|
20
53
|
});
|
package/src/sidebars/index.ts
CHANGED
|
@@ -35,7 +35,8 @@ function groupByTags(
|
|
|
35
35
|
items: ApiPageMetadata[],
|
|
36
36
|
sidebarOptions: SidebarOptions,
|
|
37
37
|
options: APIOptions,
|
|
38
|
-
tags: TagObject[]
|
|
38
|
+
tags: TagObject[],
|
|
39
|
+
docPath: string
|
|
39
40
|
): ProcessedSidebar {
|
|
40
41
|
const { outputDir } = options;
|
|
41
42
|
const {
|
|
@@ -63,10 +64,9 @@ function groupByTags(
|
|
|
63
64
|
.filter((item): item is string => !!item)
|
|
64
65
|
);
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.slice(outputDir.indexOf("/", 1))
|
|
69
|
-
.replace(/^\/+/g, "");
|
|
67
|
+
const basePath = docPath
|
|
68
|
+
? outputDir.split(docPath!)[1].replace(/^\/+/g, "")
|
|
69
|
+
: outputDir.slice(outputDir.indexOf("/", 1)).replace(/^\/+/g, "");
|
|
70
70
|
|
|
71
71
|
function createDocItem(item: ApiPageMetadata): SidebarItemDoc {
|
|
72
72
|
const sidebar_label = item.frontMatter.sidebar_label;
|
|
@@ -74,7 +74,8 @@ function groupByTags(
|
|
|
74
74
|
const id = item.id;
|
|
75
75
|
return {
|
|
76
76
|
type: "doc" as const,
|
|
77
|
-
id:
|
|
77
|
+
id:
|
|
78
|
+
basePath === "" || undefined ? `${item.id}` : `${basePath}/${item.id}`,
|
|
78
79
|
label: (sidebar_label as string) ?? title ?? id,
|
|
79
80
|
customProps: customProps,
|
|
80
81
|
className: clsx(
|
|
@@ -183,7 +184,8 @@ export default function generateSidebarSlice(
|
|
|
183
184
|
sidebarOptions: SidebarOptions,
|
|
184
185
|
options: APIOptions,
|
|
185
186
|
api: ApiMetadata[],
|
|
186
|
-
tags: TagObject[]
|
|
187
|
+
tags: TagObject[],
|
|
188
|
+
docPath: string
|
|
187
189
|
) {
|
|
188
190
|
let sidebarSlice: ProcessedSidebar = [];
|
|
189
191
|
if (sidebarOptions.groupPathsBy === "tag") {
|
|
@@ -191,7 +193,8 @@ export default function generateSidebarSlice(
|
|
|
191
193
|
api as ApiPageMetadata[],
|
|
192
194
|
sidebarOptions,
|
|
193
195
|
options,
|
|
194
|
-
tags
|
|
196
|
+
tags,
|
|
197
|
+
docPath
|
|
195
198
|
);
|
|
196
199
|
}
|
|
197
200
|
return sidebarSlice;
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { render } from "mustache";
|
|
9
|
+
|
|
10
|
+
export function versionSelector(versions: object[]) {
|
|
11
|
+
const template = `<div class="dropdown dropdown--hoverable dropdown--right">
|
|
12
|
+
<button class="button button--block button--sm button--secondary"><span>Select API Version</span></button>
|
|
13
|
+
<ul class="dropdown__menu">
|
|
14
|
+
{{#.}}<li><a class="dropdown__link" href="{{{baseUrl}}}">{{{label}}}</a></li>{{/.}}
|
|
15
|
+
</ul>
|
|
16
|
+
</div>
|
|
17
|
+
`;
|
|
18
|
+
const view = render(template, versions);
|
|
19
|
+
return view;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function versionCrumb(version: string) {
|
|
23
|
+
const template = `<ul style="display: flex;" class="breadcrumbs breadcrumbs--sm">
|
|
24
|
+
<li style="margin-left: auto; margin-right: 0;" class="breadcrumbs__item breadcrumbs__item--active">
|
|
25
|
+
<a class="breadcrumbs__link"><span>{{{.}}}</span></a></li></ul>
|
|
26
|
+
`;
|
|
27
|
+
const view = render(template, version);
|
|
28
|
+
return view;
|
|
29
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
} from "@docusaurus/plugin-content-docs-types";
|
|
23
23
|
export interface PluginOptions {
|
|
24
24
|
id?: string;
|
|
25
|
+
docPluginId: string;
|
|
25
26
|
config: {
|
|
26
27
|
[key: string]: APIOptions;
|
|
27
28
|
};
|
|
@@ -32,6 +33,27 @@ export interface APIOptions {
|
|
|
32
33
|
outputDir: string;
|
|
33
34
|
template?: string;
|
|
34
35
|
sidebarOptions?: SidebarOptions;
|
|
36
|
+
version?: string;
|
|
37
|
+
label?: string;
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
versions?: {
|
|
40
|
+
[key: string]: APIVersionOptions;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SidebarOptions {
|
|
45
|
+
groupPathsBy?: string;
|
|
46
|
+
categoryLinkSource?: string;
|
|
47
|
+
customProps?: { [key: string]: unknown };
|
|
48
|
+
sidebarCollapsible?: boolean;
|
|
49
|
+
sidebarCollapsed?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface APIVersionOptions {
|
|
53
|
+
specPath: string;
|
|
54
|
+
outputDir: string;
|
|
55
|
+
label: string;
|
|
56
|
+
baseUrl: string;
|
|
35
57
|
}
|
|
36
58
|
|
|
37
59
|
export interface LoadedContent {
|
|
@@ -99,11 +121,3 @@ export interface ApiNavLink {
|
|
|
99
121
|
title: string;
|
|
100
122
|
permalink: string;
|
|
101
123
|
}
|
|
102
|
-
|
|
103
|
-
export interface SidebarOptions {
|
|
104
|
-
groupPathsBy?: string;
|
|
105
|
-
categoryLinkSource?: string;
|
|
106
|
-
customProps?: { [key: string]: unknown };
|
|
107
|
-
sidebarCollapsible?: boolean;
|
|
108
|
-
sidebarCollapsed?: boolean;
|
|
109
|
-
}
|