@docusaurus/plugin-content-docs 3.8.1-canary-6358 → 3.8.1-canary-6360

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/props.d.ts CHANGED
@@ -11,7 +11,7 @@ export declare function toSidebarDocItemLinkProp({ item, doc, }: {
11
11
  item: SidebarItemDoc;
12
12
  doc: Pick<DocMetadata, 'id' | 'title' | 'permalink' | 'unlisted' | 'frontMatter'>;
13
13
  }): PropSidebarItemLink;
14
- export declare function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars;
14
+ export declare function toSidebarsProp(loadedVersion: Pick<LoadedVersion, 'docs' | 'sidebars'>): PropSidebars;
15
15
  export declare function toVersionMetadataProp(pluginId: string, loadedVersion: LoadedVersion): PropVersionMetadata;
16
16
  export declare function toTagDocListProp({ allTagsPath, tag, docs, }: {
17
17
  allTagsPath: string;
package/lib/props.js CHANGED
@@ -18,6 +18,7 @@ function toSidebarDocItemLinkProp({ item, doc, }) {
18
18
  const { id, title, permalink, frontMatter, unlisted } = doc;
19
19
  return {
20
20
  type: 'link',
21
+ ...(item.key && { key: item.key }),
21
22
  href: permalink,
22
23
  // Front Matter data takes precedence over sidebars.json
23
24
  label: frontMatter.sidebar_label ?? item.label ?? title,
@@ -171,6 +171,7 @@ Available doc IDs:
171
171
  ...(categoryMetadata?.description && {
172
172
  description: categoryMetadata?.description,
173
173
  }),
174
+ ...(categoryMetadata?.key && { key: categoryMetadata?.key }),
174
175
  ...(link && { link }),
175
176
  };
176
177
  }
@@ -13,6 +13,7 @@ type Expand<T extends {
13
13
  [P in keyof T]: T[P];
14
14
  };
15
15
  export type SidebarItemBase = {
16
+ key?: string;
16
17
  className?: string;
17
18
  customProps?: {
18
19
  [key: string]: unknown;
@@ -23,8 +24,9 @@ export type SidebarItemDoc = SidebarItemBase & {
23
24
  label?: string;
24
25
  id: string;
25
26
  /**
26
- * This is an internal marker. Items with labels defined in the config needs
27
- * to be translated with JSON
27
+ * This is an internal marker set during the sidebar normalization process.
28
+ * Docs with labels defined in the config need to be translated with JSON.
29
+ * Otherwise, it's preferable to translate the MDX doc title or front matter.
28
30
  */
29
31
  translatable?: true;
30
32
  };
@@ -136,6 +138,7 @@ export type PropSidebars = {
136
138
  };
137
139
  export type PropSidebarBreadcrumbsItem = PropSidebarItemLink | PropSidebarItemCategory;
138
140
  export type CategoryMetadataFile = {
141
+ key?: string;
139
142
  label?: string;
140
143
  position?: number;
141
144
  description?: string;
@@ -13,6 +13,7 @@ const utils_validation_1 = require("@docusaurus/utils-validation");
13
13
  // Config types are exposed to users for typechecking and we use the same type
14
14
  // in normalization
15
15
  const sidebarItemBaseSchema = utils_validation_1.Joi.object({
16
+ key: utils_validation_1.Joi.string(),
16
17
  className: utils_validation_1.Joi.string(),
17
18
  customProps: utils_validation_1.Joi.object().unknown(),
18
19
  });
@@ -132,6 +133,7 @@ function validateSidebars(sidebars) {
132
133
  });
133
134
  }
134
135
  const categoryMetadataFileSchema = utils_validation_1.Joi.object({
136
+ key: utils_validation_1.Joi.string(),
135
137
  label: utils_validation_1.Joi.string(),
136
138
  description: utils_validation_1.Joi.string(),
137
139
  position: utils_validation_1.Joi.number(),
@@ -11,6 +11,7 @@ exports.translateLoadedContent = translateLoadedContent;
11
11
  const tslib_1 = require("tslib");
12
12
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
13
13
  const utils_1 = require("@docusaurus/utils");
14
+ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
14
15
  const constants_1 = require("./constants");
15
16
  const utils_2 = require("./sidebars/utils");
16
17
  function getVersionFileName(versionName) {
@@ -21,12 +22,33 @@ function getVersionFileName(versionName) {
21
22
  // but it's for consistency with site/versioned_docs
22
23
  return `version-${versionName}`;
23
24
  }
25
+ function ensureNoSidebarDuplicateEntries(translationEntries) {
26
+ const grouped = lodash_1.default.groupBy(translationEntries, (entry) => entry[0]);
27
+ const duplicates = Object.entries(grouped).filter((entry) => entry[1].length > 1);
28
+ if (duplicates.length > 0) {
29
+ throw new Error(`Multiple docs sidebar items produce the same translation key.
30
+ - ${duplicates
31
+ .map(([translationKey, entries]) => {
32
+ return `${logger_1.default.code(translationKey)}: ${logger_1.default.num(entries.length)} duplicates found:\n - ${entries
33
+ .map((duplicate) => {
34
+ const desc = duplicate[1].description;
35
+ return `${logger_1.default.name(duplicate[1].message)} ${desc ? `(${logger_1.default.subdue(desc)})` : ''}`;
36
+ })
37
+ .join('\n - ')}`;
38
+ })
39
+ .join('\n\n- ')}
40
+
41
+ To avoid translation key conflicts, use the ${logger_1.default.code('key')} attribute on the sidebar items above to uniquely identify them.
42
+ `);
43
+ }
44
+ }
24
45
  function getSidebarTranslationFileContent(sidebar, sidebarName) {
25
46
  const categories = (0, utils_2.collectSidebarCategories)(sidebar);
26
- const categoryContent = Object.fromEntries(categories.flatMap((category) => {
47
+ const categoryEntries = categories.flatMap((category) => {
27
48
  const entries = [];
49
+ const categoryKey = category.key ?? category.label;
28
50
  entries.push([
29
- `sidebar.${sidebarName}.category.${category.label}`,
51
+ `sidebar.${sidebarName}.category.${categoryKey}`,
30
52
  {
31
53
  message: category.label,
32
54
  description: `The label for category ${category.label} in sidebar ${sidebarName}`,
@@ -35,7 +57,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
35
57
  if (category.link?.type === 'generated-index') {
36
58
  if (category.link.title) {
37
59
  entries.push([
38
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`,
60
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
39
61
  {
40
62
  message: category.link.title,
41
63
  description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
@@ -44,7 +66,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
44
66
  }
45
67
  if (category.link.description) {
46
68
  entries.push([
47
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`,
69
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
48
70
  {
49
71
  message: category.link.description,
50
72
  description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
@@ -53,26 +75,34 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
53
75
  }
54
76
  }
55
77
  return entries;
56
- }));
78
+ });
57
79
  const links = (0, utils_2.collectSidebarLinks)(sidebar);
58
- const linksContent = Object.fromEntries(links.map((link) => [
59
- `sidebar.${sidebarName}.link.${link.label}`,
60
- {
61
- message: link.label,
62
- description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
63
- },
64
- ]));
80
+ const linksEntries = links.map((link) => {
81
+ const linkKey = link.key ?? link.label;
82
+ return [
83
+ `sidebar.${sidebarName}.link.${linkKey}`,
84
+ {
85
+ message: link.label,
86
+ description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
87
+ },
88
+ ];
89
+ });
65
90
  const docs = (0, utils_2.collectSidebarDocItems)(sidebar)
66
91
  .concat((0, utils_2.collectSidebarRefs)(sidebar))
67
92
  .filter((item) => item.translatable);
68
- const docLinksContent = Object.fromEntries(docs.map((doc) => [
69
- `sidebar.${sidebarName}.doc.${doc.label}`,
70
- {
71
- message: doc.label,
72
- description: `The label for the doc item ${doc.label} in sidebar ${sidebarName}, linking to the doc ${doc.id}`,
73
- },
74
- ]));
75
- return (0, utils_1.mergeTranslations)([categoryContent, linksContent, docLinksContent]);
93
+ const docLinksEntries = docs.map((doc) => {
94
+ const docKey = doc.key ?? doc.label;
95
+ return [
96
+ `sidebar.${sidebarName}.doc.${docKey}`,
97
+ {
98
+ message: doc.label,
99
+ description: `The label for the doc item ${doc.label} in sidebar ${sidebarName}, linking to the doc ${doc.id}`,
100
+ },
101
+ ];
102
+ });
103
+ const allEntries = [...categoryEntries, ...linksEntries, ...docLinksEntries];
104
+ ensureNoSidebarDuplicateEntries(allEntries);
105
+ return Object.fromEntries(allEntries);
76
106
  }
77
107
  function translateSidebar({ sidebar, sidebarName, sidebarsTranslations, }) {
78
108
  function transformSidebarCategoryLink(category) {
@@ -93,24 +123,27 @@ function translateSidebar({ sidebar, sidebarName, sidebarsTranslations, }) {
93
123
  return (0, utils_2.transformSidebarItems)(sidebar, (item) => {
94
124
  if (item.type === 'category') {
95
125
  const link = transformSidebarCategoryLink(item);
126
+ const categoryKey = item.key ?? item.label;
96
127
  return {
97
128
  ...item,
98
- label: sidebarsTranslations[`sidebar.${sidebarName}.category.${item.label}`]
129
+ label: sidebarsTranslations[`sidebar.${sidebarName}.category.${categoryKey}`]
99
130
  ?.message ?? item.label,
100
131
  ...(link && { link }),
101
132
  };
102
133
  }
103
134
  if (item.type === 'link') {
135
+ const linkKey = item.key ?? item.label;
104
136
  return {
105
137
  ...item,
106
- label: sidebarsTranslations[`sidebar.${sidebarName}.link.${item.label}`]
138
+ label: sidebarsTranslations[`sidebar.${sidebarName}.link.${linkKey}`]
107
139
  ?.message ?? item.label,
108
140
  };
109
141
  }
110
142
  if ((item.type === 'doc' || item.type === 'ref') && item.translatable) {
143
+ const docKey = item.key ?? item.label;
111
144
  return {
112
145
  ...item,
113
- label: sidebarsTranslations[`sidebar.${sidebarName}.doc.${item.label}`]
146
+ label: sidebarsTranslations[`sidebar.${sidebarName}.doc.${docKey}`]
114
147
  ?.message ?? item.label,
115
148
  };
116
149
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-docs",
3
- "version": "3.8.1-canary-6358",
3
+ "version": "3.8.1-canary-6360",
4
4
  "description": "Docs plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "sideEffects": false,
@@ -35,15 +35,15 @@
35
35
  },
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@docusaurus/core": "3.8.1-canary-6358",
39
- "@docusaurus/logger": "3.8.1-canary-6358",
40
- "@docusaurus/mdx-loader": "3.8.1-canary-6358",
41
- "@docusaurus/module-type-aliases": "3.8.1-canary-6358",
42
- "@docusaurus/theme-common": "3.8.1-canary-6358",
43
- "@docusaurus/types": "3.8.1-canary-6358",
44
- "@docusaurus/utils": "3.8.1-canary-6358",
45
- "@docusaurus/utils-common": "3.8.1-canary-6358",
46
- "@docusaurus/utils-validation": "3.8.1-canary-6358",
38
+ "@docusaurus/core": "3.8.1-canary-6360",
39
+ "@docusaurus/logger": "3.8.1-canary-6360",
40
+ "@docusaurus/mdx-loader": "3.8.1-canary-6360",
41
+ "@docusaurus/module-type-aliases": "3.8.1-canary-6360",
42
+ "@docusaurus/theme-common": "3.8.1-canary-6360",
43
+ "@docusaurus/types": "3.8.1-canary-6360",
44
+ "@docusaurus/utils": "3.8.1-canary-6360",
45
+ "@docusaurus/utils-common": "3.8.1-canary-6360",
46
+ "@docusaurus/utils-validation": "3.8.1-canary-6360",
47
47
  "@types/react-router-config": "^5.0.7",
48
48
  "combine-promises": "^1.1.0",
49
49
  "fs-extra": "^11.1.1",
@@ -67,5 +67,5 @@
67
67
  "engines": {
68
68
  "node": ">=18.0"
69
69
  },
70
- "gitHead": "ffdb9f98cdff46de047de752705edb093a81e042"
70
+ "gitHead": "39d2663556664be19f2a18c24ceae1e532ab58df"
71
71
  }
package/src/props.ts CHANGED
@@ -41,6 +41,7 @@ export function toSidebarDocItemLinkProp({
41
41
  const {id, title, permalink, frontMatter, unlisted} = doc;
42
42
  return {
43
43
  type: 'link',
44
+ ...(item.key && {key: item.key}),
44
45
  href: permalink,
45
46
  // Front Matter data takes precedence over sidebars.json
46
47
  label: frontMatter.sidebar_label ?? item.label ?? title,
@@ -51,7 +52,9 @@ export function toSidebarDocItemLinkProp({
51
52
  };
52
53
  }
53
54
 
54
- export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars {
55
+ export function toSidebarsProp(
56
+ loadedVersion: Pick<LoadedVersion, 'docs' | 'sidebars'>,
57
+ ): PropSidebars {
55
58
  const docsById = createDocsByIdIndex(loadedVersion.docs);
56
59
 
57
60
  function getDocById(docId: string): DocMetadata {
@@ -252,6 +252,7 @@ Available doc IDs:
252
252
  ...(categoryMetadata?.description && {
253
253
  description: categoryMetadata?.description,
254
254
  }),
255
+ ...(categoryMetadata?.key && {key: categoryMetadata?.key}),
255
256
  ...(link && {link}),
256
257
  };
257
258
  }
@@ -19,6 +19,7 @@ import type {Slugger} from '@docusaurus/utils';
19
19
  type Expand<T extends {[x: string]: unknown}> = {[P in keyof T]: T[P]};
20
20
 
21
21
  export type SidebarItemBase = {
22
+ key?: string;
22
23
  className?: string;
23
24
  customProps?: {[key: string]: unknown};
24
25
  };
@@ -28,8 +29,9 @@ export type SidebarItemDoc = SidebarItemBase & {
28
29
  label?: string;
29
30
  id: string;
30
31
  /**
31
- * This is an internal marker. Items with labels defined in the config needs
32
- * to be translated with JSON
32
+ * This is an internal marker set during the sidebar normalization process.
33
+ * Docs with labels defined in the config need to be translated with JSON.
34
+ * Otherwise, it's preferable to translate the MDX doc title or front matter.
33
35
  */
34
36
  translatable?: true;
35
37
  };
@@ -215,6 +217,7 @@ export type PropSidebarBreadcrumbsItem =
215
217
  | PropSidebarItemCategory;
216
218
 
217
219
  export type CategoryMetadataFile = {
220
+ key?: string;
218
221
  label?: string;
219
222
  position?: number;
220
223
  description?: string;
@@ -28,6 +28,7 @@ import type {
28
28
  // in normalization
29
29
 
30
30
  const sidebarItemBaseSchema = Joi.object<SidebarItemBase>({
31
+ key: Joi.string(),
31
32
  className: Joi.string(),
32
33
  customProps: Joi.object().unknown(),
33
34
  });
@@ -166,6 +167,7 @@ export function validateSidebars(sidebars: {
166
167
  }
167
168
 
168
169
  const categoryMetadataFileSchema = Joi.object<CategoryMetadataFile>({
170
+ key: Joi.string(),
169
171
  label: Joi.string(),
170
172
  description: Joi.string(),
171
173
  position: Joi.number(),
@@ -7,6 +7,7 @@
7
7
 
8
8
  import _ from 'lodash';
9
9
  import {mergeTranslations} from '@docusaurus/utils';
10
+ import logger from '@docusaurus/logger';
10
11
  import {CURRENT_VERSION_NAME} from './constants';
11
12
  import {
12
13
  collectSidebarCategories,
@@ -40,20 +41,53 @@ function getVersionFileName(versionName: string): string {
40
41
  return `version-${versionName}`;
41
42
  }
42
43
 
44
+ type TranslationMessageEntry = [string, TranslationMessage];
45
+
46
+ function ensureNoSidebarDuplicateEntries(
47
+ translationEntries: TranslationMessageEntry[],
48
+ ): void {
49
+ const grouped = _.groupBy(translationEntries, (entry) => entry[0]);
50
+ const duplicates = Object.entries(grouped).filter(
51
+ (entry) => entry[1].length > 1,
52
+ );
53
+
54
+ if (duplicates.length > 0) {
55
+ throw new Error(`Multiple docs sidebar items produce the same translation key.
56
+ - ${duplicates
57
+ .map(([translationKey, entries]) => {
58
+ return `${logger.code(translationKey)}: ${logger.num(
59
+ entries.length,
60
+ )} duplicates found:\n - ${entries
61
+ .map((duplicate) => {
62
+ const desc = duplicate[1].description;
63
+ return `${logger.name(duplicate[1].message)} ${
64
+ desc ? `(${logger.subdue(desc)})` : ''
65
+ }`;
66
+ })
67
+ .join('\n - ')}`;
68
+ })
69
+ .join('\n\n- ')}
70
+
71
+ To avoid translation key conflicts, use the ${logger.code(
72
+ 'key',
73
+ )} attribute on the sidebar items above to uniquely identify them.
74
+ `);
75
+ }
76
+ }
77
+
43
78
  function getSidebarTranslationFileContent(
44
79
  sidebar: Sidebar,
45
80
  sidebarName: string,
46
81
  ): TranslationFileContent {
47
- type TranslationMessageEntry = [string, TranslationMessage];
48
-
49
82
  const categories = collectSidebarCategories(sidebar);
50
83
 
51
- const categoryContent: TranslationFileContent = Object.fromEntries(
52
- categories.flatMap((category) => {
84
+ const categoryEntries: TranslationMessageEntry[] = categories.flatMap(
85
+ (category) => {
53
86
  const entries: TranslationMessageEntry[] = [];
87
+ const categoryKey = category.key ?? category.label;
54
88
 
55
89
  entries.push([
56
- `sidebar.${sidebarName}.category.${category.label}`,
90
+ `sidebar.${sidebarName}.category.${categoryKey}`,
57
91
  {
58
92
  message: category.label,
59
93
  description: `The label for category ${category.label} in sidebar ${sidebarName}`,
@@ -63,7 +97,7 @@ function getSidebarTranslationFileContent(
63
97
  if (category.link?.type === 'generated-index') {
64
98
  if (category.link.title) {
65
99
  entries.push([
66
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`,
100
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
67
101
  {
68
102
  message: category.link.title,
69
103
  description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
@@ -72,7 +106,7 @@ function getSidebarTranslationFileContent(
72
106
  }
73
107
  if (category.link.description) {
74
108
  entries.push([
75
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`,
109
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
76
110
  {
77
111
  message: category.link.description,
78
112
  description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
@@ -82,36 +116,40 @@ function getSidebarTranslationFileContent(
82
116
  }
83
117
 
84
118
  return entries;
85
- }),
119
+ },
86
120
  );
87
121
 
88
122
  const links = collectSidebarLinks(sidebar);
89
- const linksContent: TranslationFileContent = Object.fromEntries(
90
- links.map((link) => [
91
- `sidebar.${sidebarName}.link.${link.label}`,
123
+ const linksEntries: TranslationMessageEntry[] = links.map((link) => {
124
+ const linkKey = link.key ?? link.label;
125
+ return [
126
+ `sidebar.${sidebarName}.link.${linkKey}`,
92
127
  {
93
128
  message: link.label,
94
129
  description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
95
130
  },
96
- ]),
97
- );
131
+ ];
132
+ });
98
133
 
99
134
  const docs = collectSidebarDocItems(sidebar)
100
135
  .concat(collectSidebarRefs(sidebar))
101
136
  .filter((item) => item.translatable);
102
- const docLinksContent: TranslationFileContent = Object.fromEntries(
103
- docs.map((doc) => [
104
- `sidebar.${sidebarName}.doc.${doc.label!}`,
137
+ const docLinksEntries: TranslationMessageEntry[] = docs.map((doc) => {
138
+ const docKey = doc.key ?? doc.label!;
139
+ return [
140
+ `sidebar.${sidebarName}.doc.${docKey}`,
105
141
  {
106
142
  message: doc.label!,
107
143
  description: `The label for the doc item ${doc.label!} in sidebar ${sidebarName}, linking to the doc ${
108
144
  doc.id
109
145
  }`,
110
146
  },
111
- ]),
112
- );
147
+ ];
148
+ });
113
149
 
114
- return mergeTranslations([categoryContent, linksContent, docLinksContent]);
150
+ const allEntries = [...categoryEntries, ...linksEntries, ...docLinksEntries];
151
+ ensureNoSidebarDuplicateEntries(allEntries);
152
+ return Object.fromEntries(allEntries);
115
153
  }
116
154
 
117
155
  function translateSidebar({
@@ -150,27 +188,30 @@ function translateSidebar({
150
188
  return transformSidebarItems(sidebar, (item) => {
151
189
  if (item.type === 'category') {
152
190
  const link = transformSidebarCategoryLink(item);
191
+ const categoryKey = item.key ?? item.label;
153
192
  return {
154
193
  ...item,
155
194
  label:
156
- sidebarsTranslations[`sidebar.${sidebarName}.category.${item.label}`]
195
+ sidebarsTranslations[`sidebar.${sidebarName}.category.${categoryKey}`]
157
196
  ?.message ?? item.label,
158
197
  ...(link && {link}),
159
198
  };
160
199
  }
161
200
  if (item.type === 'link') {
201
+ const linkKey = item.key ?? item.label;
162
202
  return {
163
203
  ...item,
164
204
  label:
165
- sidebarsTranslations[`sidebar.${sidebarName}.link.${item.label}`]
205
+ sidebarsTranslations[`sidebar.${sidebarName}.link.${linkKey}`]
166
206
  ?.message ?? item.label,
167
207
  };
168
208
  }
169
209
  if ((item.type === 'doc' || item.type === 'ref') && item.translatable) {
210
+ const docKey = item.key ?? item.label!;
170
211
  return {
171
212
  ...item,
172
213
  label:
173
- sidebarsTranslations[`sidebar.${sidebarName}.doc.${item.label!}`]
214
+ sidebarsTranslations[`sidebar.${sidebarName}.doc.${docKey}`]
174
215
  ?.message ?? item.label,
175
216
  };
176
217
  }