@docusaurus/plugin-content-docs 2.0.0-beta.1 → 2.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/categoryGeneratedIndex.d.ts +12 -0
  3. package/lib/categoryGeneratedIndex.js +37 -0
  4. package/lib/cli.d.ts +2 -2
  5. package/lib/cli.js +12 -34
  6. package/lib/client/docsClientUtils.d.ts +1 -4
  7. package/lib/client/docsClientUtils.js +21 -31
  8. package/lib/docFrontMatter.d.ts +1 -1
  9. package/lib/docFrontMatter.js +10 -6
  10. package/lib/docs.d.ts +25 -3
  11. package/lib/docs.js +125 -38
  12. package/lib/globalData.d.ts +1 -1
  13. package/lib/index.d.ts +1 -1
  14. package/lib/index.js +104 -138
  15. package/lib/lastUpdate.js +9 -10
  16. package/lib/markdown/index.d.ts +3 -6
  17. package/lib/markdown/index.js +3 -3
  18. package/lib/markdown/linkify.js +2 -2
  19. package/lib/numberPrefix.d.ts +1 -1
  20. package/lib/options.d.ts +3 -3
  21. package/lib/options.js +49 -17
  22. package/lib/props.d.ts +7 -2
  23. package/lib/props.js +61 -9
  24. package/lib/routes.d.ts +27 -0
  25. package/lib/routes.js +105 -0
  26. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +5 -2
  27. package/lib/sidebars/generator.js +216 -0
  28. package/lib/sidebars/index.d.ts +15 -0
  29. package/lib/sidebars/index.js +73 -0
  30. package/lib/sidebars/normalization.d.ts +14 -0
  31. package/lib/sidebars/normalization.js +77 -0
  32. package/lib/sidebars/processor.d.ts +18 -0
  33. package/lib/sidebars/processor.js +85 -0
  34. package/lib/sidebars/types.d.ts +127 -0
  35. package/lib/sidebars/types.js +8 -0
  36. package/lib/sidebars/utils.d.ts +35 -0
  37. package/lib/sidebars/utils.js +228 -0
  38. package/lib/sidebars/validation.d.ts +10 -0
  39. package/lib/sidebars/validation.js +138 -0
  40. package/lib/slug.d.ts +4 -3
  41. package/lib/slug.js +27 -15
  42. package/lib/tags.d.ts +8 -0
  43. package/lib/tags.js +20 -0
  44. package/lib/theme/hooks/useDocs.js +24 -21
  45. package/lib/translations.d.ts +2 -2
  46. package/lib/translations.js +71 -29
  47. package/lib/types.d.ts +52 -62
  48. package/lib/versions.d.ts +3 -3
  49. package/lib/versions.js +76 -24
  50. package/package.json +22 -20
  51. package/src/__tests__/__fixtures__/simple-site/docs/_partials/somePartial.md +3 -0
  52. package/src/__tests__/__fixtures__/simple-site/docs/_partials/subfolder/somePartial.md +3 -0
  53. package/src/__tests__/__fixtures__/simple-site/docs/_somePartial.md +3 -0
  54. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +5 -0
  55. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +2 -0
  56. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +2 -0
  57. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +2 -0
  58. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +2 -0
  59. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +2 -0
  60. package/src/__tests__/__fixtures__/simple-site/sidebars.json +15 -1
  61. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-1.md +1 -0
  62. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +6 -0
  63. package/src/__tests__/__fixtures__/versioned-site/docs/hello.md +3 -0
  64. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md +3 -0
  65. package/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md +3 -0
  66. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/hello.md +3 -0
  67. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/somePartial.md +3 -0
  68. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/subfolder/somePartial.md +3 -0
  69. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_somePartial.md +3 -0
  70. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/hello.md +3 -0
  71. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.1-sidebars.json +2 -2
  72. package/src/__tests__/__snapshots__/cli.test.ts.snap +48 -73
  73. package/src/__tests__/__snapshots__/docs.test.ts.snap +140 -0
  74. package/src/__tests__/__snapshots__/index.test.ts.snap +753 -112
  75. package/src/__tests__/__snapshots__/translations.test.ts.snap +45 -18
  76. package/src/__tests__/cli.test.ts +15 -11
  77. package/src/__tests__/docFrontMatter.test.ts +195 -40
  78. package/src/__tests__/docs.test.ts +311 -150
  79. package/src/__tests__/index.test.ts +112 -69
  80. package/src/__tests__/lastUpdate.test.ts +3 -2
  81. package/src/__tests__/options.test.ts +48 -4
  82. package/src/__tests__/props.test.ts +62 -0
  83. package/src/__tests__/slug.test.ts +127 -20
  84. package/src/__tests__/translations.test.ts +7 -2
  85. package/src/__tests__/versions.test.ts +93 -67
  86. package/src/categoryGeneratedIndex.ts +57 -0
  87. package/src/cli.ts +8 -41
  88. package/src/client/__tests__/docsClientUtils.test.ts +4 -5
  89. package/src/client/docsClientUtils.ts +19 -41
  90. package/{types.d.ts → src/deps.d.ts} +0 -0
  91. package/src/docFrontMatter.ts +13 -7
  92. package/src/docs.ts +158 -29
  93. package/src/globalData.ts +6 -1
  94. package/src/index.ts +134 -179
  95. package/src/lastUpdate.ts +10 -9
  96. package/src/markdown/index.ts +8 -12
  97. package/src/numberPrefix.ts +5 -3
  98. package/src/options.ts +59 -28
  99. package/src/plugin-content-docs.d.ts +179 -35
  100. package/src/props.ts +91 -16
  101. package/src/routes.ts +173 -0
  102. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -0
  103. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -0
  104. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -0
  105. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category.js +0 -0
  106. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -0
  107. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed.json +0 -0
  108. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -0
  109. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-first-level-not-category.js +0 -0
  110. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -0
  111. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -0
  112. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link.json +0 -0
  113. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-unknown-type.json +0 -0
  114. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-wrong-field.json +0 -0
  115. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars.json +0 -0
  116. package/src/{__tests__/__snapshots__/sidebars.test.ts.snap → sidebars/__tests__/__snapshots__/index.test.ts.snap} +36 -6
  117. package/src/{__tests__/sidebarItemsGenerator.test.ts → sidebars/__tests__/generator.test.ts} +143 -18
  118. package/src/sidebars/__tests__/index.test.ts +204 -0
  119. package/src/sidebars/__tests__/processor.test.ts +237 -0
  120. package/src/sidebars/__tests__/utils.test.ts +695 -0
  121. package/src/sidebars/__tests__/validation.test.ts +105 -0
  122. package/src/sidebars/generator.ts +310 -0
  123. package/src/sidebars/index.ts +94 -0
  124. package/src/sidebars/normalization.ts +112 -0
  125. package/src/sidebars/processor.ts +154 -0
  126. package/src/sidebars/types.ts +211 -0
  127. package/src/sidebars/utils.ts +329 -0
  128. package/src/sidebars/validation.ts +168 -0
  129. package/src/slug.ts +32 -17
  130. package/src/tags.ts +19 -0
  131. package/src/theme/hooks/useDocs.ts +5 -1
  132. package/src/translations.ts +103 -47
  133. package/src/types.ts +67 -105
  134. package/src/versions.ts +117 -21
  135. package/lib/sidebarItemsGenerator.js +0 -211
  136. package/lib/sidebars.d.ts +0 -43
  137. package/lib/sidebars.js +0 -319
  138. package/src/__tests__/sidebars.test.ts +0 -639
  139. package/src/sidebarItemsGenerator.ts +0 -307
  140. package/src/sidebars.ts +0 -506
package/src/types.ts CHANGED
@@ -5,14 +5,15 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- // eslint-disable-next-line spaced-comment
9
8
  /// <reference types="@docusaurus/module-type-aliases" />
10
9
 
11
10
  import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
12
- import {
11
+ import type {Tag, FrontMatterTag, Slugger} from '@docusaurus/utils';
12
+ import type {
13
13
  BrokenMarkdownLink as IBrokenMarkdownLink,
14
14
  ContentPaths,
15
15
  } from '@docusaurus/utils/lib/markdownLinks';
16
+ import type {SidebarItemsGeneratorOption, Sidebars} from './sidebars/types';
16
17
 
17
18
  export type DocFile = {
18
19
  contentPath: string; // /!\ may be localized
@@ -28,8 +29,12 @@ export type VersionMetadata = ContentPaths & {
28
29
  versionName: VersionName; // 1.0.0
29
30
  versionLabel: string; // Version 1.0.0
30
31
  versionPath: string; // /baseUrl/docs/1.0.0
32
+ tagsPath: string;
31
33
  versionEditUrl?: string | undefined;
32
34
  versionEditUrlLocalized?: string | undefined;
35
+ versionBanner: VersionBanner | null;
36
+ versionBadge: boolean;
37
+ versionClassName: string;
33
38
  isLast: boolean;
34
39
  sidebarFilePath: string | false | undefined; // versioned_sidebars/1.0.0.json
35
40
  routePriority: number | undefined; // -1 for the latest docs
@@ -45,7 +50,6 @@ export type EditUrlFunction = (editUrlParams: {
45
50
 
46
51
  export type MetadataOptions = {
47
52
  routeBasePath: string;
48
- homePageId?: string;
49
53
  editUrl?: string | EditUrlFunction;
50
54
  editCurrentVersion: boolean;
51
55
  editLocalizedFiles: boolean;
@@ -59,9 +63,15 @@ export type PathOptions = {
59
63
  sidebarPath?: string | false | undefined;
60
64
  };
61
65
 
66
+ // TODO support custom version banner? {type: "error", content: "html content"}
67
+ export type VersionBanner = 'unreleased' | 'unmaintained';
68
+
62
69
  export type VersionOptions = {
63
70
  path?: string;
64
71
  label?: string;
72
+ banner?: 'none' | VersionBanner;
73
+ badge?: boolean;
74
+ className?: string;
65
75
  };
66
76
 
67
77
  export type VersionsOptions = {
@@ -70,111 +80,36 @@ export type VersionsOptions = {
70
80
  onlyIncludeVersions?: string[];
71
81
  };
72
82
 
83
+ export type SidebarOptions = {
84
+ sidebarCollapsible: boolean;
85
+ sidebarCollapsed: boolean;
86
+ };
87
+
88
+ export type NormalizeSidebarsParams = SidebarOptions & {
89
+ version: VersionMetadata;
90
+ categoryLabelSlugger: Slugger;
91
+ };
92
+
73
93
  export type PluginOptions = MetadataOptions &
74
94
  PathOptions &
75
95
  VersionsOptions &
76
- RemarkAndRehypePluginOptions & {
96
+ RemarkAndRehypePluginOptions &
97
+ SidebarOptions & {
77
98
  id: string;
78
99
  include: string[];
100
+ exclude: string[];
79
101
  docLayoutComponent: string;
80
102
  docItemComponent: string;
103
+ docTagDocListComponent: string;
104
+ docTagsListComponent: string;
105
+ docCategoryGeneratedIndexComponent: string;
81
106
  admonitions: Record<string, unknown>;
82
107
  disableVersioning: boolean;
83
- excludeNextVersionDocs?: boolean;
84
108
  includeCurrentVersion: boolean;
85
109
  sidebarItemsGenerator: SidebarItemsGeneratorOption;
110
+ tagsBasePath: string;
86
111
  };
87
112
 
88
- export type SidebarItemBase = {
89
- customProps?: Record<string, unknown>;
90
- };
91
-
92
- export type SidebarItemDoc = SidebarItemBase & {
93
- type: 'doc' | 'ref';
94
- label?: string;
95
- id: string;
96
- };
97
-
98
- export type SidebarItemLink = SidebarItemBase & {
99
- type: 'link';
100
- href: string;
101
- label: string;
102
- };
103
-
104
- export type SidebarItemCategory = SidebarItemBase & {
105
- type: 'category';
106
- label: string;
107
- items: SidebarItem[];
108
- collapsed: boolean;
109
- };
110
-
111
- export type UnprocessedSidebarItemAutogenerated = {
112
- type: 'autogenerated';
113
- dirName: string;
114
- };
115
-
116
- export type UnprocessedSidebarItemCategory = SidebarItemBase & {
117
- type: 'category';
118
- label: string;
119
- items: UnprocessedSidebarItem[];
120
- collapsed: boolean;
121
- };
122
-
123
- export type UnprocessedSidebarItem =
124
- | SidebarItemDoc
125
- | SidebarItemLink
126
- | UnprocessedSidebarItemCategory
127
- | UnprocessedSidebarItemAutogenerated;
128
-
129
- export type UnprocessedSidebar = UnprocessedSidebarItem[];
130
- export type UnprocessedSidebars = Record<string, UnprocessedSidebar>;
131
-
132
- export type SidebarItem =
133
- | SidebarItemDoc
134
- | SidebarItemLink
135
- | SidebarItemCategory;
136
-
137
- export type Sidebar = SidebarItem[];
138
- export type SidebarItemType = SidebarItem['type'];
139
- export type Sidebars = Record<string, Sidebar>;
140
-
141
- // Reduce API surface for options.sidebarItemsGenerator
142
- // The user-provided generator fn should receive only a subset of metadatas
143
- // A change to any of these metadatas can be considered as a breaking change
144
- export type SidebarItemsGeneratorDoc = Pick<
145
- DocMetadataBase,
146
- 'id' | 'frontMatter' | 'source' | 'sourceDirName' | 'sidebarPosition'
147
- >;
148
- export type SidebarItemsGeneratorVersion = Pick<
149
- VersionMetadata,
150
- 'versionName' | 'contentPath'
151
- >;
152
-
153
- export type SidebarItemsGeneratorArgs = {
154
- item: UnprocessedSidebarItemAutogenerated;
155
- version: SidebarItemsGeneratorVersion;
156
- docs: SidebarItemsGeneratorDoc[];
157
- numberPrefixParser: NumberPrefixParser;
158
- };
159
- export type SidebarItemsGenerator = (
160
- generatorArgs: SidebarItemsGeneratorArgs,
161
- ) => Promise<SidebarItem[]>;
162
-
163
- // Also inject the default generator to conveniently wrap/enhance/sort the default sidebar gen logic
164
- // see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
165
- export type SidebarItemsGeneratorOptionArgs = {
166
- defaultSidebarItemsGenerator: SidebarItemsGenerator;
167
- } & SidebarItemsGeneratorArgs;
168
- export type SidebarItemsGeneratorOption = (
169
- generatorArgs: SidebarItemsGeneratorOptionArgs,
170
- ) => Promise<SidebarItem[]>;
171
-
172
- export type OrderMetadata = {
173
- previous?: string;
174
- next?: string;
175
- sidebar?: string;
176
- };
177
-
178
113
  export type LastUpdateData = {
179
114
  lastUpdatedAt?: number;
180
115
  formattedLastUpdatedAt?: string;
@@ -182,8 +117,10 @@ export type LastUpdateData = {
182
117
  };
183
118
 
184
119
  export type DocFrontMatter = {
120
+ // Front matter uses snake case
185
121
  id?: string;
186
122
  title?: string;
123
+ tags?: FrontMatterTag[];
187
124
  hide_title?: boolean;
188
125
  hide_table_of_contents?: boolean;
189
126
  keywords?: string[];
@@ -192,25 +129,29 @@ export type DocFrontMatter = {
192
129
  slug?: string;
193
130
  sidebar_label?: string;
194
131
  sidebar_position?: number;
132
+ sidebar_class_name?: string;
195
133
  pagination_label?: string;
196
134
  custom_edit_url?: string | null;
197
135
  parse_number_prefixes?: boolean;
136
+ toc_min_heading_level?: number;
137
+ toc_max_heading_level?: number;
138
+ pagination_next?: string | null;
139
+ pagination_prev?: string | null;
198
140
  };
199
141
 
200
142
  export type DocMetadataBase = LastUpdateData & {
143
+ id: string; // TODO legacy versioned id => try to remove
144
+ unversionedId: string; // TODO new unversioned id => try to rename to "id"
201
145
  version: VersionName;
202
- unversionedId: string;
203
- id: string;
204
- isDocsHomePage: boolean;
205
146
  title: string;
206
147
  description: string;
207
- source: string;
208
- sourceDirName: string; // relative to the docs folder (can be ".")
148
+ source: string; // @site aliased source => "@site/docs/folder/subFolder/subSubFolder/myDoc.md"
149
+ sourceDirName: string; // relative to the versioned docs folder (can be ".") => "folder/subFolder/subSubFolder"
209
150
  slug: string;
210
151
  permalink: string;
211
- // eslint-disable-next-line camelcase
212
152
  sidebarPosition?: number;
213
153
  editUrl?: string | null;
154
+ tags: Tag[];
214
155
  frontMatter: DocFrontMatter & Record<string, unknown>;
215
156
  };
216
157
 
@@ -225,15 +166,35 @@ export type DocMetadata = DocMetadataBase & {
225
166
  next?: DocNavLink;
226
167
  };
227
168
 
169
+ export type CategoryGeneratedIndexMetadata = {
170
+ title: string;
171
+ description?: string;
172
+ slug: string;
173
+ permalink: string;
174
+ sidebar: string;
175
+ previous?: DocNavLink;
176
+ next?: DocNavLink;
177
+ };
178
+
228
179
  export type SourceToPermalink = {
229
180
  [source: string]: string;
230
181
  };
182
+
183
+ export type VersionTag = {
184
+ name: string; // normalized name/label of the tag
185
+ docIds: string[]; // all doc ids having this tag
186
+ permalink: string; // pathname of the tag
187
+ };
188
+ export type VersionTags = {
189
+ [key: string]: VersionTag;
190
+ };
191
+
231
192
  export type LoadedVersion = VersionMetadata & {
232
193
  versionPath: string;
233
194
  mainDocId: string;
234
195
  docs: DocMetadata[];
235
196
  sidebars: Sidebars;
236
- permalinkToSidebar: Record<string, string>;
197
+ categoryGeneratedIndices: CategoryGeneratedIndexMetadata[];
237
198
  };
238
199
 
239
200
  export type LoadedContent = {
@@ -269,6 +230,7 @@ export type DocsMarkdownOption = {
269
230
  onBrokenMarkdownLink: (brokenMarkdownLink: BrokenMarkdownLink) => void;
270
231
  };
271
232
 
272
- export type NumberPrefixParser = (
273
- filename: string,
274
- ) => {filename: string; numberPrefix?: number};
233
+ export type NumberPrefixParser = (filename: string) => {
234
+ filename: string;
235
+ numberPrefix?: number;
236
+ };
package/src/versions.ts CHANGED
@@ -7,8 +7,9 @@
7
7
 
8
8
  import path from 'path';
9
9
  import fs from 'fs-extra';
10
- import {
10
+ import type {
11
11
  PluginOptions,
12
+ VersionBanner,
12
13
  VersionMetadata,
13
14
  VersionOptions,
14
15
  VersionsOptions,
@@ -20,9 +21,13 @@ import {
20
21
  CURRENT_VERSION_NAME,
21
22
  } from './constants';
22
23
 
23
- import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
24
- import {LoadContext} from '@docusaurus/types';
25
- import {getPluginI18nPath, normalizeUrl, posixPath} from '@docusaurus/utils';
24
+ import type {LoadContext} from '@docusaurus/types';
25
+ import {
26
+ getPluginI18nPath,
27
+ normalizeUrl,
28
+ posixPath,
29
+ DEFAULT_PLUGIN_ID,
30
+ } from '@docusaurus/utils';
26
31
  import {difference} from 'lodash';
27
32
  import {resolveSidebarPathOption} from './sidebars';
28
33
 
@@ -255,14 +260,92 @@ function getVersionEditUrls({
255
260
  };
256
261
  }
257
262
 
263
+ function getDefaultVersionBanner({
264
+ versionName,
265
+ versionNames,
266
+ lastVersionName,
267
+ }: {
268
+ versionName: string;
269
+ versionNames: string[];
270
+ lastVersionName: string;
271
+ }): VersionBanner | null {
272
+ // Current version: good, no banner
273
+ if (versionName === lastVersionName) {
274
+ return null;
275
+ }
276
+ // Upcoming versions: unreleased banner
277
+ else if (
278
+ versionNames.indexOf(versionName) < versionNames.indexOf(lastVersionName)
279
+ ) {
280
+ return 'unreleased';
281
+ }
282
+ // Older versions: display unmaintained banner
283
+ else {
284
+ return 'unmaintained';
285
+ }
286
+ }
287
+
288
+ function getVersionBanner({
289
+ versionName,
290
+ versionNames,
291
+ lastVersionName,
292
+ options,
293
+ }: {
294
+ versionName: string;
295
+ versionNames: string[];
296
+ lastVersionName: string;
297
+ options: Pick<PluginOptions, 'versions'>;
298
+ }): VersionBanner | null {
299
+ const versionBannerOption = options.versions[versionName]?.banner;
300
+ if (versionBannerOption) {
301
+ return versionBannerOption === 'none' ? null : versionBannerOption;
302
+ }
303
+ return getDefaultVersionBanner({
304
+ versionName,
305
+ versionNames,
306
+ lastVersionName,
307
+ });
308
+ }
309
+
310
+ function getVersionBadge({
311
+ versionName,
312
+ versionNames,
313
+ options,
314
+ }: {
315
+ versionName: string;
316
+ versionNames: string[];
317
+ options: Pick<PluginOptions, 'versions'>;
318
+ }): boolean {
319
+ const versionBadgeOption = options.versions[versionName]?.badge;
320
+ // If site is not versioned or only one version is included
321
+ // we don't show the version badge by default
322
+ // See https://github.com/facebook/docusaurus/issues/3362
323
+ const versionBadgeDefault = versionNames.length !== 1;
324
+ return versionBadgeOption ?? versionBadgeDefault;
325
+ }
326
+
327
+ function getVersionClassName({
328
+ versionName,
329
+ options,
330
+ }: {
331
+ versionName: string;
332
+ options: Pick<PluginOptions, 'versions'>;
333
+ }): string {
334
+ const versionClassNameOption = options.versions[versionName]?.className;
335
+ const versionClassNameDefault = `docs-version-${versionName}`;
336
+ return versionClassNameOption ?? versionClassNameDefault;
337
+ }
338
+
258
339
  function createVersionMetadata({
259
340
  versionName,
260
- isLast,
341
+ versionNames,
342
+ lastVersionName,
261
343
  context,
262
344
  options,
263
345
  }: {
264
346
  versionName: string;
265
- isLast: boolean;
347
+ versionNames: string[];
348
+ lastVersionName: string;
266
349
  context: Pick<LoadContext, 'siteDir' | 'baseUrl' | 'i18n'>;
267
350
  options: Pick<
268
351
  PluginOptions,
@@ -270,29 +353,27 @@ function createVersionMetadata({
270
353
  | 'path'
271
354
  | 'sidebarPath'
272
355
  | 'routeBasePath'
356
+ | 'tagsBasePath'
273
357
  | 'versions'
274
358
  | 'editUrl'
275
359
  | 'editCurrentVersion'
276
360
  >;
277
361
  }): VersionMetadata {
278
- const {
279
- sidebarFilePath,
280
- contentPath,
281
- contentPathLocalized,
282
- } = getVersionMetadataPaths({
283
- versionName,
284
- context,
285
- options,
286
- });
362
+ const {sidebarFilePath, contentPath, contentPathLocalized} =
363
+ getVersionMetadataPaths({versionName, context, options});
364
+
365
+ const isLast = versionName === lastVersionName;
287
366
 
288
367
  // retro-compatible values
289
368
  const defaultVersionLabel =
290
369
  versionName === CURRENT_VERSION_NAME ? 'Next' : versionName;
291
- const defaultVersionPathPart = isLast
292
- ? ''
293
- : versionName === CURRENT_VERSION_NAME
294
- ? 'next'
295
- : versionName;
370
+ function getDefaultVersionPathPart() {
371
+ if (isLast) {
372
+ return '';
373
+ }
374
+ return versionName === CURRENT_VERSION_NAME ? 'next' : versionName;
375
+ }
376
+ const defaultVersionPathPart = getDefaultVersionPathPart();
296
377
 
297
378
  const versionOptions: VersionOptions = options.versions[versionName] ?? {};
298
379
 
@@ -315,12 +396,25 @@ function createVersionMetadata({
315
396
  // Because /docs/:route` should always be after `/docs/versionName/:route`.
316
397
  const routePriority = versionPathPart === '' ? -1 : undefined;
317
398
 
399
+ // the path that will be used to refer the docs tags
400
+ // example below will be using /docs/tags
401
+ const tagsPath = normalizeUrl([versionPath, options.tagsBasePath]);
402
+
318
403
  return {
319
404
  versionName,
320
405
  versionLabel,
321
406
  versionPath,
407
+ tagsPath,
322
408
  versionEditUrl: versionEditUrls?.versionEditUrl,
323
409
  versionEditUrlLocalized: versionEditUrls?.versionEditUrlLocalized,
410
+ versionBanner: getVersionBanner({
411
+ versionName,
412
+ versionNames,
413
+ lastVersionName,
414
+ options,
415
+ }),
416
+ versionBadge: getVersionBadge({versionName, versionNames, options}),
417
+ versionClassName: getVersionClassName({versionName, options}),
324
418
  isLast,
325
419
  routePriority,
326
420
  sidebarFilePath,
@@ -465,6 +559,7 @@ export function readVersionsMetadata({
465
559
  | 'path'
466
560
  | 'sidebarPath'
467
561
  | 'routeBasePath'
562
+ | 'tagsBasePath'
468
563
  | 'includeCurrentVersion'
469
564
  | 'disableVersioning'
470
565
  | 'lastVersion'
@@ -486,7 +581,8 @@ export function readVersionsMetadata({
486
581
  const versionsMetadata = versionNames.map((versionName) =>
487
582
  createVersionMetadata({
488
583
  versionName,
489
- isLast: versionName === lastVersionName,
584
+ versionNames,
585
+ lastVersionName,
490
586
  context,
491
587
  options,
492
588
  }),
@@ -1,211 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) Facebook, Inc. and its affiliates.
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.DefaultSidebarItemsGenerator = exports.CategoryMetadataFilenamePattern = exports.CategoryMetadataFilenameBase = void 0;
10
- const tslib_1 = require("tslib");
11
- const lodash_1 = require("lodash");
12
- const utils_1 = require("@docusaurus/utils");
13
- const utils_validation_1 = require("@docusaurus/utils-validation");
14
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
15
- const path_1 = tslib_1.__importDefault(require("path"));
16
- const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
17
- const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
18
- const sidebars_1 = require("./sidebars");
19
- const BreadcrumbSeparator = '/';
20
- exports.CategoryMetadataFilenameBase = '_category_';
21
- exports.CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
22
- const CategoryMetadatasFileSchema = utils_validation_1.Joi.object({
23
- label: utils_validation_1.Joi.string(),
24
- position: utils_validation_1.Joi.number(),
25
- collapsed: utils_validation_1.Joi.boolean(),
26
- });
27
- // TODO I now believe we should read all the category metadata files ahead of time: we may need this metadata to customize docs metadata
28
- // Example use-case being able to disable number prefix parsing at the folder level, or customize the default route path segment for an intermediate directory...
29
- // TODO later if there is `CategoryFolder/index.md`, we may want to read the metadata as yaml on it
30
- // see https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
31
- async function readCategoryMetadatasFile(categoryDirPath) {
32
- var _a, _b;
33
- function validateCategoryMetadataFile(content) {
34
- return utils_validation_1.Joi.attempt(content, CategoryMetadatasFileSchema);
35
- }
36
- async function tryReadFile(fileNameWithExtension, parse) {
37
- // Simpler to use only posix paths for mocking file metadatas in tests
38
- const filePath = utils_1.posixPath(path_1.default.join(categoryDirPath, fileNameWithExtension));
39
- if (await fs_extra_1.default.pathExists(filePath)) {
40
- const contentString = await fs_extra_1.default.readFile(filePath, { encoding: 'utf8' });
41
- const unsafeContent = parse(contentString);
42
- try {
43
- return validateCategoryMetadataFile(unsafeContent);
44
- }
45
- catch (e) {
46
- console.error(chalk_1.default.red(`The docs sidebar category metadata file looks invalid!\nPath: ${filePath}`));
47
- throw e;
48
- }
49
- }
50
- return null;
51
- }
52
- return ((_b = (_a = (await tryReadFile(`${exports.CategoryMetadataFilenameBase}.json`, JSON.parse))) !== null && _a !== void 0 ? _a : (await tryReadFile(`${exports.CategoryMetadataFilenameBase}.yml`, js_yaml_1.default.load))) !== null && _b !== void 0 ? _b :
53
- // eslint-disable-next-line no-return-await
54
- (await tryReadFile(`${exports.CategoryMetadataFilenameBase}.yaml`, js_yaml_1.default.load)));
55
- }
56
- // [...parents, tail]
57
- function parseBreadcrumb(breadcrumb) {
58
- return {
59
- parents: lodash_1.take(breadcrumb, breadcrumb.length - 1),
60
- tail: lodash_1.last(breadcrumb),
61
- };
62
- }
63
- // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
64
- const DefaultSidebarItemsGenerator = async function defaultSidebarItemsGenerator({ item, docs: allDocs, version, numberPrefixParser, }) {
65
- // Doc at the root of the autogenerated sidebar dir
66
- function isRootDoc(doc) {
67
- return doc.sourceDirName === item.dirName;
68
- }
69
- // Doc inside a subfolder of the autogenerated sidebar dir
70
- function isCategoryDoc(doc) {
71
- if (isRootDoc(doc)) {
72
- return false;
73
- }
74
- return (
75
- // autogen dir is . and doc is in subfolder
76
- item.dirName === '.' ||
77
- // autogen dir is not . and doc is in subfolder
78
- // "api/myDoc" startsWith "api/" (note "api2/myDoc" is not included)
79
- doc.sourceDirName.startsWith(utils_1.addTrailingSlash(item.dirName)));
80
- }
81
- function isInAutogeneratedDir(doc) {
82
- return isRootDoc(doc) || isCategoryDoc(doc);
83
- }
84
- // autogenDir=a/b and docDir=a/b/c/d => returns c/d
85
- // autogenDir=a/b and docDir=a/b => returns .
86
- function getDocDirRelativeToAutogenDir(doc) {
87
- if (!isInAutogeneratedDir(doc)) {
88
- throw new Error('getDocDirRelativeToAutogenDir() can only be called for subdocs of the sidebar autogen dir.');
89
- }
90
- // Is there a node API to compare 2 relative paths more easily?
91
- // path.relative() does not give good results
92
- if (item.dirName === '.') {
93
- return doc.sourceDirName;
94
- }
95
- else if (item.dirName === doc.sourceDirName) {
96
- return '.';
97
- }
98
- else {
99
- return doc.sourceDirName.replace(utils_1.addTrailingSlash(item.dirName), '');
100
- }
101
- }
102
- // Get only docs in the autogen dir
103
- // Sort by folder+filename at once
104
- const docs = lodash_1.sortBy(allDocs.filter(isInAutogeneratedDir), (d) => d.source);
105
- if (docs.length === 0) {
106
- console.warn(chalk_1.default.yellow(`No docs found in dir ${item.dirName}: can't auto-generate a sidebar.`));
107
- }
108
- function createDocSidebarItem(doc) {
109
- return {
110
- type: 'doc',
111
- id: doc.id,
112
- ...(doc.frontMatter.sidebar_label && {
113
- label: doc.frontMatter.sidebar_label,
114
- }),
115
- ...(typeof doc.sidebarPosition !== 'undefined' && {
116
- position: doc.sidebarPosition,
117
- }),
118
- };
119
- }
120
- async function createCategorySidebarItem({ breadcrumb, }) {
121
- var _a, _b, _c;
122
- const categoryDirPath = path_1.default.join(version.contentPath, item.dirName, // fix https://github.com/facebook/docusaurus/issues/4638
123
- breadcrumb.join(BreadcrumbSeparator));
124
- const categoryMetadatas = await readCategoryMetadatasFile(categoryDirPath);
125
- const { tail } = parseBreadcrumb(breadcrumb);
126
- const { filename, numberPrefix } = numberPrefixParser(tail);
127
- const position = (_a = categoryMetadatas === null || categoryMetadatas === void 0 ? void 0 : categoryMetadatas.position) !== null && _a !== void 0 ? _a : numberPrefix;
128
- return {
129
- type: 'category',
130
- label: (_b = categoryMetadatas === null || categoryMetadatas === void 0 ? void 0 : categoryMetadatas.label) !== null && _b !== void 0 ? _b : filename,
131
- items: [],
132
- collapsed: (_c = categoryMetadatas === null || categoryMetadatas === void 0 ? void 0 : categoryMetadatas.collapsed) !== null && _c !== void 0 ? _c : sidebars_1.DefaultCategoryCollapsedValue,
133
- ...(typeof position !== 'undefined' && { position }),
134
- };
135
- }
136
- // Not sure how to simplify this algorithm :/
137
- async function autogenerateSidebarItems() {
138
- const sidebarItems = []; // mutable result
139
- const categoriesByBreadcrumb = {}; // mutable cache of categories already created
140
- async function getOrCreateCategoriesForBreadcrumb(breadcrumb) {
141
- if (breadcrumb.length === 0) {
142
- return null;
143
- }
144
- const { parents } = parseBreadcrumb(breadcrumb);
145
- const parentCategory = await getOrCreateCategoriesForBreadcrumb(parents);
146
- const existingCategory = categoriesByBreadcrumb[breadcrumb.join(BreadcrumbSeparator)];
147
- if (existingCategory) {
148
- return existingCategory;
149
- }
150
- else {
151
- const newCategory = await createCategorySidebarItem({
152
- breadcrumb,
153
- });
154
- if (parentCategory) {
155
- parentCategory.items.push(newCategory);
156
- }
157
- else {
158
- sidebarItems.push(newCategory);
159
- }
160
- categoriesByBreadcrumb[breadcrumb.join(BreadcrumbSeparator)] = newCategory;
161
- return newCategory;
162
- }
163
- }
164
- // Get the category breadcrumb of a doc (relative to the dir of the autogenerated sidebar item)
165
- function getRelativeBreadcrumb(doc) {
166
- const relativeDirPath = getDocDirRelativeToAutogenDir(doc);
167
- if (relativeDirPath === '.') {
168
- return [];
169
- }
170
- else {
171
- return relativeDirPath.split(BreadcrumbSeparator);
172
- }
173
- }
174
- async function handleDocItem(doc) {
175
- const breadcrumb = getRelativeBreadcrumb(doc);
176
- const category = await getOrCreateCategoriesForBreadcrumb(breadcrumb);
177
- const docSidebarItem = createDocSidebarItem(doc);
178
- if (category) {
179
- category.items.push(docSidebarItem);
180
- }
181
- else {
182
- sidebarItems.push(docSidebarItem);
183
- }
184
- }
185
- // async process made sequential on purpose! order matters
186
- for (const doc of docs) {
187
- // eslint-disable-next-line no-await-in-loop
188
- await handleDocItem(doc);
189
- }
190
- return sidebarItems;
191
- }
192
- const sidebarItems = await autogenerateSidebarItems();
193
- return sortSidebarItems(sidebarItems);
194
- };
195
- exports.DefaultSidebarItemsGenerator = DefaultSidebarItemsGenerator;
196
- // Recursively sort the categories/docs + remove the "position" attribute from final output
197
- // Note: the "position" is only used to sort "inside" a sidebar slice
198
- // It is not used to sort across multiple consecutive sidebar slices (ie a whole Category composed of multiple autogenerated items)
199
- function sortSidebarItems(sidebarItems) {
200
- const processedSidebarItems = sidebarItems.map((item) => {
201
- if (item.type === 'category') {
202
- return {
203
- ...item,
204
- items: sortSidebarItems(item.items),
205
- };
206
- }
207
- return item;
208
- });
209
- const sortedSidebarItems = lodash_1.orderBy(processedSidebarItems, (item) => item.position, ['asc']);
210
- return sortedSidebarItems.map(({ position: _removed, ...item }) => item);
211
- }