docusaurus-plugin-openapi-docs 1.4.3 → 1.4.5

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/lib/index.js CHANGED
@@ -125,7 +125,7 @@ description: "{{{frontMatter.description}}}"
125
125
  sidebar_label: Introduction
126
126
  {{/api}}
127
127
  {{#api}}
128
- sidebar_label: {{{title}}}
128
+ sidebar_label: "{{{title}}}"
129
129
  {{/api}}
130
130
  {{^api}}
131
131
  sidebar_position: 0
@@ -151,7 +151,7 @@ info_path: {{{infoPath}}}
151
151
  id: {{{id}}}
152
152
  title: "{{{title}}}"
153
153
  description: "{{{frontMatter.description}}}"
154
- sidebar_label: {{{title}}}
154
+ sidebar_label: "{{{title}}}"
155
155
  hide_title: true
156
156
  custom_edit_url: null
157
157
  ---
@@ -25,6 +25,9 @@ function mergeAllOf(allOf) {
25
25
  example: function () {
26
26
  return true;
27
27
  },
28
+ "x-examples": function () {
29
+ return true;
30
+ },
28
31
  ignoreAdditionalProperties: true,
29
32
  },
30
33
  });
@@ -89,14 +92,16 @@ function createAnyOneOf(schema) {
89
92
  }
90
93
  function createProperties(schema) {
91
94
  const discriminator = schema.discriminator;
92
- return Object.entries(schema.properties).map(([key, val]) => createEdges({
93
- name: key,
94
- schema: val,
95
- required: Array.isArray(schema.required)
96
- ? schema.required.includes(key)
97
- : false,
98
- discriminator,
99
- }));
95
+ return Object.entries(schema.properties).map(([key, val]) => {
96
+ return createEdges({
97
+ name: key,
98
+ schema: val,
99
+ required: Array.isArray(schema.required)
100
+ ? schema.required.includes(key)
101
+ : false,
102
+ discriminator,
103
+ });
104
+ });
100
105
  }
101
106
  function createAdditionalProperties(schema) {
102
107
  // TODO?:
@@ -440,7 +445,7 @@ function createPropertyDiscriminator(name, schemaName, schema, discriminator, re
440
445
  * Creates the edges or "leaves" of a schema tree. Edges can branch into sub-nodes with createDetails().
441
446
  */
442
447
  function createEdges({ name, schema, required, discriminator, }) {
443
- var _a, _b;
448
+ var _a, _b, _c, _d;
444
449
  const schemaName = (0, schema_1.getSchemaName)(schema);
445
450
  if (discriminator !== undefined && discriminator.propertyName === name) {
446
451
  return createPropertyDiscriminator(name, "string", schema, discriminator, required);
@@ -488,6 +493,9 @@ function createEdges({ name, schema, required, discriminator, }) {
488
493
  if (((_b = schema.items) === null || _b === void 0 ? void 0 : _b.properties) !== undefined) {
489
494
  return createDetailsNode(name, schemaName, schema, required);
490
495
  }
496
+ if (((_c = schema.items) === null || _c === void 0 ? void 0 : _c.anyOf) !== undefined || ((_d = schema.items) === null || _d === void 0 ? void 0 : _d.oneOf) !== undefined) {
497
+ return createDetailsNode(name, schemaName, schema, required);
498
+ }
491
499
  if (schema.readOnly && schema.readOnly === true) {
492
500
  return undefined;
493
501
  }
@@ -26,6 +26,9 @@ function mergeAllOf(allOf) {
26
26
  example: function () {
27
27
  return true;
28
28
  },
29
+ "x-examples": function () {
30
+ return true;
31
+ },
29
32
  },
30
33
  ignoreAdditionalProperties: true,
31
34
  });
@@ -90,14 +93,16 @@ function createAnyOneOf(schema) {
90
93
  }
91
94
  function createProperties(schema) {
92
95
  const discriminator = schema.discriminator;
93
- return Object.entries(schema.properties).map(([key, val]) => createEdges({
94
- name: key,
95
- schema: val,
96
- required: Array.isArray(schema.required)
97
- ? schema.required.includes(key)
98
- : false,
99
- discriminator,
100
- }));
96
+ return Object.entries(schema.properties).map(([key, val]) => {
97
+ return createEdges({
98
+ name: key,
99
+ schema: val,
100
+ required: Array.isArray(schema.required)
101
+ ? schema.required.includes(key)
102
+ : false,
103
+ discriminator,
104
+ });
105
+ });
101
106
  }
102
107
  function createAdditionalProperties(schema) {
103
108
  // TODO?:
@@ -439,7 +444,7 @@ function createPropertyDiscriminator(name, schemaName, schema, discriminator, re
439
444
  * Creates the edges or "leaves" of a schema tree. Edges can branch into sub-nodes with createDetails().
440
445
  */
441
446
  function createEdges({ name, schema, required, discriminator, }) {
442
- var _a, _b;
447
+ var _a, _b, _c, _d;
443
448
  const schemaName = (0, schema_1.getSchemaName)(schema);
444
449
  if (discriminator !== undefined && discriminator.propertyName === name) {
445
450
  return createPropertyDiscriminator(name, "string", schema, discriminator, required);
@@ -487,6 +492,9 @@ function createEdges({ name, schema, required, discriminator, }) {
487
492
  if (((_b = schema.items) === null || _b === void 0 ? void 0 : _b.properties) !== undefined) {
488
493
  return createDetailsNode(name, schemaName, schema, required);
489
494
  }
495
+ if (((_c = schema.items) === null || _c === void 0 ? void 0 : _c.anyOf) !== undefined || ((_d = schema.items) === null || _d === void 0 ? void 0 : _d.oneOf) !== undefined) {
496
+ return createDetailsNode(name, schemaName, schema, required);
497
+ }
490
498
  if (schema.writeOnly && schema.writeOnly === true) {
491
499
  return undefined;
492
500
  }
@@ -19,6 +19,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
19
19
  const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
20
20
  const kebabCase_1 = __importDefault(require("lodash/kebabCase"));
21
21
  const unionBy_1 = __importDefault(require("lodash/unionBy"));
22
+ const uniq_1 = __importDefault(require("lodash/uniq"));
22
23
  const index_1 = require("../index");
23
24
  const createRequestExample_1 = require("./createRequestExample");
24
25
  const loadAndResolveSpec_1 = require("./utils/loadAndResolveSpec");
@@ -64,41 +65,9 @@ function createItems(openapiData, sidebarOptions) {
64
65
  // TODO: Find a better way to handle this
65
66
  let items = [];
66
67
  const infoId = (0, kebabCase_1.default)(openapiData.info.title);
67
- if ((sidebarOptions === null || sidebarOptions === void 0 ? void 0 : sidebarOptions.categoryLinkSource) === "tag") {
68
- // Only create an tag pages if categoryLinkSource set to tag.
69
- const tags = (_a = openapiData.tags) !== null && _a !== void 0 ? _a : [];
70
- // eslint-disable-next-line array-callback-return
71
- tags
72
- .filter((tag) => { var _a; return !((_a = tag.description) === null || _a === void 0 ? void 0 : _a.includes("SchemaDefinition")); })
73
- // eslint-disable-next-line array-callback-return
74
- .map((tag) => {
75
- var _a;
76
- const description = getTagDisplayName(tag.name, (_a = openapiData.tags) !== null && _a !== void 0 ? _a : []);
77
- const tagId = (0, kebabCase_1.default)(tag.name);
78
- const splitDescription = description.match(/[^\r\n]+/g);
79
- const tagPage = {
80
- type: "tag",
81
- id: tagId,
82
- unversionedId: tagId,
83
- title: description !== null && description !== void 0 ? description : "",
84
- description: description !== null && description !== void 0 ? description : "",
85
- frontMatter: {
86
- description: splitDescription
87
- ? splitDescription[0]
88
- .replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
89
- .replace(/\s+$/, "")
90
- : "",
91
- },
92
- tag: {
93
- ...tag,
94
- },
95
- };
96
- items.push(tagPage);
97
- });
98
- }
99
68
  if (openapiData.info.description) {
100
69
  // Only create an info page if we have a description.
101
- const infoDescription = (_b = openapiData.info) === null || _b === void 0 ? void 0 : _b.description;
70
+ const infoDescription = (_a = openapiData.info) === null || _a === void 0 ? void 0 : _a.description;
102
71
  let splitDescription;
103
72
  if (infoDescription) {
104
73
  splitDescription = infoDescription.match(/[^\r\n]+/g);
@@ -107,7 +76,9 @@ function createItems(openapiData, sidebarOptions) {
107
76
  type: "info",
108
77
  id: infoId,
109
78
  unversionedId: infoId,
110
- title: openapiData.info.title,
79
+ title: openapiData.info.title
80
+ ? openapiData.info.title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
81
+ : "",
111
82
  description: openapiData.info.description
112
83
  ? openapiData.info.description.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
113
84
  : "",
@@ -118,11 +89,11 @@ function createItems(openapiData, sidebarOptions) {
118
89
  .replace(/\s+$/, "")
119
90
  : "",
120
91
  },
121
- securitySchemes: (_c = openapiData.components) === null || _c === void 0 ? void 0 : _c.securitySchemes,
92
+ securitySchemes: (_b = openapiData.components) === null || _b === void 0 ? void 0 : _b.securitySchemes,
122
93
  info: {
123
94
  ...openapiData.info,
124
95
  tags: openapiData.tags,
125
- title: (_d = openapiData.info.title) !== null && _d !== void 0 ? _d : "Introduction",
96
+ title: (_c = openapiData.info.title) !== null && _c !== void 0 ? _c : "Introduction",
126
97
  logo: openapiData.info["x-logo"],
127
98
  darkLogo: openapiData.info["x-dark-logo"],
128
99
  },
@@ -132,18 +103,18 @@ function createItems(openapiData, sidebarOptions) {
132
103
  for (let [path, pathObject] of Object.entries(openapiData.paths)) {
133
104
  const { $ref, description, parameters, servers, summary, ...rest } = pathObject;
134
105
  for (let [method, operationObject] of Object.entries({ ...rest })) {
135
- const title = (_f = (_e = operationObject.summary) !== null && _e !== void 0 ? _e : operationObject.operationId) !== null && _f !== void 0 ? _f : "Missing summary";
106
+ const title = (_e = (_d = operationObject.summary) !== null && _d !== void 0 ? _d : operationObject.operationId) !== null && _e !== void 0 ? _e : "Missing summary";
136
107
  if (operationObject.description === undefined) {
137
108
  operationObject.description =
138
- (_h = (_g = operationObject.summary) !== null && _g !== void 0 ? _g : operationObject.operationId) !== null && _h !== void 0 ? _h : "";
109
+ (_g = (_f = operationObject.summary) !== null && _f !== void 0 ? _f : operationObject.operationId) !== null && _g !== void 0 ? _g : "";
139
110
  }
140
111
  const baseId = operationObject.operationId
141
112
  ? (0, kebabCase_1.default)(operationObject.operationId)
142
113
  : (0, kebabCase_1.default)(operationObject.summary);
143
- const servers = (_k = (_j = operationObject.servers) !== null && _j !== void 0 ? _j : pathObject.servers) !== null && _k !== void 0 ? _k : openapiData.servers;
144
- const security = (_l = operationObject.security) !== null && _l !== void 0 ? _l : openapiData.security;
114
+ const servers = (_j = (_h = operationObject.servers) !== null && _h !== void 0 ? _h : pathObject.servers) !== null && _j !== void 0 ? _j : openapiData.servers;
115
+ const security = (_k = operationObject.security) !== null && _k !== void 0 ? _k : openapiData.security;
145
116
  // Add security schemes so we know how to handle security.
146
- const securitySchemes = (_m = openapiData.components) === null || _m === void 0 ? void 0 : _m.securitySchemes;
117
+ const securitySchemes = (_l = openapiData.components) === null || _l === void 0 ? void 0 : _l.securitySchemes;
147
118
  // Make sure schemes are lowercase. See: https://github.com/cloud-annotations/docusaurus-plugin-openapi/issues/79
148
119
  if (securitySchemes) {
149
120
  for (let securityScheme of Object.values(securitySchemes)) {
@@ -153,12 +124,12 @@ function createItems(openapiData, sidebarOptions) {
153
124
  }
154
125
  }
155
126
  let jsonRequestBodyExample;
156
- const body = (_p = (_o = operationObject.requestBody) === null || _o === void 0 ? void 0 : _o.content) === null || _p === void 0 ? void 0 : _p["application/json"];
127
+ const body = (_o = (_m = operationObject.requestBody) === null || _m === void 0 ? void 0 : _m.content) === null || _o === void 0 ? void 0 : _o["application/json"];
157
128
  if (body === null || body === void 0 ? void 0 : body.schema) {
158
129
  jsonRequestBodyExample = (0, createRequestExample_1.sampleRequestFromSchema)(body.schema);
159
130
  }
160
131
  // Handle vendor JSON media types
161
- const bodyContent = (_q = operationObject.requestBody) === null || _q === void 0 ? void 0 : _q.content;
132
+ const bodyContent = (_p = operationObject.requestBody) === null || _p === void 0 ? void 0 : _p.content;
162
133
  if (bodyContent) {
163
134
  const firstBodyContentKey = Object.keys(bodyContent)[0];
164
135
  if (firstBodyContentKey.endsWith("+json")) {
@@ -190,7 +161,7 @@ function createItems(openapiData, sidebarOptions) {
190
161
  id: baseId,
191
162
  infoId: infoId !== null && infoId !== void 0 ? infoId : "",
192
163
  unversionedId: baseId,
193
- title: title,
164
+ title: title ? title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'") : "",
194
165
  description: operationObject.description
195
166
  ? operationObject.description.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
196
167
  : "",
@@ -216,6 +187,45 @@ function createItems(openapiData, sidebarOptions) {
216
187
  items.push(apiPage);
217
188
  }
218
189
  }
190
+ if ((sidebarOptions === null || sidebarOptions === void 0 ? void 0 : sidebarOptions.categoryLinkSource) === "tag") {
191
+ // Get global tags
192
+ const tags = (_q = openapiData.tags) !== null && _q !== void 0 ? _q : [];
193
+ // Get operation tags
194
+ const apiItems = items.filter((item) => {
195
+ return item.type === "api";
196
+ });
197
+ const operationTags = (0, uniq_1.default)(apiItems
198
+ .flatMap((item) => item.api.tags)
199
+ .filter((item) => !!item));
200
+ // eslint-disable-next-line array-callback-return
201
+ tags
202
+ .filter((tag) => operationTags.includes(tag.name)) // include only tags referenced by operation
203
+ // eslint-disable-next-line array-callback-return
204
+ .map((tag) => {
205
+ var _a;
206
+ const description = getTagDisplayName(tag.name, (_a = openapiData.tags) !== null && _a !== void 0 ? _a : []);
207
+ const tagId = (0, kebabCase_1.default)(tag.name);
208
+ const splitDescription = description.match(/[^\r\n]+/g);
209
+ const tagPage = {
210
+ type: "tag",
211
+ id: tagId,
212
+ unversionedId: tagId,
213
+ title: description !== null && description !== void 0 ? description : "",
214
+ description: description !== null && description !== void 0 ? description : "",
215
+ frontMatter: {
216
+ description: splitDescription
217
+ ? splitDescription[0]
218
+ .replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
219
+ .replace(/\s+$/, "")
220
+ : "",
221
+ },
222
+ tag: {
223
+ ...tag,
224
+ },
225
+ };
226
+ items.push(tagPage);
227
+ });
228
+ }
219
229
  return items;
220
230
  }
221
231
  /**
@@ -38,9 +38,13 @@ function groupByTags(items, sidebarOptions, options, tags, docPath) {
38
38
  .flatMap((item) => item.api.tags)
39
39
  .filter((item) => !!item));
40
40
  // Combine globally defined tags with operation tags
41
+ // Only include global tag if referenced in operation tags
41
42
  let apiTags = [];
42
43
  tags.flat().forEach((tag) => {
43
- apiTags.push(tag.name);
44
+ // Should we also check x-displayName?
45
+ if (operationTags.includes(tag.name)) {
46
+ apiTags.push(tag.name);
47
+ }
44
48
  });
45
49
  apiTags = (0, uniq_1.default)(apiTags.concat(operationTags));
46
50
  const basePath = docPath
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.4.3",
4
+ "version": "1.4.5",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -28,8 +28,8 @@
28
28
  "watch": "tsc --watch"
29
29
  },
30
30
  "devDependencies": {
31
- "@docusaurus/module-type-aliases": "2.0.1",
32
- "@docusaurus/types": "2.0.1",
31
+ "@docusaurus/module-type-aliases": "^2.0.1",
32
+ "@docusaurus/types": "^2.0.1",
33
33
  "@types/fs-extra": "^9.0.13",
34
34
  "@types/js-yaml": "^4.0.5",
35
35
  "@types/json-pointer": "^1.0.31",
@@ -68,5 +68,5 @@
68
68
  "engines": {
69
69
  "node": ">=14"
70
70
  },
71
- "gitHead": "277586102253008f831bedbb990fb9854658a190"
71
+ "gitHead": "84ff5b3176d6734e9ec967131f45e044eca5d108"
72
72
  }
package/src/index.ts CHANGED
@@ -164,7 +164,7 @@ description: "{{{frontMatter.description}}}"
164
164
  sidebar_label: Introduction
165
165
  {{/api}}
166
166
  {{#api}}
167
- sidebar_label: {{{title}}}
167
+ sidebar_label: "{{{title}}}"
168
168
  {{/api}}
169
169
  {{^api}}
170
170
  sidebar_position: 0
@@ -191,7 +191,7 @@ info_path: {{{infoPath}}}
191
191
  id: {{{id}}}
192
192
  title: "{{{title}}}"
193
193
  description: "{{{frontMatter.description}}}"
194
- sidebar_label: {{{title}}}
194
+ sidebar_label: "{{{title}}}"
195
195
  hide_title: true
196
196
  custom_edit_url: null
197
197
  ---
@@ -26,6 +26,9 @@ export function mergeAllOf(allOf: SchemaObject[]) {
26
26
  example: function () {
27
27
  return true;
28
28
  },
29
+ "x-examples": function () {
30
+ return true;
31
+ },
29
32
  ignoreAdditionalProperties: true,
30
33
  },
31
34
  });
@@ -101,16 +104,16 @@ function createAnyOneOf(schema: SchemaObject): any {
101
104
 
102
105
  function createProperties(schema: SchemaObject) {
103
106
  const discriminator = schema.discriminator;
104
- return Object.entries(schema.properties!).map(([key, val]) =>
105
- createEdges({
107
+ return Object.entries(schema.properties!).map(([key, val]) => {
108
+ return createEdges({
106
109
  name: key,
107
110
  schema: val,
108
111
  required: Array.isArray(schema.required)
109
112
  ? schema.required.includes(key)
110
113
  : false,
111
114
  discriminator,
112
- })
113
- );
115
+ });
116
+ });
114
117
  }
115
118
 
116
119
  function createAdditionalProperties(schema: SchemaObject) {
@@ -610,6 +613,10 @@ function createEdges({
610
613
  return createDetailsNode(name, schemaName, schema, required);
611
614
  }
612
615
 
616
+ if (schema.items?.anyOf !== undefined || schema.items?.oneOf !== undefined) {
617
+ return createDetailsNode(name, schemaName, schema, required);
618
+ }
619
+
613
620
  if (schema.readOnly && schema.readOnly === true) {
614
621
  return undefined;
615
622
  }
@@ -31,6 +31,9 @@ export function mergeAllOf(allOf: SchemaObject[]) {
31
31
  example: function () {
32
32
  return true;
33
33
  },
34
+ "x-examples": function () {
35
+ return true;
36
+ },
34
37
  },
35
38
  ignoreAdditionalProperties: true,
36
39
  });
@@ -106,16 +109,16 @@ function createAnyOneOf(schema: SchemaObject): any {
106
109
 
107
110
  function createProperties(schema: SchemaObject) {
108
111
  const discriminator = schema.discriminator;
109
- return Object.entries(schema.properties!).map(([key, val]) =>
110
- createEdges({
112
+ return Object.entries(schema.properties!).map(([key, val]) => {
113
+ return createEdges({
111
114
  name: key,
112
115
  schema: val,
113
116
  required: Array.isArray(schema.required)
114
117
  ? schema.required.includes(key)
115
118
  : false,
116
119
  discriminator,
117
- })
118
- );
120
+ });
121
+ });
119
122
  }
120
123
 
121
124
  function createAdditionalProperties(schema: SchemaObject) {
@@ -610,6 +613,10 @@ function createEdges({
610
613
  return createDetailsNode(name, schemaName, schema, required);
611
614
  }
612
615
 
616
+ if (schema.items?.anyOf !== undefined || schema.items?.oneOf !== undefined) {
617
+ return createDetailsNode(name, schemaName, schema, required);
618
+ }
619
+
613
620
  if (schema.writeOnly && schema.writeOnly === true) {
614
621
  return undefined;
615
622
  }
@@ -16,6 +16,7 @@ import fs from "fs-extra";
16
16
  import cloneDeep from "lodash/cloneDeep";
17
17
  import kebabCase from "lodash/kebabCase";
18
18
  import unionBy from "lodash/unionBy";
19
+ import uniq from "lodash/uniq";
19
20
 
20
21
  import { isURL } from "../index";
21
22
  import {
@@ -85,41 +86,6 @@ function createItems(
85
86
  let items: PartialPage<ApiMetadata>[] = [];
86
87
  const infoId = kebabCase(openapiData.info.title);
87
88
 
88
- if (sidebarOptions?.categoryLinkSource === "tag") {
89
- // Only create an tag pages if categoryLinkSource set to tag.
90
- const tags: TagObject[] = openapiData.tags ?? [];
91
- // eslint-disable-next-line array-callback-return
92
- tags
93
- .filter((tag) => !tag.description?.includes("SchemaDefinition"))
94
- // eslint-disable-next-line array-callback-return
95
- .map((tag) => {
96
- const description = getTagDisplayName(
97
- tag.name!,
98
- openapiData.tags ?? []
99
- );
100
- const tagId = kebabCase(tag.name);
101
- const splitDescription = description.match(/[^\r\n]+/g);
102
- const tagPage: PartialPage<TagPageMetadata> = {
103
- type: "tag",
104
- id: tagId,
105
- unversionedId: tagId,
106
- title: description ?? "",
107
- description: description ?? "",
108
- frontMatter: {
109
- description: splitDescription
110
- ? splitDescription[0]
111
- .replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
112
- .replace(/\s+$/, "")
113
- : "",
114
- },
115
- tag: {
116
- ...tag,
117
- },
118
- };
119
- items.push(tagPage);
120
- });
121
- }
122
-
123
89
  if (openapiData.info.description) {
124
90
  // Only create an info page if we have a description.
125
91
  const infoDescription = openapiData.info?.description;
@@ -131,7 +97,9 @@ function createItems(
131
97
  type: "info",
132
98
  id: infoId,
133
99
  unversionedId: infoId,
134
- title: openapiData.info.title,
100
+ title: openapiData.info.title
101
+ ? openapiData.info.title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
102
+ : "",
135
103
  description: openapiData.info.description
136
104
  ? openapiData.info.description.replace(
137
105
  /((?:^|[^\\])(?:\\{2})*)"/g,
@@ -237,7 +205,7 @@ function createItems(
237
205
  id: baseId,
238
206
  infoId: infoId ?? "",
239
207
  unversionedId: baseId,
240
- title: title,
208
+ title: title ? title.replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'") : "",
241
209
  description: operationObject.description
242
210
  ? operationObject.description.replace(
243
211
  /((?:^|[^\\])(?:\\{2})*)"/g,
@@ -268,6 +236,52 @@ function createItems(
268
236
  }
269
237
  }
270
238
 
239
+ if (sidebarOptions?.categoryLinkSource === "tag") {
240
+ // Get global tags
241
+ const tags: TagObject[] = openapiData.tags ?? [];
242
+
243
+ // Get operation tags
244
+ const apiItems = items.filter((item) => {
245
+ return item.type === "api";
246
+ }) as ApiPageMetadata[];
247
+ const operationTags = uniq(
248
+ apiItems
249
+ .flatMap((item) => item.api.tags)
250
+ .filter((item): item is string => !!item)
251
+ );
252
+
253
+ // eslint-disable-next-line array-callback-return
254
+ tags
255
+ .filter((tag) => operationTags.includes(tag.name!)) // include only tags referenced by operation
256
+ // eslint-disable-next-line array-callback-return
257
+ .map((tag) => {
258
+ const description = getTagDisplayName(
259
+ tag.name!,
260
+ openapiData.tags ?? []
261
+ );
262
+ const tagId = kebabCase(tag.name);
263
+ const splitDescription = description.match(/[^\r\n]+/g);
264
+ const tagPage: PartialPage<TagPageMetadata> = {
265
+ type: "tag",
266
+ id: tagId,
267
+ unversionedId: tagId,
268
+ title: description ?? "",
269
+ description: description ?? "",
270
+ frontMatter: {
271
+ description: splitDescription
272
+ ? splitDescription[0]
273
+ .replace(/((?:^|[^\\])(?:\\{2})*)"/g, "$1'")
274
+ .replace(/\s+$/, "")
275
+ : "",
276
+ },
277
+ tag: {
278
+ ...tag,
279
+ },
280
+ };
281
+ items.push(tagPage);
282
+ });
283
+ }
284
+
271
285
  return items as ApiMetadata[];
272
286
  }
273
287
 
@@ -68,9 +68,13 @@ function groupByTags(
68
68
  );
69
69
 
70
70
  // Combine globally defined tags with operation tags
71
+ // Only include global tag if referenced in operation tags
71
72
  let apiTags: string[] = [];
72
73
  tags.flat().forEach((tag) => {
73
- apiTags.push(tag.name!);
74
+ // Should we also check x-displayName?
75
+ if (operationTags.includes(tag.name!)) {
76
+ apiTags.push(tag.name!);
77
+ }
74
78
  });
75
79
  apiTags = uniq(apiTags.concat(operationTags));
76
80