@docusaurus/plugin-content-docs 2.0.0-beta.0e652730d → 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
@@ -0,0 +1,168 @@
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 type {
10
+ SidebarItemConfig,
11
+ SidebarCategoriesShorthand,
12
+ SidebarItemBase,
13
+ SidebarItemAutogenerated,
14
+ SidebarItemDoc,
15
+ SidebarItemLink,
16
+ SidebarItemCategoryConfig,
17
+ SidebarItemCategoryLink,
18
+ SidebarsConfig,
19
+ SidebarItemCategoryLinkDoc,
20
+ SidebarItemCategoryLinkGeneratedIndex,
21
+ } from './types';
22
+ import {isCategoriesShorthand} from './utils';
23
+ import {CategoryMetadataFile} from './generator';
24
+
25
+ const sidebarItemBaseSchema = Joi.object<SidebarItemBase>({
26
+ className: Joi.string(),
27
+ customProps: Joi.object().unknown(),
28
+ });
29
+
30
+ const sidebarItemAutogeneratedSchema =
31
+ sidebarItemBaseSchema.append<SidebarItemAutogenerated>({
32
+ type: 'autogenerated',
33
+ dirName: Joi.string()
34
+ .required()
35
+ .pattern(/^[^/](.*[^/])?$/)
36
+ .message(
37
+ '"dirName" must be a dir path relative to the docs folder root, and should not start or end with slash',
38
+ ),
39
+ });
40
+
41
+ const sidebarItemDocSchema = sidebarItemBaseSchema.append<SidebarItemDoc>({
42
+ type: Joi.string().valid('doc', 'ref').required(),
43
+ id: Joi.string().required(),
44
+ label: Joi.string(),
45
+ });
46
+
47
+ const sidebarItemLinkSchema = sidebarItemBaseSchema.append<SidebarItemLink>({
48
+ type: 'link',
49
+ href: URISchema.required(),
50
+ label: Joi.string()
51
+ .required()
52
+ .messages({'any.unknown': '"label" must be a string'}),
53
+ });
54
+
55
+ const sidebarItemCategoryLinkSchema = Joi.object<SidebarItemCategoryLink>()
56
+ .when('.type', {
57
+ switch: [
58
+ {
59
+ is: 'doc',
60
+ then: Joi.object<SidebarItemCategoryLinkDoc>({
61
+ type: 'doc',
62
+ id: Joi.string().required(),
63
+ }),
64
+ },
65
+ {
66
+ is: 'generated-index',
67
+ then: Joi.object<SidebarItemCategoryLinkGeneratedIndex>({
68
+ type: 'generated-index',
69
+ slug: Joi.string().optional(),
70
+ // permalink: Joi.string().optional(), // No, this one is not in the user config, only in the normalized version
71
+ title: Joi.string().optional(),
72
+ description: Joi.string().optional(),
73
+ }),
74
+ },
75
+ {
76
+ is: Joi.string().required(),
77
+ then: Joi.forbidden().messages({
78
+ 'any.unknown': 'Unknown sidebar category link type "{.type}".',
79
+ }),
80
+ },
81
+ ],
82
+ })
83
+ .id('sidebarCategoryLinkSchema');
84
+
85
+ const sidebarItemCategorySchema =
86
+ sidebarItemBaseSchema.append<SidebarItemCategoryConfig>({
87
+ type: 'category',
88
+ label: Joi.string()
89
+ .required()
90
+ .messages({'any.unknown': '"label" must be a string'}),
91
+ // TODO: Joi doesn't allow mutual recursion. See https://github.com/sideway/joi/issues/2611
92
+ items: Joi.array()
93
+ .required()
94
+ .messages({'any.unknown': '"items" must be an array'}), // .items(Joi.link('#sidebarItemSchema')),
95
+ link: sidebarItemCategoryLinkSchema,
96
+ collapsed: Joi.boolean().messages({
97
+ 'any.unknown': '"collapsed" must be a boolean',
98
+ }),
99
+ collapsible: Joi.boolean().messages({
100
+ 'any.unknown': '"collapsible" must be a boolean',
101
+ }),
102
+ });
103
+
104
+ const sidebarItemSchema: Joi.Schema<SidebarItemConfig> = Joi.object()
105
+ .when('.type', {
106
+ switch: [
107
+ {is: 'link', then: sidebarItemLinkSchema},
108
+ {
109
+ is: Joi.string().valid('doc', 'ref').required(),
110
+ then: sidebarItemDocSchema,
111
+ },
112
+ {is: 'autogenerated', then: sidebarItemAutogeneratedSchema},
113
+ {is: 'category', then: sidebarItemCategorySchema},
114
+ {
115
+ is: Joi.any().required(),
116
+ then: Joi.forbidden().messages({
117
+ 'any.unknown': 'Unknown sidebar item type "{.type}".',
118
+ }),
119
+ },
120
+ ],
121
+ })
122
+ .id('sidebarItemSchema');
123
+
124
+ function validateSidebarItem(item: unknown): asserts item is SidebarItemConfig {
125
+ if (typeof item === 'string') {
126
+ return;
127
+ }
128
+ // TODO: remove once with proper Joi support
129
+ // Because we can't use Joi to validate nested items (see above), we do it manually
130
+ if (isCategoriesShorthand(item as SidebarItemConfig)) {
131
+ Object.values(item as SidebarCategoriesShorthand).forEach((category) =>
132
+ category.forEach(validateSidebarItem),
133
+ );
134
+ } else {
135
+ Joi.assert(item, sidebarItemSchema);
136
+
137
+ if ((item as SidebarItemCategoryConfig).type === 'category') {
138
+ (item as SidebarItemCategoryConfig).items.forEach(validateSidebarItem);
139
+ }
140
+ }
141
+ }
142
+
143
+ export function validateSidebars(
144
+ sidebars: unknown,
145
+ ): asserts sidebars is SidebarsConfig {
146
+ Object.values(sidebars as SidebarsConfig).forEach((sidebar) => {
147
+ if (Array.isArray(sidebar)) {
148
+ sidebar.forEach(validateSidebarItem);
149
+ } else {
150
+ validateSidebarItem(sidebar);
151
+ }
152
+ });
153
+ }
154
+
155
+ const categoryMetadataFileSchema = Joi.object<CategoryMetadataFile>({
156
+ label: Joi.string(),
157
+ position: Joi.number(),
158
+ collapsed: Joi.boolean(),
159
+ collapsible: Joi.boolean(),
160
+ className: Joi.string(),
161
+ link: sidebarItemCategoryLinkSchema,
162
+ });
163
+
164
+ export function validateCategoryMetadataFile(
165
+ unsafeContent: unknown,
166
+ ): CategoryMetadataFile {
167
+ return Joi.attempt(unsafeContent, categoryMetadataFileSchema);
168
+ }
package/src/slug.ts CHANGED
@@ -15,39 +15,52 @@ import {
15
15
  DefaultNumberPrefixParser,
16
16
  stripPathNumberPrefixes,
17
17
  } from './numberPrefix';
18
- import {NumberPrefixParser} from './types';
18
+ import type {DocMetadataBase, NumberPrefixParser} from './types';
19
+ import {isConventionalDocIndex} from './docs';
19
20
 
20
21
  export default function getSlug({
21
22
  baseID,
22
23
  frontmatterSlug,
23
- dirName,
24
+ source,
25
+ sourceDirName,
24
26
  stripDirNumberPrefixes = true,
25
27
  numberPrefixParser = DefaultNumberPrefixParser,
26
28
  }: {
27
29
  baseID: string;
28
30
  frontmatterSlug?: string;
29
- dirName: string;
31
+ source: DocMetadataBase['slug'];
32
+ sourceDirName: DocMetadataBase['sourceDirName'];
30
33
  stripDirNumberPrefixes?: boolean;
31
34
  numberPrefixParser?: NumberPrefixParser;
32
35
  }): string {
33
- const baseSlug = frontmatterSlug || baseID;
34
- let slug: string;
35
- if (baseSlug.startsWith('/')) {
36
- slug = baseSlug;
37
- } else {
36
+ function getDirNameSlug(): string {
38
37
  const dirNameStripped = stripDirNumberPrefixes
39
- ? stripPathNumberPrefixes(dirName, numberPrefixParser)
40
- : dirName;
38
+ ? stripPathNumberPrefixes(sourceDirName, numberPrefixParser)
39
+ : sourceDirName;
41
40
  const resolveDirname =
42
- dirName === '.'
41
+ sourceDirName === '.'
43
42
  ? '/'
44
43
  : addLeadingSlash(addTrailingSlash(dirNameStripped));
45
- slug = resolvePathname(baseSlug, resolveDirname);
44
+ return resolveDirname;
46
45
  }
47
46
 
48
- if (!isValidPathname(slug)) {
49
- throw new Error(
50
- `We couldn't compute a valid slug for document with id "${baseID}" in "${dirName}" directory.
47
+ function computeSlug(): string {
48
+ if (frontmatterSlug?.startsWith('/')) {
49
+ return frontmatterSlug;
50
+ } else {
51
+ const dirNameSlug = getDirNameSlug();
52
+ if (!frontmatterSlug && isConventionalDocIndex({source, sourceDirName})) {
53
+ return dirNameSlug;
54
+ }
55
+ const baseSlug = frontmatterSlug || baseID;
56
+ return resolvePathname(baseSlug, getDirNameSlug());
57
+ }
58
+ }
59
+
60
+ function ensureValidSlug(slug: string): string {
61
+ if (!isValidPathname(slug)) {
62
+ throw new Error(
63
+ `We couldn't compute a valid slug for document with id "${baseID}" in "${sourceDirName}" directory.
51
64
  The slug we computed looks invalid: ${slug}.
52
65
  Maybe your slug frontmatter is incorrect or you use weird chars in the file path?
53
66
  By using the slug frontmatter, you should be able to fix this error, by using the slug of your choice:
@@ -57,8 +70,10 @@ Example =>
57
70
  slug: /my/customDocPath
58
71
  ---
59
72
  `,
60
- );
73
+ );
74
+ }
75
+ return slug;
61
76
  }
62
77
 
63
- return slug;
78
+ return ensureValidSlug(computeSlug());
64
79
  }
package/src/tags.ts ADDED
@@ -0,0 +1,19 @@
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 type {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
+ name: group.tag.label,
16
+ docIds: group.items.map((item) => item.id),
17
+ permalink: group.tag.permalink,
18
+ }));
19
+ }
@@ -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,27 +5,28 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {
9
- LoadedVersion,
8
+ import type {LoadedVersion, LoadedContent} from './types';
9
+ import type {
10
10
  Sidebar,
11
- LoadedContent,
11
+ SidebarItemCategory,
12
+ SidebarItemCategoryLink,
12
13
  Sidebars,
13
- SidebarItem,
14
- } from './types';
14
+ } from './sidebars/types';
15
15
 
16
- import {chain, mapValues, flatten, keyBy} from 'lodash';
16
+ import {chain, mapValues, keyBy} from 'lodash';
17
17
  import {
18
18
  collectSidebarCategories,
19
19
  transformSidebarItems,
20
20
  collectSidebarLinks,
21
- } from './sidebars';
22
- import {
21
+ } from './sidebars/utils';
22
+ import type {
23
23
  TranslationFileContent,
24
24
  TranslationFile,
25
25
  TranslationFiles,
26
26
  } from '@docusaurus/types';
27
27
  import {mergeTranslations} from '@docusaurus/utils';
28
28
  import {CURRENT_VERSION_NAME} from './constants';
29
+ import {TranslationMessage} from '@docusaurus/types';
29
30
 
30
31
  function getVersionFileName(versionName: string): string {
31
32
  if (versionName === CURRENT_VERSION_NAME) {
@@ -55,7 +56,7 @@ function getNormalizedSidebarName({
55
56
  }
56
57
 
57
58
  /*
58
- // Do we need to translate doc metadatas?
59
+ // Do we need to translate doc metadata?
59
60
  // It seems translating frontmatter labels is good enough
60
61
  function getDocTranslations(doc: DocMetadata): TranslationFileContent {
61
62
  return {
@@ -101,14 +102,48 @@ function getSidebarTranslationFileContent(
101
102
  sidebar: Sidebar,
102
103
  sidebarName: string,
103
104
  ): TranslationFileContent {
105
+ type TranslationMessageEntry = [string, TranslationMessage];
106
+
104
107
  const categories = collectSidebarCategories(sidebar);
105
- const categoryContent: TranslationFileContent = chain(categories)
106
- .keyBy((category) => `sidebar.${sidebarName}.category.${category.label}`)
107
- .mapValues((category) => ({
108
- message: category.label,
109
- description: `The label for category ${category.label} in sidebar ${sidebarName}`,
110
- }))
111
- .value();
108
+
109
+ const categoryContent: TranslationFileContent = Object.fromEntries(
110
+ categories.flatMap((category) => {
111
+ const entries: TranslationMessageEntry[] = [];
112
+
113
+ entries.push([
114
+ `sidebar.${sidebarName}.category.${category.label}`,
115
+ {
116
+ message: category.label,
117
+ description: `The label for category ${category.label} in sidebar ${sidebarName}`,
118
+ },
119
+ ]);
120
+
121
+ if (category.link) {
122
+ if (category.link.type === 'generated-index') {
123
+ if (category.link.title) {
124
+ entries.push([
125
+ `sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`,
126
+ {
127
+ message: category.link.title,
128
+ description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
129
+ },
130
+ ]);
131
+ }
132
+ if (category.link.description) {
133
+ entries.push([
134
+ `sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`,
135
+ {
136
+ message: category.link.description,
137
+ description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
138
+ },
139
+ ]);
140
+ }
141
+ }
142
+ }
143
+
144
+ return entries;
145
+ }),
146
+ );
112
147
 
113
148
  const links = collectSidebarLinks(sidebar);
114
149
  const linksContent: TranslationFileContent = chain(links)
@@ -131,29 +166,51 @@ function translateSidebar({
131
166
  sidebarName: string;
132
167
  sidebarsTranslations: TranslationFileContent;
133
168
  }): 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
- );
169
+ function transformSidebarCategoryLink(
170
+ category: SidebarItemCategory,
171
+ ): SidebarItemCategoryLink | undefined {
172
+ if (!category.link) {
173
+ return undefined;
174
+ }
175
+ if (category.link.type === 'generated-index') {
176
+ const title =
177
+ sidebarsTranslations[
178
+ `sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`
179
+ ]?.message ?? category.link.title;
180
+ const description =
181
+ sidebarsTranslations[
182
+ `sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`
183
+ ]?.message ?? category.link.description;
184
+ return {
185
+ ...category.link,
186
+ title,
187
+ description,
188
+ };
189
+ }
190
+ return category.link;
191
+ }
192
+
193
+ return transformSidebarItems(sidebar, (item) => {
194
+ if (item.type === 'category') {
195
+ const link = transformSidebarCategoryLink(item);
196
+ return {
197
+ ...item,
198
+ label:
199
+ sidebarsTranslations[`sidebar.${sidebarName}.category.${item.label}`]
200
+ ?.message ?? item.label,
201
+ ...(link && {link}),
202
+ };
203
+ }
204
+ if (item.type === 'link') {
205
+ return {
206
+ ...item,
207
+ label:
208
+ sidebarsTranslations[`sidebar.${sidebarName}.link.${item.label}`]
209
+ ?.message ?? item.label,
210
+ };
211
+ }
212
+ return item;
213
+ });
157
214
  }
158
215
 
159
216
  function getSidebarsTranslations(
@@ -173,16 +230,16 @@ function translateSidebars(
173
230
  version: LoadedVersion,
174
231
  sidebarsTranslations: TranslationFileContent,
175
232
  ): Sidebars {
176
- return mapValues(version.sidebars, (sidebar, sidebarName) => {
177
- return translateSidebar({
233
+ return mapValues(version.sidebars, (sidebar, sidebarName) =>
234
+ translateSidebar({
178
235
  sidebar,
179
236
  sidebarName: getNormalizedSidebarName({
180
237
  sidebarName,
181
238
  versionName: version.versionName,
182
239
  }),
183
240
  sidebarsTranslations,
184
- });
185
- });
241
+ }),
242
+ );
186
243
  }
187
244
 
188
245
  function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
@@ -193,9 +250,8 @@ function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
193
250
  },
194
251
  };
195
252
 
196
- const sidebarsTranslations: TranslationFileContent = getSidebarsTranslations(
197
- version,
198
- );
253
+ const sidebarsTranslations: TranslationFileContent =
254
+ getSidebarsTranslations(version);
199
255
 
200
256
  // const docsTranslations: TranslationFileContent = getDocsTranslations(version);
201
257
 
@@ -227,7 +283,7 @@ function translateVersion(
227
283
  function getVersionsTranslationFiles(
228
284
  versions: LoadedVersion[],
229
285
  ): TranslationFiles {
230
- return flatten(versions.map(getVersionTranslationFiles));
286
+ return versions.flatMap(getVersionTranslationFiles);
231
287
  }
232
288
  function translateVersions(
233
289
  versions: LoadedVersion[],