@docusaurus/plugin-content-docs 2.0.0-beta.16 → 2.0.0-beta.19

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 (93) hide show
  1. package/lib/categoryGeneratedIndex.d.ts +1 -1
  2. package/lib/categoryGeneratedIndex.js +5 -7
  3. package/lib/cli.d.ts +3 -2
  4. package/lib/cli.js +49 -41
  5. package/lib/client/docsClientUtils.d.ts +8 -5
  6. package/lib/client/docsClientUtils.js +13 -15
  7. package/lib/client/index.d.ts +12 -9
  8. package/lib/client/index.js +30 -33
  9. package/lib/constants.d.ts +4 -0
  10. package/lib/constants.js +4 -1
  11. package/lib/docs.d.ts +8 -15
  12. package/lib/docs.js +34 -39
  13. package/lib/{docFrontMatter.d.ts → frontMatter.d.ts} +4 -2
  14. package/lib/{docFrontMatter.js → frontMatter.js} +3 -0
  15. package/lib/globalData.d.ts +3 -7
  16. package/lib/globalData.js +9 -13
  17. package/lib/index.d.ts +1 -2
  18. package/lib/index.js +33 -26
  19. package/lib/lastUpdate.d.ts +4 -6
  20. package/lib/lastUpdate.js +14 -5
  21. package/lib/markdown/index.js +1 -1
  22. package/lib/markdown/linkify.js +5 -2
  23. package/lib/numberPrefix.js +16 -22
  24. package/lib/options.d.ts +3 -5
  25. package/lib/options.js +6 -5
  26. package/lib/props.d.ts +3 -3
  27. package/lib/props.js +10 -10
  28. package/lib/routes.d.ts +5 -4
  29. package/lib/routes.js +10 -24
  30. package/lib/server-export.d.ts +2 -1
  31. package/lib/server-export.js +3 -4
  32. package/lib/sidebars/generator.js +63 -44
  33. package/lib/sidebars/index.js +20 -16
  34. package/lib/sidebars/normalization.js +3 -3
  35. package/lib/sidebars/postProcessor.js +18 -25
  36. package/lib/sidebars/processor.d.ts +3 -1
  37. package/lib/sidebars/processor.js +17 -6
  38. package/lib/sidebars/types.d.ts +31 -19
  39. package/lib/sidebars/utils.d.ts +17 -6
  40. package/lib/sidebars/utils.js +27 -37
  41. package/lib/sidebars/validation.d.ts +3 -1
  42. package/lib/sidebars/validation.js +1 -0
  43. package/lib/slug.d.ts +1 -2
  44. package/lib/slug.js +4 -5
  45. package/lib/tags.d.ts +2 -1
  46. package/lib/tags.js +2 -2
  47. package/lib/translations.d.ts +3 -3
  48. package/lib/translations.js +28 -81
  49. package/lib/types.d.ts +10 -95
  50. package/lib/versions/files.d.ts +44 -0
  51. package/lib/versions/files.js +142 -0
  52. package/lib/versions/index.d.ts +36 -0
  53. package/lib/versions/index.js +155 -0
  54. package/lib/versions/validation.d.ts +17 -0
  55. package/lib/versions/validation.js +71 -0
  56. package/package.json +14 -12
  57. package/src/categoryGeneratedIndex.ts +10 -9
  58. package/src/cli.ts +64 -69
  59. package/src/client/docsClientUtils.ts +14 -16
  60. package/src/client/index.ts +42 -43
  61. package/src/constants.ts +4 -2
  62. package/src/deps.d.ts +1 -1
  63. package/src/docs.ts +48 -51
  64. package/src/{docFrontMatter.ts → frontMatter.ts} +9 -6
  65. package/src/globalData.ts +15 -16
  66. package/src/index.ts +45 -40
  67. package/src/lastUpdate.ts +20 -8
  68. package/src/markdown/index.ts +1 -3
  69. package/src/markdown/linkify.ts +6 -3
  70. package/src/numberPrefix.ts +18 -28
  71. package/src/options.ts +6 -8
  72. package/src/plugin-content-docs.d.ts +457 -116
  73. package/src/props.ts +12 -9
  74. package/src/routes.ts +13 -39
  75. package/src/server-export.ts +1 -3
  76. package/src/sidebars/generator.ts +88 -59
  77. package/src/sidebars/index.ts +20 -15
  78. package/src/sidebars/normalization.ts +1 -1
  79. package/src/sidebars/postProcessor.ts +6 -11
  80. package/src/sidebars/processor.ts +27 -14
  81. package/src/sidebars/types.ts +25 -23
  82. package/src/sidebars/utils.ts +45 -46
  83. package/src/sidebars/validation.ts +4 -3
  84. package/src/slug.ts +7 -6
  85. package/src/tags.ts +3 -2
  86. package/src/translations.ts +32 -84
  87. package/src/types.ts +15 -107
  88. package/src/versions/files.ts +220 -0
  89. package/src/versions/index.ts +247 -0
  90. package/src/versions/validation.ts +113 -0
  91. package/lib/versions.d.ts +0 -41
  92. package/lib/versions.js +0 -329
  93. package/src/versions.ts +0 -606
package/src/props.ts CHANGED
@@ -5,13 +5,12 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {LoadedVersion, VersionTag, DocMetadata} from './types';
8
+ import type {VersionTag} from './types';
9
9
  import type {
10
10
  SidebarItemDoc,
11
11
  SidebarItem,
12
12
  SidebarItemCategory,
13
13
  SidebarItemCategoryLink,
14
- PropVersionDocs,
15
14
  } from './sidebars/types';
16
15
  import type {
17
16
  PropSidebars,
@@ -21,6 +20,9 @@ import type {
21
20
  PropTagDocList,
22
21
  PropTagDocListDoc,
23
22
  PropSidebarItemLink,
23
+ PropVersionDocs,
24
+ DocMetadata,
25
+ LoadedVersion,
24
26
  } from '@docusaurus/plugin-content-docs';
25
27
  import _ from 'lodash';
26
28
  import {createDocsByIdIndex} from './docs';
@@ -119,10 +121,10 @@ export function toVersionMetadataProp(
119
121
  return {
120
122
  pluginId,
121
123
  version: loadedVersion.versionName,
122
- label: loadedVersion.versionLabel,
123
- banner: loadedVersion.versionBanner,
124
- badge: loadedVersion.versionBadge,
125
- className: loadedVersion.versionClassName,
124
+ label: loadedVersion.label,
125
+ banner: loadedVersion.banner,
126
+ badge: loadedVersion.badge,
127
+ className: loadedVersion.className,
126
128
  isLast: loadedVersion.isLast,
127
129
  docsSidebars: toSidebarsProp(loadedVersion),
128
130
  docs: toVersionDocsProp(loadedVersion),
@@ -136,7 +138,7 @@ export function toTagDocListProp({
136
138
  }: {
137
139
  allTagsPath: string;
138
140
  tag: VersionTag;
139
- docs: Pick<DocMetadata, 'id' | 'title' | 'description' | 'permalink'>[];
141
+ docs: DocMetadata[];
140
142
  }): PropTagDocList {
141
143
  function toDocListProp(): PropTagDocListDoc[] {
142
144
  const list = _.compact(
@@ -153,9 +155,10 @@ export function toTagDocListProp({
153
155
  }
154
156
 
155
157
  return {
156
- name: tag.name,
158
+ label: tag.label,
157
159
  permalink: tag.permalink,
158
- docs: toDocListProp(),
159
160
  allTagsPath,
161
+ count: tag.docIds.length,
162
+ items: toDocListProp(),
160
163
  };
161
164
  }
package/src/routes.ts CHANGED
@@ -7,12 +7,11 @@
7
7
 
8
8
  import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types';
9
9
  import {docuHash, createSlugger} from '@docusaurus/utils';
10
+ import type {FullVersion} from './types';
10
11
  import type {
11
12
  CategoryGeneratedIndexMetadata,
12
13
  DocMetadata,
13
- LoadedVersion,
14
- } from './types';
15
- import type {PropCategoryGeneratedIndex} from '@docusaurus/plugin-content-docs';
14
+ } from '@docusaurus/plugin-content-docs';
16
15
  import {toVersionMetadataProp} from './props';
17
16
  import logger from '@docusaurus/logger';
18
17
 
@@ -22,7 +21,7 @@ export async function createCategoryGeneratedIndexRoutes({
22
21
  docCategoryGeneratedIndexComponent,
23
22
  aliasedSource,
24
23
  }: {
25
- version: LoadedVersion;
24
+ version: FullVersion;
26
25
  actions: PluginContentLoadedActions;
27
26
  docCategoryGeneratedIndexComponent: string;
28
27
  aliasedSource: (str: string) => string;
@@ -32,42 +31,19 @@ export async function createCategoryGeneratedIndexRoutes({
32
31
  async function createCategoryGeneratedIndexRoute(
33
32
  categoryGeneratedIndex: CategoryGeneratedIndexMetadata,
34
33
  ): Promise<RouteConfig> {
35
- const {
36
- sidebar,
37
- title,
38
- description,
39
- slug,
40
- permalink,
41
- previous,
42
- next,
43
- image,
44
- keywords,
45
- } = categoryGeneratedIndex;
34
+ const {sidebar, ...prop} = categoryGeneratedIndex;
46
35
 
47
36
  const propFileName = slugs.slug(
48
- `${version.versionPath}-${categoryGeneratedIndex.sidebar}-category-${categoryGeneratedIndex.title}`,
37
+ `${version.path}-${categoryGeneratedIndex.sidebar}-category-${categoryGeneratedIndex.title}`,
49
38
  );
50
39
 
51
- const prop: PropCategoryGeneratedIndex = {
52
- title,
53
- description,
54
- slug,
55
- permalink,
56
- image,
57
- keywords,
58
- navigation: {
59
- previous,
60
- next,
61
- },
62
- };
63
-
64
40
  const propData = await actions.createData(
65
41
  `${docuHash(`category/${propFileName}`)}.json`,
66
42
  JSON.stringify(prop, null, 2),
67
43
  );
68
44
 
69
45
  return {
70
- path: permalink,
46
+ path: categoryGeneratedIndex.permalink,
71
47
  component: docCategoryGeneratedIndexComponent,
72
48
  exact: true,
73
49
  modules: {
@@ -123,7 +99,7 @@ export async function createDocRoutes({
123
99
  }
124
100
 
125
101
  export async function createVersionRoutes({
126
- loadedVersion,
102
+ version,
127
103
  actions,
128
104
  docItemComponent,
129
105
  docLayoutComponent,
@@ -131,7 +107,7 @@ export async function createVersionRoutes({
131
107
  pluginId,
132
108
  aliasedSource,
133
109
  }: {
134
- loadedVersion: LoadedVersion;
110
+ version: FullVersion;
135
111
  actions: PluginContentLoadedActions;
136
112
  docLayoutComponent: string;
137
113
  docItemComponent: string;
@@ -139,7 +115,7 @@ export async function createVersionRoutes({
139
115
  pluginId: string;
140
116
  aliasedSource: (str: string) => string;
141
117
  }): Promise<void> {
142
- async function doCreateVersionRoutes(version: LoadedVersion): Promise<void> {
118
+ async function doCreateVersionRoutes(): Promise<void> {
143
119
  const versionMetadata = toVersionMetadataProp(pluginId, version);
144
120
  const versionMetadataPropPath = await actions.createData(
145
121
  `${docuHash(`version-${version.versionName}-metadata-prop`)}.json`,
@@ -162,12 +138,10 @@ export async function createVersionRoutes({
162
138
  }
163
139
 
164
140
  actions.addRoute({
165
- path: version.versionPath,
166
- // allow matching /docs/* as well
141
+ path: version.path,
142
+ // Allow matching /docs/* since this is the wrapping route
167
143
  exact: false,
168
- // main docs component (DocPage)
169
144
  component: docLayoutComponent,
170
- // sub-routes for each doc
171
145
  routes: await createVersionSubRoutes(),
172
146
  modules: {
173
147
  versionMetadata: aliasedSource(versionMetadataPropPath),
@@ -177,9 +151,9 @@ export async function createVersionRoutes({
177
151
  }
178
152
 
179
153
  try {
180
- return await doCreateVersionRoutes(loadedVersion);
154
+ return await doCreateVersionRoutes();
181
155
  } catch (err) {
182
- logger.error`Can't create version routes for version name=${loadedVersion.versionName}`;
156
+ logger.error`Can't create version routes for version name=${version.versionName}`;
183
157
  throw err;
184
158
  }
185
159
  }
@@ -18,7 +18,5 @@ export {
18
18
  getDefaultVersionBanner,
19
19
  getVersionBadge,
20
20
  getVersionBanner,
21
- getVersionsFilePath,
22
- readVersionsFile,
23
- readVersionNames,
24
21
  } from './versions';
22
+ export {readVersionNames} from './versions/files';
@@ -14,14 +14,12 @@ import type {
14
14
  SidebarItemCategoryLinkConfig,
15
15
  } from './types';
16
16
  import _ from 'lodash';
17
- import {addTrailingSlash, posixPath} from '@docusaurus/utils';
17
+ import {addTrailingSlash} from '@docusaurus/utils';
18
18
  import logger from '@docusaurus/logger';
19
19
  import path from 'path';
20
20
  import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs';
21
21
 
22
22
  const BreadcrumbSeparator = '/';
23
- // To avoid possible name clashes with a folder of the same name as the ID
24
- const docIdPrefix = '$doc$/';
25
23
 
26
24
  // Just an alias to the make code more explicit
27
25
  function getLocalDocId(docId: string): string {
@@ -31,16 +29,20 @@ function getLocalDocId(docId: string): string {
31
29
  export const CategoryMetadataFilenameBase = '_category_';
32
30
  export const CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
33
31
 
34
- type WithPosition<T> = T & {position?: number};
32
+ type WithPosition<T> = T & {
33
+ position?: number;
34
+ /** The source is the file/folder name */
35
+ source?: string;
36
+ };
35
37
 
36
38
  /**
37
39
  * A representation of the fs structure. For each object entry:
38
40
  * If it's a folder, the key is the directory name, and value is the directory
39
- * content; If it's a doc file, the key is the doc id prefixed with '$doc$/',
40
- * and value is null
41
+ * content; If it's a doc file, the key is the doc's source file name, and value
42
+ * is the doc ID
41
43
  */
42
44
  type Dir = {
43
- [item: string]: Dir | null;
45
+ [item: string]: Dir | string;
44
46
  };
45
47
 
46
48
  // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
@@ -58,9 +60,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
58
60
  const doc = findDoc(docId);
59
61
  if (!doc) {
60
62
  throw new Error(
61
- `Can't find any doc with id=${docId}.\nAvailable doc ids:\n- ${Object.keys(
62
- docsById,
63
- ).join('\n- ')}`,
63
+ `Can't find any doc with ID ${docId}.
64
+ Available doc IDs:
65
+ - ${Object.keys(docsById).join('\n- ')}`,
64
66
  );
65
67
  }
66
68
  return doc;
@@ -74,9 +76,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
74
76
  return (
75
77
  // Doc at the root of the autogenerated sidebar dir
76
78
  doc.sourceDirName === autogenDir ||
77
- // autogen dir is . and doc is in subfolder
79
+ // Autogen dir is . and doc is in subfolder
78
80
  autogenDir === '.' ||
79
- // autogen dir is not . and doc is in subfolder
81
+ // Autogen dir is not . and doc is in subfolder
80
82
  // "api/myDoc" startsWith "api/" (note "api2/myDoc" is not included)
81
83
  doc.sourceDirName.startsWith(addTrailingSlash(autogenDir))
82
84
  );
@@ -108,14 +110,16 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
108
110
  const treeRoot: Dir = {};
109
111
  docs.forEach((doc) => {
110
112
  const breadcrumb = getRelativeBreadcrumb(doc);
111
- let currentDir = treeRoot; // We walk down the file's path to generate the fs structure
113
+ // We walk down the file's path to generate the fs structure
114
+ let currentDir = treeRoot;
112
115
  breadcrumb.forEach((dir) => {
113
116
  if (typeof currentDir[dir] === 'undefined') {
114
117
  currentDir[dir] = {}; // Create new folder.
115
118
  }
116
- currentDir = currentDir[dir]!; // Go into the subdirectory.
119
+ currentDir = currentDir[dir] as Dir; // Go into the subdirectory.
117
120
  });
118
- currentDir[`${docIdPrefix}${doc.id}`] = null; // We've walked through the file path. Register the file in this directory.
121
+ // We've walked through the path. Register the file in this directory.
122
+ currentDir[path.basename(doc.source)] = doc.id;
119
123
  });
120
124
  return treeRoot;
121
125
  }
@@ -126,8 +130,12 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
126
130
  */
127
131
  function generateSidebar(
128
132
  fsModel: Dir,
129
- ): Promise<WithPosition<NormalizedSidebarItem>[]> {
130
- function createDocItem(id: string): WithPosition<SidebarItemDoc> {
133
+ ): WithPosition<NormalizedSidebarItem>[] {
134
+ function createDocItem(
135
+ id: string,
136
+ fullPath: string,
137
+ fileName: string,
138
+ ): WithPosition<SidebarItemDoc> {
131
139
  const {
132
140
  sidebarPosition: position,
133
141
  frontMatter: {sidebar_label: label, sidebar_class_name: className},
@@ -136,25 +144,22 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
136
144
  type: 'doc',
137
145
  id,
138
146
  position,
147
+ source: fileName,
139
148
  // We don't want these fields to magically appear in the generated
140
149
  // sidebar
141
150
  ...(label !== undefined && {label}),
142
151
  ...(className !== undefined && {className}),
143
152
  };
144
153
  }
145
- async function createCategoryItem(
154
+ function createCategoryItem(
146
155
  dir: Dir,
147
156
  fullPath: string,
148
157
  folderName: string,
149
- ): Promise<WithPosition<NormalizedSidebarItemCategory>> {
158
+ ): WithPosition<NormalizedSidebarItemCategory> {
150
159
  const categoryMetadata =
151
- categoriesMetadata[posixPath(path.join(autogenDir, fullPath))];
152
- const className = categoryMetadata?.className;
153
- const {filename, numberPrefix} = numberPrefixParser(folderName);
154
- const allItems = await Promise.all(
155
- Object.entries(dir).map(([key, content]) =>
156
- dirToItem(content, key, `${fullPath}/${key}`),
157
- ),
160
+ categoriesMetadata[path.posix.join(autogenDir, fullPath)];
161
+ const allItems = Object.entries(dir).map(([key, content]) =>
162
+ dirToItem(content, key, `${fullPath}/${key}`),
158
163
  );
159
164
 
160
165
  // Try to match a doc inside the category folder,
@@ -176,59 +181,83 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
176
181
  });
177
182
  }
178
183
 
179
- function getCategoryLinkedDocId(): string | undefined {
180
- const link = categoryMetadata?.link;
181
- if (link !== undefined) {
182
- if (link && link.type === 'doc') {
183
- return findDocByLocalId(link.id)?.id || getDoc(link.id).id;
184
+ // In addition to the ID, this function also retrieves metadata of the
185
+ // linked doc that could be used as fallback values for category metadata
186
+ function getCategoryLinkedDocMetadata():
187
+ | {
188
+ id: string;
189
+ position?: number;
190
+ label?: string;
191
+ customProps?: {[key: string]: unknown};
192
+ className?: string;
184
193
  }
194
+ | undefined {
195
+ const link = categoryMetadata?.link;
196
+ if (link !== undefined && link?.type !== 'doc') {
185
197
  // If a link is explicitly specified, we won't apply conventions
186
198
  return undefined;
187
199
  }
188
- // Apply default convention to pick index.md, README.md or
189
- // <categoryName>.md as the category doc
190
- return findConventionalCategoryDocLink()?.id;
200
+ const id = link
201
+ ? findDocByLocalId(link.id)?.id ?? getDoc(link.id).id
202
+ : findConventionalCategoryDocLink()?.id;
203
+ if (!id) {
204
+ return undefined;
205
+ }
206
+ const doc = getDoc(id);
207
+ return {
208
+ id,
209
+ position: doc.sidebarPosition,
210
+ label: doc.frontMatter.sidebar_label ?? doc.title,
211
+ customProps: doc.frontMatter.sidebar_custom_props,
212
+ className: doc.frontMatter.sidebar_class_name,
213
+ };
191
214
  }
192
-
193
- const categoryLinkedDocId = getCategoryLinkedDocId();
194
-
215
+ const categoryLinkedDoc = getCategoryLinkedDocMetadata();
195
216
  const link: SidebarItemCategoryLinkConfig | null | undefined =
196
- categoryLinkedDocId
217
+ categoryLinkedDoc
197
218
  ? {
198
219
  type: 'doc',
199
- id: categoryLinkedDocId, // We "remap" a potentially "local id" to a "qualified id"
220
+ id: categoryLinkedDoc.id, // We "remap" a potentially "local id" to a "qualified id"
200
221
  }
201
222
  : categoryMetadata?.link;
202
-
203
223
  // If a doc is linked, remove it from the category subItems
204
224
  const items = allItems.filter(
205
- (item) => !(item.type === 'doc' && item.id === categoryLinkedDocId),
225
+ (item) => !(item.type === 'doc' && item.id === categoryLinkedDoc?.id),
206
226
  );
207
227
 
228
+ const className =
229
+ categoryMetadata?.className ?? categoryLinkedDoc?.className;
230
+ const customProps =
231
+ categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
232
+ const {filename, numberPrefix} = numberPrefixParser(folderName);
233
+
208
234
  return {
209
235
  type: 'category',
210
- label: categoryMetadata?.label ?? filename,
236
+ label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
211
237
  collapsible: categoryMetadata?.collapsible,
212
238
  collapsed: categoryMetadata?.collapsed,
213
- position: categoryMetadata?.position ?? numberPrefix,
239
+ position:
240
+ categoryMetadata?.position ??
241
+ categoryLinkedDoc?.position ??
242
+ numberPrefix,
243
+ source: folderName,
244
+ ...(customProps !== undefined && {customProps}),
214
245
  ...(className !== undefined && {className}),
215
246
  items,
216
247
  ...(link && {link}),
217
248
  };
218
249
  }
219
- async function dirToItem(
220
- dir: Dir | null, // The directory item to be transformed.
221
- itemKey: string, // For docs, it's the doc ID; for categories, it's used to generate the next `relativePath`.
250
+ function dirToItem(
251
+ dir: Dir | string, // The directory item to be transformed.
252
+ itemKey: string, // File/folder name; for categories, it's used to generate the next `relativePath`.
222
253
  fullPath: string, // `dir`'s full path relative to the autogen dir.
223
- ): Promise<WithPosition<NormalizedSidebarItem>> {
224
- return dir
254
+ ): WithPosition<NormalizedSidebarItem> {
255
+ return typeof dir === 'object'
225
256
  ? createCategoryItem(dir, fullPath, itemKey)
226
- : createDocItem(itemKey.substring(docIdPrefix.length));
257
+ : createDocItem(dir, fullPath, itemKey);
227
258
  }
228
- return Promise.all(
229
- Object.entries(fsModel).map(([key, content]) =>
230
- dirToItem(content, key, key),
231
- ),
259
+ return Object.entries(fsModel).map(([key, content]) =>
260
+ dirToItem(content, key, key),
232
261
  );
233
262
  }
234
263
 
@@ -248,16 +277,16 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
248
277
  }
249
278
  return item;
250
279
  });
251
- const sortedSidebarItems = _.sortBy(
252
- processedSidebarItems,
253
- (item) => item.position,
254
- );
255
- return sortedSidebarItems.map(({position, ...item}) => item);
280
+ const sortedSidebarItems = _.sortBy(processedSidebarItems, [
281
+ 'position',
282
+ 'source',
283
+ ]);
284
+ return sortedSidebarItems.map(({position, source, ...item}) => item);
256
285
  }
257
286
  // TODO: the whole code is designed for pipeline operator
258
287
  const docs = getAutogenDocs();
259
288
  const fsModel = treeify(docs);
260
- const sidebarWithPosition = await generateSidebar(fsModel);
289
+ const sidebarWithPosition = generateSidebar(fsModel);
261
290
  const sortedSidebar = sortItems(sidebarWithPosition);
262
291
  return sortedSidebar;
263
292
  };
@@ -32,7 +32,6 @@ export const DefaultSidebars: SidebarsConfig = {
32
32
  export const DisabledSidebars: SidebarsConfig = {};
33
33
 
34
34
  // If a path is provided, make it absolute
35
- // use this before loadSidebars()
36
35
  export function resolveSidebarPathOption(
37
36
  siteDir: string,
38
37
  sidebarPathOption: PluginOptions['sidebarPath'],
@@ -49,7 +48,7 @@ async function readCategoriesMetadata(contentPath: string) {
49
48
  const categoryToFile = _.groupBy(categoryFiles, path.dirname);
50
49
  return combinePromises(
51
50
  _.mapValues(categoryToFile, async (files, folder) => {
52
- const [filePath] = files;
51
+ const filePath = files[0]!;
53
52
  if (files.length > 1) {
54
53
  logger.warn`There are more than one category metadata files for path=${folder}: ${files.join(
55
54
  ', ',
@@ -93,21 +92,27 @@ export async function loadSidebarsFileUnsafe(
93
92
  return importFresh(sidebarFilePath);
94
93
  }
95
94
 
96
- // Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
97
95
  export async function loadSidebars(
98
96
  sidebarFilePath: string | false | undefined,
99
97
  options: SidebarProcessorParams,
100
98
  ): Promise<Sidebars> {
101
- const sidebarsConfig = await loadSidebarsFileUnsafe(sidebarFilePath);
102
- const normalizedSidebars = normalizeSidebars(sidebarsConfig);
103
- validateSidebars(normalizedSidebars);
104
- const categoriesMetadata = await readCategoriesMetadata(
105
- options.version.contentPath,
106
- );
107
- const processedSidebars = await processSidebars(
108
- normalizedSidebars,
109
- categoriesMetadata,
110
- options,
111
- );
112
- return postProcessSidebars(processedSidebars, options);
99
+ try {
100
+ const sidebarsConfig = await loadSidebarsFileUnsafe(sidebarFilePath);
101
+ const normalizedSidebars = normalizeSidebars(sidebarsConfig);
102
+ validateSidebars(normalizedSidebars);
103
+ const categoriesMetadata = await readCategoriesMetadata(
104
+ options.version.contentPath,
105
+ );
106
+ const processedSidebars = await processSidebars(
107
+ normalizedSidebars,
108
+ categoriesMetadata,
109
+ options,
110
+ );
111
+ return postProcessSidebars(processedSidebars, options);
112
+ } catch (err) {
113
+ logger.error`Sidebars file at path=${
114
+ sidebarFilePath as string
115
+ } failed to be loaded.`;
116
+ throw err;
117
+ }
113
118
  }
@@ -65,7 +65,7 @@ function normalizeSidebar(
65
65
  throw new Error(
66
66
  logger.interpolate`Invalid sidebar items collection code=${JSON.stringify(
67
67
  sidebar,
68
- )} in ${place}: it must either be an array of sidebar items or a shorthand notation (which doesn't contain a code=${'type'} property). See path=${'https://docusaurus.io/docs/sidebar/items'} for all valid syntaxes.`,
68
+ )} in ${place}: it must either be an array of sidebar items or a shorthand notation (which doesn't contain a code=${'type'} property). See url=${'https://docusaurus.io/docs/sidebar/items'} for all valid syntaxes.`,
69
69
  );
70
70
  }
71
71
 
@@ -22,11 +22,11 @@ function normalizeCategoryLink(
22
22
  params: SidebarProcessorParams,
23
23
  ): SidebarItemCategoryLink | undefined {
24
24
  if (category.link?.type === 'generated-index') {
25
- // default slug logic can be improved
25
+ // Default slug logic can be improved
26
26
  const getDefaultSlug = () =>
27
27
  `/category/${params.categoryLabelSlugger.slug(category.label)}`;
28
28
  const slug = category.link.slug ?? getDefaultSlug();
29
- const permalink = normalizeUrl([params.version.versionPath, slug]);
29
+ const permalink = normalizeUrl([params.version.path, slug]);
30
30
  return {
31
31
  ...category.link,
32
32
  slug,
@@ -58,22 +58,17 @@ function postProcessSidebarItem(
58
58
  `Sidebar category ${item.label} has neither any subitem nor a link. This makes this item not able to link to anything.`,
59
59
  );
60
60
  }
61
- switch (category.link.type) {
62
- case 'doc':
63
- return {
61
+ return category.link.type === 'doc'
62
+ ? {
64
63
  type: 'doc',
65
64
  label: category.label,
66
65
  id: category.link.id,
67
- };
68
- case 'generated-index':
69
- return {
66
+ }
67
+ : {
70
68
  type: 'link',
71
69
  label: category.label,
72
70
  href: category.link.permalink,
73
71
  };
74
- default:
75
- throw new Error('Unexpected sidebar category link type');
76
- }
77
72
  }
78
73
  // A non-collapsible category can't be collapsed!
79
74
  if (category.collapsible === false) {
@@ -5,7 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {DocMetadataBase, VersionMetadata} from '../types';
8
+ import type {
9
+ DocMetadataBase,
10
+ VersionMetadata,
11
+ } from '@docusaurus/plugin-content-docs';
9
12
  import type {
10
13
  NormalizedSidebarItem,
11
14
  NormalizedSidebar,
@@ -23,7 +26,7 @@ import {DefaultSidebarItemsGenerator} from './generator';
23
26
  import {validateSidebars} from './validation';
24
27
  import _ from 'lodash';
25
28
  import combinePromises from 'combine-promises';
26
- import {isCategoryIndex} from '../docs';
29
+ import {getDocIds, isCategoryIndex} from '../docs';
27
30
 
28
31
  function toSidebarItemsGeneratorDoc(
29
32
  doc: DocMetadataBase,
@@ -31,6 +34,7 @@ function toSidebarItemsGeneratorDoc(
31
34
  return _.pick(doc, [
32
35
  'id',
33
36
  'unversionedId',
37
+ 'title',
34
38
  'frontMatter',
35
39
  'source',
36
40
  'sourceDirName',
@@ -48,16 +52,11 @@ function toSidebarItemsGeneratorVersion(
48
52
  // post-processing checks
49
53
  async function processSidebar(
50
54
  unprocessedSidebar: NormalizedSidebar,
51
- categoriesMetadata: Record<string, CategoryMetadataFile>,
55
+ categoriesMetadata: {[filePath: string]: CategoryMetadataFile},
52
56
  params: SidebarProcessorParams,
53
57
  ): Promise<ProcessedSidebar> {
54
- const {
55
- sidebarItemsGenerator,
56
- numberPrefixParser,
57
- docs,
58
- version,
59
- sidebarOptions,
60
- } = params;
58
+ const {sidebarItemsGenerator, numberPrefixParser, docs, drafts, version} =
59
+ params;
61
60
 
62
61
  // Just a minor lazy transformation optimization
63
62
  const getSidebarItemsGeneratorDocsAndVersion = _.memoize(() => ({
@@ -74,7 +73,6 @@ async function processSidebar(
74
73
  defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
75
74
  isCategoryIndex,
76
75
  ...getSidebarItemsGeneratorDocsAndVersion(),
77
- options: sidebarOptions,
78
76
  categoriesMetadata,
79
77
  });
80
78
  // Process again... weird but sidebar item generated might generate some
@@ -84,6 +82,19 @@ async function processSidebar(
84
82
  return processItems(generatedItems);
85
83
  }
86
84
 
85
+ const draftIds = new Set(drafts.flatMap(getDocIds));
86
+
87
+ const isDraftItem = (item: NormalizedSidebarItem): boolean => {
88
+ if (item.type === 'doc' || item.type === 'ref') {
89
+ return draftIds.has(item.id);
90
+ }
91
+ // If a category only contains draft items, it should be filtered entirely.
92
+ if (item.type === 'category') {
93
+ return item.items.every(isDraftItem);
94
+ }
95
+ return false;
96
+ };
97
+
87
98
  async function processItem(
88
99
  item: NormalizedSidebarItem,
89
100
  ): Promise<ProcessedSidebarItem[]> {
@@ -91,7 +102,7 @@ async function processSidebar(
91
102
  return [
92
103
  {
93
104
  ...item,
94
- items: (await Promise.all(item.items.map(processItem))).flat(),
105
+ items: await processItems(item.items),
95
106
  },
96
107
  ];
97
108
  }
@@ -104,7 +115,9 @@ async function processSidebar(
104
115
  async function processItems(
105
116
  items: NormalizedSidebarItem[],
106
117
  ): Promise<ProcessedSidebarItem[]> {
107
- return (await Promise.all(items.map(processItem))).flat();
118
+ return (
119
+ await Promise.all(items.filter((i) => !isDraftItem(i)).map(processItem))
120
+ ).flat();
108
121
  }
109
122
 
110
123
  const processedSidebar = await processItems(unprocessedSidebar);
@@ -113,7 +126,7 @@ async function processSidebar(
113
126
 
114
127
  export async function processSidebars(
115
128
  unprocessedSidebars: NormalizedSidebars,
116
- categoriesMetadata: Record<string, CategoryMetadataFile>,
129
+ categoriesMetadata: {[filePath: string]: CategoryMetadataFile},
117
130
  params: SidebarProcessorParams,
118
131
  ): Promise<ProcessedSidebars> {
119
132
  const processedSidebars = await combinePromises(