docusaurus-plugin-openapi-docs 1.0.5 → 1.0.6
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 +5 -5
- package/lib/index.js +3 -3
- package/lib/openapi/openapi.d.ts +2 -2
- package/lib/openapi/openapi.js +6 -3
- package/lib/openapi/openapi.test.js +1 -1
- package/lib/openapi/utils/loadAndBundleSpec.d.ts +1 -1
- package/lib/openapi/utils/loadAndBundleSpec.js +36 -1
- package/lib/options.js +1 -1
- package/lib/sidebars/index.js +4 -2
- package/lib/types.d.ts +1 -1
- package/package.json +3 -2
- package/src/index.ts +3 -3
- package/src/openapi/openapi.test.ts +1 -1
- package/src/openapi/openapi.ts +12 -3
- package/src/openapi/utils/loadAndBundleSpec.ts +32 -1
- package/src/options.ts +1 -1
- package/src/sidebars/index.ts +4 -2
- package/src/types.ts +1 -1
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Here is an example of properly configuring your `docusaurus.config.js` file for
|
|
|
81
81
|
'docusaurus-plugin-openapi-docs',
|
|
82
82
|
{
|
|
83
83
|
id: "apiDocs",
|
|
84
|
-
|
|
84
|
+
docsPluginId: "classic",
|
|
85
85
|
config: {
|
|
86
86
|
petstore: { // Note: petstore key is treated as the <id> and can be used to specify an API doc instance when using CLI commands
|
|
87
87
|
specPath: "examples/petstore.yaml", // Path to designated spec file
|
|
@@ -108,10 +108,10 @@ Here is an example of properly configuring your `docusaurus.config.js` file for
|
|
|
108
108
|
|
|
109
109
|
The `docusaurus-plugin-openapi-docs` plugin can be configured with the following options:
|
|
110
110
|
|
|
111
|
-
| Name
|
|
112
|
-
|
|
|
113
|
-
| `id`
|
|
114
|
-
| `
|
|
111
|
+
| Name | Type | Default | Description |
|
|
112
|
+
| -------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `id` | `string` | `null` | A unique document id. |
|
|
114
|
+
| `docsPluginId` | `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"). |
|
|
115
115
|
|
|
116
116
|
### config
|
|
117
117
|
|
package/lib/index.js
CHANGED
|
@@ -54,13 +54,13 @@ function getDocsData(dataArray, filter) {
|
|
|
54
54
|
}
|
|
55
55
|
exports.getDocsData = getDocsData;
|
|
56
56
|
function pluginOpenAPIDocs(context, options) {
|
|
57
|
-
const { config,
|
|
57
|
+
const { config, docsPluginId } = options;
|
|
58
58
|
const { siteDir, siteConfig } = context;
|
|
59
59
|
// Get routeBasePath and path from plugin-content-docs or preset
|
|
60
60
|
const presets = siteConfig.presets;
|
|
61
61
|
const plugins = siteConfig.plugins;
|
|
62
62
|
const presetsPlugins = presets.concat(plugins);
|
|
63
|
-
const docData = getDocsData(presetsPlugins,
|
|
63
|
+
const docData = getDocsData(presetsPlugins, docsPluginId);
|
|
64
64
|
const docRouteBasePath = docData ? docData.routeBasePath : undefined;
|
|
65
65
|
const docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
|
|
66
66
|
async function generateApiDocs(options) {
|
|
@@ -69,7 +69,7 @@ function pluginOpenAPIDocs(context, options) {
|
|
|
69
69
|
? specPath
|
|
70
70
|
: path_1.default.resolve(siteDir, specPath);
|
|
71
71
|
try {
|
|
72
|
-
const openapiFiles = await (0, openapi_1.readOpenapiFiles)(contentPath,
|
|
72
|
+
const openapiFiles = await (0, openapi_1.readOpenapiFiles)(contentPath, options);
|
|
73
73
|
const [loadedApi, tags] = await (0, openapi_1.processOpenapiFiles)(openapiFiles, sidebarOptions);
|
|
74
74
|
if (!fs_1.default.existsSync(outputDir)) {
|
|
75
75
|
try {
|
package/lib/openapi/openapi.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ApiMetadata, SidebarOptions } from "../types";
|
|
1
|
+
import { ApiMetadata, APIOptions, SidebarOptions } from "../types";
|
|
2
2
|
import { OpenApiObjectWithRef, TagObject } from "./types";
|
|
3
3
|
interface OpenApiFiles {
|
|
4
4
|
source: string;
|
|
5
5
|
sourceDirName: string;
|
|
6
6
|
data: OpenApiObjectWithRef;
|
|
7
7
|
}
|
|
8
|
-
export declare function readOpenapiFiles(openapiPath: string,
|
|
8
|
+
export declare function readOpenapiFiles(openapiPath: string, options: APIOptions): Promise<OpenApiFiles[]>;
|
|
9
9
|
export declare function processOpenapiFiles(files: OpenApiFiles[], sidebarOptions: SidebarOptions): Promise<[ApiMetadata[], TagObject[]]>;
|
|
10
10
|
export declare function processOpenapiFile(openapiDataWithRefs: OpenApiObjectWithRef, sidebarOptions: SidebarOptions): Promise<[ApiMetadata[], TagObject[]]>;
|
|
11
11
|
export declare function getTagDisplayName(tagName: string, tags: TagObject[]): string;
|
package/lib/openapi/openapi.js
CHANGED
|
@@ -188,7 +188,10 @@ function bindCollectionToApiItems(items, postmanCollection) {
|
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
|
-
async function readOpenapiFiles(openapiPath,
|
|
191
|
+
async function readOpenapiFiles(openapiPath, options) {
|
|
192
|
+
// TODO: determine if this should be an API option
|
|
193
|
+
// Forces the json-schema-ref-parser
|
|
194
|
+
const parseJsonRefs = true;
|
|
192
195
|
if (!(0, index_1.isURL)(openapiPath)) {
|
|
193
196
|
const stat = await fs_extra_1.default.lstat(openapiPath);
|
|
194
197
|
if (stat.isDirectory()) {
|
|
@@ -203,7 +206,7 @@ async function readOpenapiFiles(openapiPath, _options) {
|
|
|
203
206
|
return Promise.all(sources.map(async (source) => {
|
|
204
207
|
// TODO: make a function for this
|
|
205
208
|
const fullPath = path_1.default.join(openapiPath, source);
|
|
206
|
-
const data = (await (0, loadAndBundleSpec_1.loadAndBundleSpec)(fullPath));
|
|
209
|
+
const data = (await (0, loadAndBundleSpec_1.loadAndBundleSpec)(fullPath, parseJsonRefs));
|
|
207
210
|
return {
|
|
208
211
|
source: fullPath,
|
|
209
212
|
sourceDirName: path_1.default.dirname(source),
|
|
@@ -212,7 +215,7 @@ async function readOpenapiFiles(openapiPath, _options) {
|
|
|
212
215
|
}));
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
|
-
const data = (await (0, loadAndBundleSpec_1.loadAndBundleSpec)(openapiPath));
|
|
218
|
+
const data = (await (0, loadAndBundleSpec_1.loadAndBundleSpec)(openapiPath, parseJsonRefs));
|
|
216
219
|
return [
|
|
217
220
|
{
|
|
218
221
|
source: openapiPath,
|
|
@@ -15,7 +15,7 @@ const _1 = require(".");
|
|
|
15
15
|
describe("openapi", () => {
|
|
16
16
|
describe("readOpenapiFiles", () => {
|
|
17
17
|
it("readOpenapiFiles", async () => {
|
|
18
|
-
const results = await (0, _1.readOpenapiFiles)(path_1.default.join(__dirname, "__fixtures__/examples"), {});
|
|
18
|
+
const results = await (0, _1.readOpenapiFiles)(path_1.default.join(__dirname, "__fixtures__/examples"), { specPath: "./", outputDir: "./" });
|
|
19
19
|
const categoryMeta = results.find((x) => x.source.endsWith("_category_.json"));
|
|
20
20
|
expect(categoryMeta).toBeFalsy();
|
|
21
21
|
// console.log(results);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { OpenAPISpec } from "./types";
|
|
2
|
-
export declare function loadAndBundleSpec(specUrlOrObject: object | string): Promise<OpenAPISpec>;
|
|
2
|
+
export declare function loadAndBundleSpec(specUrlOrObject: object | string, parseJsonRefs: boolean | undefined): Promise<OpenAPISpec>;
|
|
3
3
|
export declare function convertSwagger2OpenAPI(spec: any): Promise<OpenAPISpec>;
|
|
@@ -5,12 +5,39 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
* ========================================================================== */
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
8
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
12
|
exports.convertSwagger2OpenAPI = exports.loadAndBundleSpec = void 0;
|
|
13
|
+
// @ts-nocheck
|
|
14
|
+
const json_schema_ref_parser_1 = __importDefault(require("@apidevtools/json-schema-ref-parser"));
|
|
10
15
|
const bundle_1 = require("@redocly/openapi-core/lib/bundle");
|
|
11
16
|
const config_1 = require("@redocly/openapi-core/lib/config/config");
|
|
17
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
18
|
const swagger2openapi_1 = require("swagger2openapi");
|
|
13
|
-
async function
|
|
19
|
+
async function resolveJsonRefs(specUrlOrObject) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
try {
|
|
22
|
+
let schema = await json_schema_ref_parser_1.default.dereference(specUrlOrObject, {
|
|
23
|
+
continueOnError: true,
|
|
24
|
+
resolve: {
|
|
25
|
+
http: {
|
|
26
|
+
timeout: 15000, // 15 sec timeout
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
dereference: {
|
|
30
|
+
circular: "ignore",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
return schema;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error(chalk_1.default.yellow((_b = (_a = err.errors[0]) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : err));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function loadAndBundleSpec(specUrlOrObject, parseJsonRefs) {
|
|
14
41
|
const config = new config_1.Config({});
|
|
15
42
|
const bundleOpts = {
|
|
16
43
|
config,
|
|
@@ -28,6 +55,14 @@ async function loadAndBundleSpec(specUrlOrObject) {
|
|
|
28
55
|
// Force dereference ?
|
|
29
56
|
// bundleOpts["dereference"] = true;
|
|
30
57
|
const { bundle: { parsed }, } = await (0, bundle_1.bundle)(bundleOpts);
|
|
58
|
+
if (parseJsonRefs) {
|
|
59
|
+
const resolved = resolveJsonRefs(parsed);
|
|
60
|
+
return typeof resolved === Object
|
|
61
|
+
? resolved.swagger !== undefined
|
|
62
|
+
? convertSwagger2OpenAPI(resolved)
|
|
63
|
+
: resolved
|
|
64
|
+
: parsed;
|
|
65
|
+
}
|
|
31
66
|
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
|
32
67
|
}
|
|
33
68
|
exports.loadAndBundleSpec = loadAndBundleSpec;
|
package/lib/options.js
CHANGED
|
@@ -17,7 +17,7 @@ const sidebarOptions = utils_validation_1.Joi.object({
|
|
|
17
17
|
});
|
|
18
18
|
exports.OptionsSchema = utils_validation_1.Joi.object({
|
|
19
19
|
id: utils_validation_1.Joi.string().required(),
|
|
20
|
-
|
|
20
|
+
docsPluginId: utils_validation_1.Joi.string().required(),
|
|
21
21
|
config: utils_validation_1.Joi.object()
|
|
22
22
|
.pattern(/^/, utils_validation_1.Joi.object({
|
|
23
23
|
specPath: utils_validation_1.Joi.string().required(),
|
package/lib/sidebars/index.js
CHANGED
|
@@ -19,7 +19,7 @@ function isInfoItem(item) {
|
|
|
19
19
|
return item.type === "info";
|
|
20
20
|
}
|
|
21
21
|
function groupByTags(items, sidebarOptions, options, tags, docPath) {
|
|
22
|
-
const { outputDir } = options;
|
|
22
|
+
const { outputDir, label } = options;
|
|
23
23
|
const { sidebarCollapsed, sidebarCollapsible, customProps, categoryLinkSource, } = sidebarOptions;
|
|
24
24
|
const apiItems = items.filter(isApiItem);
|
|
25
25
|
const infoItems = items.filter(isInfoItem);
|
|
@@ -95,7 +95,9 @@ function groupByTags(items, sidebarOptions, options, tags, docPath) {
|
|
|
95
95
|
linkConfig = {
|
|
96
96
|
type: "generated-index",
|
|
97
97
|
title: tag,
|
|
98
|
-
slug:
|
|
98
|
+
slug: label
|
|
99
|
+
? "/category/" + (0, lodash_1.kebabCase)(label) + "/" + (0, lodash_1.kebabCase)(tag)
|
|
100
|
+
: "/category/" + (0, lodash_1.kebabCase)(tag),
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
103
|
return {
|
package/lib/types.d.ts
CHANGED
|
@@ -3,7 +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
|
-
|
|
6
|
+
docsPluginId: string;
|
|
7
7
|
config: {
|
|
8
8
|
[key: string]: APIOptions;
|
|
9
9
|
};
|
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.6",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"openapi",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"utility-types": "^3.10.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@apidevtools/json-schema-ref-parser": "^9.0.9",
|
|
39
40
|
"@docusaurus/mdx-loader": "2.0.0-beta.21",
|
|
40
41
|
"@docusaurus/plugin-content-docs": "2.0.0-beta.21",
|
|
41
42
|
"@docusaurus/utils": "2.0.0-beta.21",
|
|
@@ -62,5 +63,5 @@
|
|
|
62
63
|
"engines": {
|
|
63
64
|
"node": ">=14"
|
|
64
65
|
},
|
|
65
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "ff7622e334e96e7f39b0daf5ef2cb17bfa832834"
|
|
66
67
|
}
|
package/src/index.ts
CHANGED
|
@@ -62,14 +62,14 @@ export default function pluginOpenAPIDocs(
|
|
|
62
62
|
context: LoadContext,
|
|
63
63
|
options: PluginOptions
|
|
64
64
|
): Plugin<LoadedContent> {
|
|
65
|
-
const { config,
|
|
65
|
+
const { config, docsPluginId } = options;
|
|
66
66
|
const { siteDir, siteConfig } = context;
|
|
67
67
|
|
|
68
68
|
// Get routeBasePath and path from plugin-content-docs or preset
|
|
69
69
|
const presets: any = siteConfig.presets;
|
|
70
70
|
const plugins: any = siteConfig.plugins;
|
|
71
71
|
const presetsPlugins = presets.concat(plugins);
|
|
72
|
-
const docData: any = getDocsData(presetsPlugins,
|
|
72
|
+
const docData: any = getDocsData(presetsPlugins, docsPluginId);
|
|
73
73
|
const docRouteBasePath = docData ? docData.routeBasePath : undefined;
|
|
74
74
|
const docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
|
|
75
75
|
|
|
@@ -81,7 +81,7 @@ export default function pluginOpenAPIDocs(
|
|
|
81
81
|
: path.resolve(siteDir, specPath);
|
|
82
82
|
|
|
83
83
|
try {
|
|
84
|
-
const openapiFiles = await readOpenapiFiles(contentPath,
|
|
84
|
+
const openapiFiles = await readOpenapiFiles(contentPath, options);
|
|
85
85
|
const [loadedApi, tags] = await processOpenapiFiles(
|
|
86
86
|
openapiFiles,
|
|
87
87
|
sidebarOptions!
|
|
@@ -16,7 +16,7 @@ describe("openapi", () => {
|
|
|
16
16
|
it("readOpenapiFiles", async () => {
|
|
17
17
|
const results = await readOpenapiFiles(
|
|
18
18
|
path.join(__dirname, "__fixtures__/examples"),
|
|
19
|
-
{}
|
|
19
|
+
{ specPath: "./", outputDir: "./" }
|
|
20
20
|
);
|
|
21
21
|
const categoryMeta = results.find((x) =>
|
|
22
22
|
x.source.endsWith("_category_.json")
|
package/src/openapi/openapi.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { kebabCase } from "lodash";
|
|
|
19
19
|
import { isURL } from "../index";
|
|
20
20
|
import {
|
|
21
21
|
ApiMetadata,
|
|
22
|
+
APIOptions,
|
|
22
23
|
ApiPageMetadata,
|
|
23
24
|
InfoPageMetadata,
|
|
24
25
|
SidebarOptions,
|
|
@@ -247,8 +248,12 @@ interface OpenApiFiles {
|
|
|
247
248
|
|
|
248
249
|
export async function readOpenapiFiles(
|
|
249
250
|
openapiPath: string,
|
|
250
|
-
|
|
251
|
+
options: APIOptions
|
|
251
252
|
): Promise<OpenApiFiles[]> {
|
|
253
|
+
// TODO: determine if this should be an API option
|
|
254
|
+
// Forces the json-schema-ref-parser
|
|
255
|
+
const parseJsonRefs = true;
|
|
256
|
+
|
|
252
257
|
if (!isURL(openapiPath)) {
|
|
253
258
|
const stat = await fs.lstat(openapiPath);
|
|
254
259
|
if (stat.isDirectory()) {
|
|
@@ -270,7 +275,8 @@ export async function readOpenapiFiles(
|
|
|
270
275
|
// TODO: make a function for this
|
|
271
276
|
const fullPath = path.join(openapiPath, source);
|
|
272
277
|
const data = (await loadAndBundleSpec(
|
|
273
|
-
fullPath
|
|
278
|
+
fullPath,
|
|
279
|
+
parseJsonRefs
|
|
274
280
|
)) as OpenApiObjectWithRef;
|
|
275
281
|
return {
|
|
276
282
|
source: fullPath, // This will be aliased in process.
|
|
@@ -281,7 +287,10 @@ export async function readOpenapiFiles(
|
|
|
281
287
|
);
|
|
282
288
|
}
|
|
283
289
|
}
|
|
284
|
-
const data = (await loadAndBundleSpec(
|
|
290
|
+
const data = (await loadAndBundleSpec(
|
|
291
|
+
openapiPath,
|
|
292
|
+
parseJsonRefs
|
|
293
|
+
)) as OpenApiObjectWithRef;
|
|
285
294
|
return [
|
|
286
295
|
{
|
|
287
296
|
source: openapiPath, // This will be aliased in process.
|
|
@@ -7,16 +7,39 @@
|
|
|
7
7
|
|
|
8
8
|
// @ts-nocheck
|
|
9
9
|
|
|
10
|
+
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
10
11
|
import type { Source, Document } from "@redocly/openapi-core";
|
|
11
12
|
import { bundle } from "@redocly/openapi-core/lib/bundle";
|
|
12
13
|
import type { ResolvedConfig } from "@redocly/openapi-core/lib/config";
|
|
13
14
|
import { Config } from "@redocly/openapi-core/lib/config/config";
|
|
15
|
+
import chalk from "chalk";
|
|
14
16
|
import { convertObj } from "swagger2openapi";
|
|
15
17
|
|
|
16
18
|
import { OpenAPISpec } from "./types";
|
|
17
19
|
|
|
20
|
+
async function resolveJsonRefs(specUrlOrObject: object | string) {
|
|
21
|
+
try {
|
|
22
|
+
let schema = await $RefParser.dereference(specUrlOrObject, {
|
|
23
|
+
continueOnError: true,
|
|
24
|
+
resolve: {
|
|
25
|
+
http: {
|
|
26
|
+
timeout: 15000, // 15 sec timeout
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
dereference: {
|
|
30
|
+
circular: "ignore",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
return schema;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error(chalk.yellow(err.errors[0]?.message ?? err));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
export async function loadAndBundleSpec(
|
|
19
|
-
specUrlOrObject: object | string
|
|
41
|
+
specUrlOrObject: object | string,
|
|
42
|
+
parseJsonRefs: boolean | undefined
|
|
20
43
|
): Promise<OpenAPISpec> {
|
|
21
44
|
const config = new Config({} as ResolvedConfig);
|
|
22
45
|
const bundleOpts = {
|
|
@@ -39,6 +62,14 @@ export async function loadAndBundleSpec(
|
|
|
39
62
|
const {
|
|
40
63
|
bundle: { parsed },
|
|
41
64
|
} = await bundle(bundleOpts);
|
|
65
|
+
if (parseJsonRefs) {
|
|
66
|
+
const resolved = resolveJsonRefs(parsed);
|
|
67
|
+
return typeof resolved === Object
|
|
68
|
+
? resolved.swagger !== undefined
|
|
69
|
+
? convertSwagger2OpenAPI(resolved)
|
|
70
|
+
: resolved
|
|
71
|
+
: parsed;
|
|
72
|
+
}
|
|
42
73
|
return parsed.swagger !== undefined ? convertSwagger2OpenAPI(parsed) : parsed;
|
|
43
74
|
}
|
|
44
75
|
|
package/src/options.ts
CHANGED
package/src/sidebars/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ function groupByTags(
|
|
|
38
38
|
tags: TagObject[],
|
|
39
39
|
docPath: string
|
|
40
40
|
): ProcessedSidebar {
|
|
41
|
-
const { outputDir } = options;
|
|
41
|
+
const { outputDir, label } = options;
|
|
42
42
|
const {
|
|
43
43
|
sidebarCollapsed,
|
|
44
44
|
sidebarCollapsible,
|
|
@@ -135,7 +135,9 @@ function groupByTags(
|
|
|
135
135
|
linkConfig = {
|
|
136
136
|
type: "generated-index" as "generated-index",
|
|
137
137
|
title: tag,
|
|
138
|
-
slug:
|
|
138
|
+
slug: label
|
|
139
|
+
? "/category/" + kebabCase(label) + "/" + kebabCase(tag)
|
|
140
|
+
: "/category/" + kebabCase(tag),
|
|
139
141
|
} as SidebarItemCategoryLinkConfig;
|
|
140
142
|
}
|
|
141
143
|
|