@docusaurus/plugin-content-docs 2.0.0-beta.8bda3b2db → 2.0.0-beta.9

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 (117) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/cli.d.ts +2 -2
  3. package/lib/cli.js +20 -24
  4. package/lib/client/docsClientUtils.d.ts +1 -4
  5. package/lib/client/docsClientUtils.js +12 -16
  6. package/lib/docFrontMatter.js +7 -3
  7. package/lib/docs.d.ts +4 -2
  8. package/lib/docs.js +77 -23
  9. package/lib/index.js +88 -94
  10. package/lib/lastUpdate.js +8 -8
  11. package/lib/markdown/index.d.ts +3 -6
  12. package/lib/markdown/index.js +3 -3
  13. package/lib/markdown/linkify.js +2 -2
  14. package/lib/options.d.ts +1 -1
  15. package/lib/options.js +39 -11
  16. package/lib/props.d.ts +7 -2
  17. package/lib/props.js +27 -4
  18. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +3 -1
  19. package/lib/sidebars/generator.js +174 -0
  20. package/lib/sidebars/index.d.ts +14 -0
  21. package/lib/sidebars/index.js +64 -0
  22. package/lib/sidebars/normalization.d.ts +9 -0
  23. package/lib/sidebars/normalization.js +58 -0
  24. package/lib/sidebars/processor.d.ts +16 -0
  25. package/lib/sidebars/processor.js +70 -0
  26. package/lib/sidebars/types.d.ts +87 -0
  27. package/lib/sidebars/types.js +13 -0
  28. package/lib/sidebars/utils.d.ts +22 -0
  29. package/lib/sidebars/utils.js +101 -0
  30. package/lib/sidebars/validation.d.ts +8 -0
  31. package/lib/sidebars/validation.js +102 -0
  32. package/lib/slug.js +4 -4
  33. package/lib/tags.d.ts +8 -0
  34. package/lib/tags.js +22 -0
  35. package/lib/theme/hooks/useDocs.js +24 -21
  36. package/lib/translations.d.ts +1 -1
  37. package/lib/translations.js +13 -13
  38. package/lib/types.d.ts +35 -58
  39. package/lib/versions.d.ts +1 -1
  40. package/lib/versions.js +75 -22
  41. package/package.json +15 -14
  42. package/src/__tests__/__fixtures__/simple-site/docs/_partials/somePartial.md +3 -0
  43. package/src/__tests__/__fixtures__/simple-site/docs/_partials/subfolder/somePartial.md +3 -0
  44. package/src/__tests__/__fixtures__/simple-site/docs/_somePartial.md +3 -0
  45. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +5 -0
  46. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +1 -0
  47. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +2 -0
  48. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +2 -0
  49. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +2 -0
  50. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +2 -0
  51. package/src/__tests__/__fixtures__/simple-site/sidebars.json +15 -1
  52. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +6 -0
  53. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/somePartial.md +3 -0
  54. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/subfolder/somePartial.md +3 -0
  55. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_somePartial.md +3 -0
  56. package/src/__tests__/__snapshots__/cli.test.ts.snap +33 -0
  57. package/src/__tests__/__snapshots__/docs.test.ts.snap +140 -0
  58. package/src/__tests__/__snapshots__/index.test.ts.snap +478 -60
  59. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -3
  60. package/src/__tests__/cli.test.ts +14 -10
  61. package/src/__tests__/docFrontMatter.test.ts +163 -48
  62. package/src/__tests__/docs.test.ts +167 -21
  63. package/src/__tests__/index.test.ts +74 -30
  64. package/src/__tests__/lastUpdate.test.ts +3 -2
  65. package/src/__tests__/options.test.ts +46 -3
  66. package/src/__tests__/props.test.ts +62 -0
  67. package/src/__tests__/translations.test.ts +0 -1
  68. package/src/__tests__/versions.test.ts +88 -60
  69. package/src/cli.ts +27 -30
  70. package/src/client/__tests__/docsClientUtils.test.ts +4 -5
  71. package/src/client/docsClientUtils.ts +6 -27
  72. package/src/docFrontMatter.ts +8 -3
  73. package/src/docs.ts +92 -9
  74. package/src/index.ts +114 -121
  75. package/src/lastUpdate.ts +10 -6
  76. package/src/markdown/index.ts +8 -12
  77. package/src/numberPrefix.ts +4 -2
  78. package/src/options.ts +47 -17
  79. package/src/plugin-content-docs.d.ts +121 -34
  80. package/src/props.ts +42 -6
  81. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -0
  82. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -0
  83. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -0
  84. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category.js +0 -0
  85. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -0
  86. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed.json +0 -0
  87. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -0
  88. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-first-level-not-category.js +0 -0
  89. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -0
  90. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -0
  91. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link.json +0 -0
  92. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-unknown-type.json +0 -0
  93. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-wrong-field.json +0 -0
  94. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars.json +0 -0
  95. package/src/{__tests__/__snapshots__/sidebars.test.ts.snap → sidebars/__tests__/__snapshots__/index.test.ts.snap} +21 -6
  96. package/src/{__tests__/sidebarItemsGenerator.test.ts → sidebars/__tests__/generator.test.ts} +29 -7
  97. package/src/sidebars/__tests__/index.test.ts +202 -0
  98. package/src/sidebars/__tests__/processor.test.ts +148 -0
  99. package/src/sidebars/__tests__/utils.test.ts +395 -0
  100. package/src/sidebars/generator.ts +253 -0
  101. package/src/sidebars/index.ts +84 -0
  102. package/src/sidebars/normalization.ts +88 -0
  103. package/src/sidebars/processor.ts +124 -0
  104. package/src/sidebars/types.ts +156 -0
  105. package/src/sidebars/utils.ts +146 -0
  106. package/src/sidebars/validation.ts +124 -0
  107. package/src/tags.ts +21 -0
  108. package/src/theme/hooks/useDocs.ts +5 -1
  109. package/src/translations.ts +26 -36
  110. package/src/types.ts +48 -99
  111. package/src/versions.ts +109 -17
  112. package/lib/sidebarItemsGenerator.js +0 -211
  113. package/lib/sidebars.d.ts +0 -43
  114. package/lib/sidebars.js +0 -319
  115. package/src/__tests__/sidebars.test.ts +0 -639
  116. package/src/sidebarItemsGenerator.ts +0 -307
  117. package/src/sidebars.ts +0 -506
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
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 {Joi, URISchema} from '@docusaurus/utils-validation';
9
+ import {
10
+ SidebarItemConfig,
11
+ SidebarCategoriesShorthand,
12
+ SidebarItemBase,
13
+ SidebarItemAutogenerated,
14
+ SidebarItemDoc,
15
+ SidebarItemLink,
16
+ SidebarItemCategoryConfig,
17
+ SidebarsConfig,
18
+ isCategoriesShorthand,
19
+ } from './types';
20
+
21
+ const sidebarItemBaseSchema = Joi.object<SidebarItemBase>({
22
+ className: Joi.string(),
23
+ customProps: Joi.object().unknown(),
24
+ });
25
+
26
+ const sidebarItemAutogeneratedSchema =
27
+ sidebarItemBaseSchema.append<SidebarItemAutogenerated>({
28
+ type: 'autogenerated',
29
+ dirName: Joi.string()
30
+ .required()
31
+ .pattern(/^[^/](.*[^/])?$/)
32
+ .message(
33
+ '"dirName" must be a dir path relative to the docs folder root, and should not start or end with slash',
34
+ ),
35
+ });
36
+
37
+ const sidebarItemDocSchema = sidebarItemBaseSchema.append<SidebarItemDoc>({
38
+ type: Joi.string().valid('doc', 'ref').required(),
39
+ id: Joi.string().required(),
40
+ label: Joi.string(),
41
+ });
42
+
43
+ const sidebarItemLinkSchema = sidebarItemBaseSchema.append<SidebarItemLink>({
44
+ type: 'link',
45
+ href: URISchema.required(),
46
+ label: Joi.string()
47
+ .required()
48
+ .messages({'any.unknown': '"label" must be a string'}),
49
+ });
50
+
51
+ const sidebarItemCategorySchema =
52
+ sidebarItemBaseSchema.append<SidebarItemCategoryConfig>({
53
+ type: 'category',
54
+ label: Joi.string()
55
+ .required()
56
+ .messages({'any.unknown': '"label" must be a string'}),
57
+ // TODO: Joi doesn't allow mutual recursion. See https://github.com/sideway/joi/issues/2611
58
+ items: Joi.array()
59
+ .required()
60
+ .messages({'any.unknown': '"items" must be an array'}), // .items(Joi.link('#sidebarItemSchema')),
61
+ collapsed: Joi.boolean().messages({
62
+ 'any.unknown': '"collapsed" must be a boolean',
63
+ }),
64
+ collapsible: Joi.boolean().messages({
65
+ 'any.unknown': '"collapsible" must be a boolean',
66
+ }),
67
+ });
68
+
69
+ const sidebarItemSchema: Joi.Schema<SidebarItemConfig> = Joi.object()
70
+ .when('.type', {
71
+ switch: [
72
+ {is: 'link', then: sidebarItemLinkSchema},
73
+ {
74
+ is: Joi.string().valid('doc', 'ref').required(),
75
+ then: sidebarItemDocSchema,
76
+ },
77
+ {is: 'autogenerated', then: sidebarItemAutogeneratedSchema},
78
+ {is: 'category', then: sidebarItemCategorySchema},
79
+ {
80
+ is: 'subcategory',
81
+ then: Joi.forbidden().messages({
82
+ 'any.unknown':
83
+ 'Docusaurus v2: "subcategory" has been renamed as "category".',
84
+ }),
85
+ },
86
+ {
87
+ is: Joi.string().required(),
88
+ then: Joi.forbidden().messages({
89
+ 'any.unknown': 'Unknown sidebar item type "{.type}".',
90
+ }),
91
+ },
92
+ ],
93
+ })
94
+ .id('sidebarItemSchema');
95
+
96
+ function validateSidebarItem(item: unknown): asserts item is SidebarItemConfig {
97
+ if (typeof item === 'string') {
98
+ return;
99
+ }
100
+ // TODO: remove once with proper Joi support
101
+ // Because we can't use Joi to validate nested items (see above), we do it manually
102
+ if (isCategoriesShorthand(item as SidebarItemConfig)) {
103
+ Object.values(item as SidebarCategoriesShorthand).forEach((category) =>
104
+ category.forEach(validateSidebarItem),
105
+ );
106
+ } else {
107
+ Joi.assert(item, sidebarItemSchema);
108
+ if ((item as SidebarItemCategoryConfig).type === 'category') {
109
+ (item as SidebarItemCategoryConfig).items.forEach(validateSidebarItem);
110
+ }
111
+ }
112
+ }
113
+
114
+ export function validateSidebars(
115
+ sidebars: unknown,
116
+ ): asserts sidebars is SidebarsConfig {
117
+ Object.values(sidebars as SidebarsConfig).forEach((sidebar) => {
118
+ if (Array.isArray(sidebar)) {
119
+ sidebar.forEach(validateSidebarItem);
120
+ } else {
121
+ validateSidebarItem(sidebar);
122
+ }
123
+ });
124
+ }
package/src/tags.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
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 {groupTaggedItems} from '@docusaurus/utils';
9
+ import {VersionTags, DocMetadata} from './types';
10
+ import {mapValues} from 'lodash';
11
+
12
+ export function getVersionTags(docs: DocMetadata[]): VersionTags {
13
+ const groups = groupTaggedItems(docs, (doc) => doc.tags);
14
+ return mapValues(groups, (group) => {
15
+ return {
16
+ name: group.tag.label,
17
+ docIds: group.items.map((item) => item.id),
18
+ permalink: group.tag.permalink,
19
+ };
20
+ });
21
+ }
@@ -24,11 +24,15 @@ import {
24
24
  GetActivePluginOptions,
25
25
  } from '../../client/docsClientUtils';
26
26
 
27
+ // Important to use a constant object to avoid React useEffect executions etc...,
28
+ // see https://github.com/facebook/docusaurus/issues/5089
29
+ const StableEmptyObject = {};
30
+
27
31
  // Not using useAllPluginInstancesData() because in blog-only mode, docs hooks are still used by the theme
28
32
  // We need a fail-safe fallback when the docs plugin is not in use
29
33
  export const useAllDocsData = (): Record<string, GlobalPluginData> =>
30
34
  // useAllPluginInstancesData('docusaurus-plugin-content-docs');
31
- useGlobalData()['docusaurus-plugin-content-docs'] ?? {};
35
+ useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject;
32
36
 
33
37
  export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
34
38
  usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData;
@@ -5,20 +5,15 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {
9
- LoadedVersion,
10
- Sidebar,
11
- LoadedContent,
12
- Sidebars,
13
- SidebarItem,
14
- } from './types';
8
+ import type {LoadedVersion, LoadedContent} from './types';
9
+ import type {Sidebar, Sidebars} from './sidebars/types';
15
10
 
16
- import {chain, mapValues, flatten, keyBy} from 'lodash';
11
+ import {chain, mapValues, keyBy} from 'lodash';
17
12
  import {
18
13
  collectSidebarCategories,
19
14
  transformSidebarItems,
20
15
  collectSidebarLinks,
21
- } from './sidebars';
16
+ } from './sidebars/utils';
22
17
  import {
23
18
  TranslationFileContent,
24
19
  TranslationFile,
@@ -131,29 +126,25 @@ function translateSidebar({
131
126
  sidebarName: string;
132
127
  sidebarsTranslations: TranslationFileContent;
133
128
  }): Sidebar {
134
- return transformSidebarItems(
135
- sidebar,
136
- (item: SidebarItem): SidebarItem => {
137
- if (item.type === 'category') {
138
- return {
139
- ...item,
140
- label:
141
- sidebarsTranslations[
142
- `sidebar.${sidebarName}.category.${item.label}`
143
- ]?.message ?? item.label,
144
- };
145
- }
146
- if (item.type === 'link') {
147
- return {
148
- ...item,
149
- label:
150
- sidebarsTranslations[`sidebar.${sidebarName}.link.${item.label}`]
151
- ?.message ?? item.label,
152
- };
153
- }
154
- return item;
155
- },
156
- );
129
+ return transformSidebarItems(sidebar, (item) => {
130
+ if (item.type === 'category') {
131
+ return {
132
+ ...item,
133
+ label:
134
+ sidebarsTranslations[`sidebar.${sidebarName}.category.${item.label}`]
135
+ ?.message ?? item.label,
136
+ };
137
+ }
138
+ if (item.type === 'link') {
139
+ return {
140
+ ...item,
141
+ label:
142
+ sidebarsTranslations[`sidebar.${sidebarName}.link.${item.label}`]
143
+ ?.message ?? item.label,
144
+ };
145
+ }
146
+ return item;
147
+ });
157
148
  }
158
149
 
159
150
  function getSidebarsTranslations(
@@ -193,9 +184,8 @@ function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
193
184
  },
194
185
  };
195
186
 
196
- const sidebarsTranslations: TranslationFileContent = getSidebarsTranslations(
197
- version,
198
- );
187
+ const sidebarsTranslations: TranslationFileContent =
188
+ getSidebarsTranslations(version);
199
189
 
200
190
  // const docsTranslations: TranslationFileContent = getDocsTranslations(version);
201
191
 
@@ -227,7 +217,7 @@ function translateVersion(
227
217
  function getVersionsTranslationFiles(
228
218
  versions: LoadedVersion[],
229
219
  ): TranslationFiles {
230
- return flatten(versions.map(getVersionTranslationFiles));
220
+ return versions.flatMap(getVersionTranslationFiles);
231
221
  }
232
222
  function translateVersions(
233
223
  versions: LoadedVersion[],
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} 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
@@ -59,9 +64,15 @@ export type PathOptions = {
59
64
  sidebarPath?: string | false | undefined;
60
65
  };
61
66
 
67
+ // TODO support custom version banner? {type: "error", content: "html content"}
68
+ export type VersionBanner = 'unreleased' | 'unmaintained';
69
+
62
70
  export type VersionOptions = {
63
71
  path?: string;
64
72
  label?: string;
73
+ banner?: 'none' | VersionBanner;
74
+ badge?: boolean;
75
+ className?: string;
65
76
  };
66
77
 
67
78
  export type VersionsOptions = {
@@ -70,111 +81,30 @@ export type VersionsOptions = {
70
81
  onlyIncludeVersions?: string[];
71
82
  };
72
83
 
84
+ export type SidebarOptions = {
85
+ sidebarCollapsible: boolean;
86
+ sidebarCollapsed: boolean;
87
+ };
88
+
73
89
  export type PluginOptions = MetadataOptions &
74
90
  PathOptions &
75
91
  VersionsOptions &
76
- RemarkAndRehypePluginOptions & {
92
+ RemarkAndRehypePluginOptions &
93
+ SidebarOptions & {
77
94
  id: string;
78
95
  include: string[];
96
+ exclude: string[];
79
97
  docLayoutComponent: string;
80
98
  docItemComponent: string;
99
+ docTagDocListComponent: string;
100
+ docTagsListComponent: string;
81
101
  admonitions: Record<string, unknown>;
82
102
  disableVersioning: boolean;
83
- excludeNextVersionDocs?: boolean;
84
103
  includeCurrentVersion: boolean;
85
104
  sidebarItemsGenerator: SidebarItemsGeneratorOption;
105
+ tagsBasePath: string;
86
106
  };
87
107
 
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
108
  export type LastUpdateData = {
179
109
  lastUpdatedAt?: number;
180
110
  formattedLastUpdatedAt?: string;
@@ -182,8 +112,11 @@ export type LastUpdateData = {
182
112
  };
183
113
 
184
114
  export type DocFrontMatter = {
115
+ // Front matter uses snake case
116
+ /* eslint-disable camelcase */
185
117
  id?: string;
186
118
  title?: string;
119
+ tags?: FrontMatterTag[];
187
120
  hide_title?: boolean;
188
121
  hide_table_of_contents?: boolean;
189
122
  keywords?: string[];
@@ -192,9 +125,15 @@ export type DocFrontMatter = {
192
125
  slug?: string;
193
126
  sidebar_label?: string;
194
127
  sidebar_position?: number;
128
+ sidebar_class_name?: string;
195
129
  pagination_label?: string;
196
130
  custom_edit_url?: string | null;
197
131
  parse_number_prefixes?: boolean;
132
+ toc_min_heading_level?: number;
133
+ toc_max_heading_level?: number;
134
+ pagination_next?: string | null;
135
+ pagination_prev?: string | null;
136
+ /* eslint-enable camelcase */
198
137
  };
199
138
 
200
139
  export type DocMetadataBase = LastUpdateData & {
@@ -208,9 +147,9 @@ export type DocMetadataBase = LastUpdateData & {
208
147
  sourceDirName: string; // relative to the docs folder (can be ".")
209
148
  slug: string;
210
149
  permalink: string;
211
- // eslint-disable-next-line camelcase
212
150
  sidebarPosition?: number;
213
151
  editUrl?: string | null;
152
+ tags: Tag[];
214
153
  frontMatter: DocFrontMatter & Record<string, unknown>;
215
154
  };
216
155
 
@@ -228,12 +167,21 @@ export type DocMetadata = DocMetadataBase & {
228
167
  export type SourceToPermalink = {
229
168
  [source: string]: string;
230
169
  };
170
+
171
+ export type VersionTag = {
172
+ name: string; // normalized name/label of the tag
173
+ docIds: string[]; // all doc ids having this tag
174
+ permalink: string; // pathname of the tag
175
+ };
176
+ export type VersionTags = {
177
+ [key: string]: VersionTag;
178
+ };
179
+
231
180
  export type LoadedVersion = VersionMetadata & {
232
181
  versionPath: string;
233
182
  mainDocId: string;
234
183
  docs: DocMetadata[];
235
184
  sidebars: Sidebars;
236
- permalinkToSidebar: Record<string, string>;
237
185
  };
238
186
 
239
187
  export type LoadedContent = {
@@ -269,6 +217,7 @@ export type DocsMarkdownOption = {
269
217
  onBrokenMarkdownLink: (brokenMarkdownLink: BrokenMarkdownLink) => void;
270
218
  };
271
219
 
272
- export type NumberPrefixParser = (
273
- filename: string,
274
- ) => {filename: string; numberPrefix?: number};
220
+ export type NumberPrefixParser = (filename: string) => {
221
+ filename: string;
222
+ numberPrefix?: number;
223
+ };
package/src/versions.ts CHANGED
@@ -9,6 +9,7 @@ import path from 'path';
9
9
  import fs from 'fs-extra';
10
10
  import {
11
11
  PluginOptions,
12
+ VersionBanner,
12
13
  VersionMetadata,
13
14
  VersionOptions,
14
15
  VersionsOptions,
@@ -255,14 +256,92 @@ function getVersionEditUrls({
255
256
  };
256
257
  }
257
258
 
259
+ function getDefaultVersionBanner({
260
+ versionName,
261
+ versionNames,
262
+ lastVersionName,
263
+ }: {
264
+ versionName: string;
265
+ versionNames: string[];
266
+ lastVersionName: string;
267
+ }): VersionBanner | null {
268
+ // Current version: good, no banner
269
+ if (versionName === lastVersionName) {
270
+ return null;
271
+ }
272
+ // Upcoming versions: unreleased banner
273
+ else if (
274
+ versionNames.indexOf(versionName) < versionNames.indexOf(lastVersionName)
275
+ ) {
276
+ return 'unreleased';
277
+ }
278
+ // Older versions: display unmaintained banner
279
+ else {
280
+ return 'unmaintained';
281
+ }
282
+ }
283
+
284
+ function getVersionBanner({
285
+ versionName,
286
+ versionNames,
287
+ lastVersionName,
288
+ options,
289
+ }: {
290
+ versionName: string;
291
+ versionNames: string[];
292
+ lastVersionName: string;
293
+ options: Pick<PluginOptions, 'versions'>;
294
+ }): VersionBanner | null {
295
+ const versionBannerOption = options.versions[versionName]?.banner;
296
+ if (versionBannerOption) {
297
+ return versionBannerOption === 'none' ? null : versionBannerOption;
298
+ }
299
+ return getDefaultVersionBanner({
300
+ versionName,
301
+ versionNames,
302
+ lastVersionName,
303
+ });
304
+ }
305
+
306
+ function getVersionBadge({
307
+ versionName,
308
+ versionNames,
309
+ options,
310
+ }: {
311
+ versionName: string;
312
+ versionNames: string[];
313
+ options: Pick<PluginOptions, 'versions'>;
314
+ }): boolean {
315
+ const versionBadgeOption = options.versions[versionName]?.badge;
316
+ // If site is not versioned or only one version is included
317
+ // we don't show the version badge by default
318
+ // See https://github.com/facebook/docusaurus/issues/3362
319
+ const versionBadgeDefault = versionNames.length !== 1;
320
+ return versionBadgeOption ?? versionBadgeDefault;
321
+ }
322
+
323
+ function getVersionClassName({
324
+ versionName,
325
+ options,
326
+ }: {
327
+ versionName: string;
328
+ options: Pick<PluginOptions, 'versions'>;
329
+ }): string {
330
+ const versionClassNameOption = options.versions[versionName]?.className;
331
+ const versionClassNameDefault = `docs-version-${versionName}`;
332
+ return versionClassNameOption ?? versionClassNameDefault;
333
+ }
334
+
258
335
  function createVersionMetadata({
259
336
  versionName,
260
- isLast,
337
+ versionNames,
338
+ lastVersionName,
261
339
  context,
262
340
  options,
263
341
  }: {
264
342
  versionName: string;
265
- isLast: boolean;
343
+ versionNames: string[];
344
+ lastVersionName: string;
266
345
  context: Pick<LoadContext, 'siteDir' | 'baseUrl' | 'i18n'>;
267
346
  options: Pick<
268
347
  PluginOptions,
@@ -270,29 +349,27 @@ function createVersionMetadata({
270
349
  | 'path'
271
350
  | 'sidebarPath'
272
351
  | 'routeBasePath'
352
+ | 'tagsBasePath'
273
353
  | 'versions'
274
354
  | 'editUrl'
275
355
  | 'editCurrentVersion'
276
356
  >;
277
357
  }): VersionMetadata {
278
- const {
279
- sidebarFilePath,
280
- contentPath,
281
- contentPathLocalized,
282
- } = getVersionMetadataPaths({
283
- versionName,
284
- context,
285
- options,
286
- });
358
+ const {sidebarFilePath, contentPath, contentPathLocalized} =
359
+ getVersionMetadataPaths({versionName, context, options});
360
+
361
+ const isLast = versionName === lastVersionName;
287
362
 
288
363
  // retro-compatible values
289
364
  const defaultVersionLabel =
290
365
  versionName === CURRENT_VERSION_NAME ? 'Next' : versionName;
291
- const defaultVersionPathPart = isLast
292
- ? ''
293
- : versionName === CURRENT_VERSION_NAME
294
- ? 'next'
295
- : versionName;
366
+ function getDefaultVersionPathPart() {
367
+ if (isLast) {
368
+ return '';
369
+ }
370
+ return versionName === CURRENT_VERSION_NAME ? 'next' : versionName;
371
+ }
372
+ const defaultVersionPathPart = getDefaultVersionPathPart();
296
373
 
297
374
  const versionOptions: VersionOptions = options.versions[versionName] ?? {};
298
375
 
@@ -315,12 +392,25 @@ function createVersionMetadata({
315
392
  // Because /docs/:route` should always be after `/docs/versionName/:route`.
316
393
  const routePriority = versionPathPart === '' ? -1 : undefined;
317
394
 
395
+ // the path that will be used to refer the docs tags
396
+ // example below will be using /docs/tags
397
+ const tagsPath = normalizeUrl([versionPath, options.tagsBasePath]);
398
+
318
399
  return {
319
400
  versionName,
320
401
  versionLabel,
321
402
  versionPath,
403
+ tagsPath,
322
404
  versionEditUrl: versionEditUrls?.versionEditUrl,
323
405
  versionEditUrlLocalized: versionEditUrls?.versionEditUrlLocalized,
406
+ versionBanner: getVersionBanner({
407
+ versionName,
408
+ versionNames,
409
+ lastVersionName,
410
+ options,
411
+ }),
412
+ versionBadge: getVersionBadge({versionName, versionNames, options}),
413
+ versionClassName: getVersionClassName({versionName, options}),
324
414
  isLast,
325
415
  routePriority,
326
416
  sidebarFilePath,
@@ -465,6 +555,7 @@ export function readVersionsMetadata({
465
555
  | 'path'
466
556
  | 'sidebarPath'
467
557
  | 'routeBasePath'
558
+ | 'tagsBasePath'
468
559
  | 'includeCurrentVersion'
469
560
  | 'disableVersioning'
470
561
  | 'lastVersion'
@@ -486,7 +577,8 @@ export function readVersionsMetadata({
486
577
  const versionsMetadata = versionNames.map((versionName) =>
487
578
  createVersionMetadata({
488
579
  versionName,
489
- isLast: versionName === lastVersionName,
580
+ versionNames,
581
+ lastVersionName,
490
582
  context,
491
583
  options,
492
584
  }),