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

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 (69) hide show
  1. package/lib/cli.d.ts +1 -1
  2. package/lib/cli.js +18 -14
  3. package/lib/client/docsClientUtils.js +2 -2
  4. package/lib/client/index.d.ts +13 -1
  5. package/lib/client/index.js +66 -1
  6. package/lib/docFrontMatter.js +1 -0
  7. package/lib/docs.d.ts +2 -1
  8. package/lib/docs.js +23 -22
  9. package/lib/globalData.js +3 -2
  10. package/lib/index.js +11 -6
  11. package/lib/lastUpdate.js +14 -27
  12. package/lib/numberPrefix.js +7 -6
  13. package/lib/options.js +5 -2
  14. package/lib/props.js +15 -10
  15. package/lib/routes.js +6 -4
  16. package/lib/server-export.d.ts +8 -0
  17. package/lib/server-export.js +23 -0
  18. package/lib/sidebars/generator.d.ts +1 -9
  19. package/lib/sidebars/generator.js +26 -50
  20. package/lib/sidebars/index.d.ts +2 -5
  21. package/lib/sidebars/index.js +35 -20
  22. package/lib/sidebars/normalization.d.ts +2 -3
  23. package/lib/sidebars/normalization.js +17 -39
  24. package/lib/sidebars/postProcessor.d.ts +8 -0
  25. package/lib/sidebars/postProcessor.js +72 -0
  26. package/lib/sidebars/processor.d.ts +2 -13
  27. package/lib/sidebars/processor.js +25 -33
  28. package/lib/sidebars/types.d.ts +44 -10
  29. package/lib/sidebars/utils.js +53 -61
  30. package/lib/sidebars/validation.d.ts +2 -3
  31. package/lib/sidebars/validation.js +25 -30
  32. package/lib/slug.js +6 -8
  33. package/lib/tags.js +3 -2
  34. package/lib/translations.js +18 -17
  35. package/lib/types.d.ts +3 -6
  36. package/lib/versions.d.ts +25 -1
  37. package/lib/versions.js +25 -29
  38. package/package.json +19 -18
  39. package/src/cli.ts +25 -16
  40. package/src/client/docsClientUtils.ts +2 -2
  41. package/src/client/index.ts +97 -1
  42. package/src/docFrontMatter.ts +1 -0
  43. package/src/docs.ts +25 -20
  44. package/src/globalData.ts +2 -2
  45. package/src/index.ts +17 -7
  46. package/src/lastUpdate.ts +12 -33
  47. package/src/numberPrefix.ts +7 -6
  48. package/src/options.ts +5 -2
  49. package/src/plugin-content-docs.d.ts +16 -60
  50. package/src/props.ts +17 -12
  51. package/src/routes.ts +6 -4
  52. package/src/server-export.ts +24 -0
  53. package/src/sidebars/README.md +9 -0
  54. package/src/sidebars/generator.ts +50 -94
  55. package/src/sidebars/index.ts +50 -32
  56. package/src/sidebars/normalization.ts +22 -50
  57. package/src/sidebars/postProcessor.ts +94 -0
  58. package/src/sidebars/processor.ts +37 -66
  59. package/src/sidebars/types.ts +68 -10
  60. package/src/sidebars/utils.ts +63 -68
  61. package/src/sidebars/validation.ts +53 -53
  62. package/src/slug.ts +9 -10
  63. package/src/tags.ts +2 -2
  64. package/src/translations.ts +19 -16
  65. package/src/types.ts +3 -10
  66. package/src/versions.ts +30 -34
  67. package/lib/client/globalDataHooks.d.ts +0 -19
  68. package/lib/client/globalDataHooks.js +0 -76
  69. package/src/client/globalDataHooks.ts +0 -107
@@ -7,14 +7,18 @@
7
7
 
8
8
  import fs from 'fs-extra';
9
9
  import importFresh from 'import-fresh';
10
- import type {SidebarsConfig, Sidebars, NormalizedSidebars} from './types';
11
- import type {NormalizeSidebarsParams} from '../types';
12
- import {validateSidebars} from './validation';
10
+ import type {SidebarsConfig, Sidebars, SidebarProcessorParams} from './types';
11
+ import {validateSidebars, validateCategoryMetadataFile} from './validation';
13
12
  import {normalizeSidebars} from './normalization';
14
- import {processSidebars, type SidebarProcessorParams} from './processor';
13
+ import {processSidebars} from './processor';
14
+ import {postProcessSidebars} from './postProcessor';
15
15
  import path from 'path';
16
- import {createSlugger} from '@docusaurus/utils';
16
+ import {Globby} from '@docusaurus/utils';
17
+ import logger from '@docusaurus/logger';
17
18
  import type {PluginOptions} from '@docusaurus/plugin-content-docs';
19
+ import Yaml from 'js-yaml';
20
+ import _ from 'lodash';
21
+ import combinePromises from 'combine-promises';
18
22
 
19
23
  export const DefaultSidebars: SidebarsConfig = {
20
24
  defaultSidebar: [
@@ -38,9 +42,36 @@ export function resolveSidebarPathOption(
38
42
  : sidebarPathOption;
39
43
  }
40
44
 
41
- function loadSidebarsFileUnsafe(
45
+ async function readCategoriesMetadata(contentPath: string) {
46
+ const categoryFiles = await Globby('**/_category_.{json,yml,yaml}', {
47
+ cwd: contentPath,
48
+ });
49
+ const categoryToFile = _.groupBy(categoryFiles, path.dirname);
50
+ return combinePromises(
51
+ _.mapValues(categoryToFile, async (files, folder) => {
52
+ const [filePath] = files;
53
+ if (files.length > 1) {
54
+ logger.warn`There are more than one category metadata files for path=${folder}: ${files.join(
55
+ ', ',
56
+ )}. The behavior is undetermined.`;
57
+ }
58
+ const content = await fs.readFile(
59
+ path.join(contentPath, filePath),
60
+ 'utf-8',
61
+ );
62
+ try {
63
+ return validateCategoryMetadataFile(Yaml.load(content));
64
+ } catch (err) {
65
+ logger.error`The docs sidebar category metadata file path=${filePath} looks invalid!`;
66
+ throw err;
67
+ }
68
+ }),
69
+ );
70
+ }
71
+
72
+ export async function loadSidebarsFileUnsafe(
42
73
  sidebarFilePath: string | false | undefined,
43
- ): SidebarsConfig {
74
+ ): Promise<SidebarsConfig> {
44
75
  // false => no sidebars
45
76
  if (sidebarFilePath === false) {
46
77
  return DisabledSidebars;
@@ -54,7 +85,7 @@ function loadSidebarsFileUnsafe(
54
85
  // Non-existent sidebars file: no sidebars
55
86
  // Note: this edge case can happen on versioned docs, not current version
56
87
  // We avoid creating empty versioned sidebars file with the CLI
57
- if (!fs.existsSync(sidebarFilePath)) {
88
+ if (!(await fs.pathExists(sidebarFilePath))) {
58
89
  return DisabledSidebars;
59
90
  }
60
91
 
@@ -62,34 +93,21 @@ function loadSidebarsFileUnsafe(
62
93
  return importFresh(sidebarFilePath);
63
94
  }
64
95
 
65
- export function loadSidebarsFile(
66
- sidebarFilePath: string | false | undefined,
67
- ): SidebarsConfig {
68
- const sidebarsConfig = loadSidebarsFileUnsafe(sidebarFilePath);
69
- validateSidebars(sidebarsConfig);
70
- return sidebarsConfig;
71
- }
72
-
73
- export function loadNormalizedSidebars(
74
- sidebarFilePath: string | false | undefined,
75
- params: NormalizeSidebarsParams,
76
- ): NormalizedSidebars {
77
- return normalizeSidebars(loadSidebarsFile(sidebarFilePath), params);
78
- }
79
-
80
96
  // Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
81
97
  export async function loadSidebars(
82
98
  sidebarFilePath: string | false | undefined,
83
99
  options: SidebarProcessorParams,
84
100
  ): Promise<Sidebars> {
85
- const normalizeSidebarsParams: NormalizeSidebarsParams = {
86
- ...options.sidebarOptions,
87
- version: options.version,
88
- categoryLabelSlugger: createSlugger(),
89
- };
90
- const normalizedSidebars = loadNormalizedSidebars(
91
- sidebarFilePath,
92
- normalizeSidebarsParams,
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,
93
111
  );
94
- return processSidebars(normalizedSidebars, options);
112
+ return postProcessSidebars(processedSidebars, options);
95
113
  }
@@ -5,7 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {NormalizeSidebarsParams} from '../types';
9
8
  import type {
10
9
  NormalizedSidebarItem,
11
10
  NormalizedSidebar,
@@ -15,41 +14,17 @@ import type {
15
14
  SidebarItemConfig,
16
15
  SidebarConfig,
17
16
  SidebarsConfig,
18
- SidebarItemCategoryLink,
19
17
  NormalizedSidebarItemCategory,
20
18
  } from './types';
21
19
  import {isCategoriesShorthand} from './utils';
22
- import {mapValues} from 'lodash';
23
- import {normalizeUrl} from '@docusaurus/utils';
24
- import type {SidebarOptions} from '@docusaurus/plugin-content-docs';
25
-
26
- function normalizeCategoryLink(
27
- category: SidebarItemCategoryConfig,
28
- params: NormalizeSidebarsParams,
29
- ): SidebarItemCategoryLink | undefined {
30
- if (category.link?.type === 'generated-index') {
31
- // default slug logic can be improved
32
- const getDefaultSlug = () =>
33
- `/category/${params.categoryLabelSlugger.slug(category.label)}`;
34
- const slug = category.link.slug ?? getDefaultSlug();
35
- const permalink = normalizeUrl([params.version.versionPath, slug]);
36
- return {
37
- ...category.link,
38
- slug,
39
- permalink,
40
- };
41
- }
42
- return category.link;
43
- }
20
+ import _ from 'lodash';
21
+ import logger from '@docusaurus/logger';
44
22
 
45
23
  function normalizeCategoriesShorthand(
46
24
  sidebar: SidebarCategoriesShorthand,
47
- options: SidebarOptions,
48
25
  ): SidebarItemCategoryConfig[] {
49
26
  return Object.entries(sidebar).map(([label, items]) => ({
50
27
  type: 'category',
51
- collapsed: options.sidebarCollapsed,
52
- collapsible: options.sidebarCollapsible,
53
28
  label,
54
29
  items,
55
30
  }));
@@ -61,31 +36,21 @@ function normalizeCategoriesShorthand(
61
36
  */
62
37
  export function normalizeItem(
63
38
  item: SidebarItemConfig,
64
- options: NormalizeSidebarsParams,
65
39
  ): NormalizedSidebarItem[] {
66
40
  if (typeof item === 'string') {
67
- return [
68
- {
69
- type: 'doc',
70
- id: item,
71
- },
72
- ];
41
+ return [{type: 'doc', id: item}];
73
42
  }
74
43
  if (isCategoriesShorthand(item)) {
75
- return normalizeCategoriesShorthand(item, options).flatMap((subItem) =>
76
- normalizeItem(subItem, options),
77
- );
44
+ // This will never throw anyways
45
+ return normalizeSidebar(item, 'sidebar items slice');
78
46
  }
79
47
  if (item.type === 'category') {
80
- const link = normalizeCategoryLink(item, options);
81
48
  const normalizedCategory: NormalizedSidebarItemCategory = {
82
49
  ...item,
83
- link,
84
- items: (item.items ?? []).flatMap((subItem) =>
85
- normalizeItem(subItem, options),
50
+ items: normalizeSidebar(
51
+ item.items,
52
+ logger.interpolate`code=${'items'} of the category name=${item.label}`,
86
53
  ),
87
- collapsible: item.collapsible ?? options.sidebarCollapsible,
88
- collapsed: item.collapsed ?? options.sidebarCollapsed,
89
54
  };
90
55
  return [normalizedCategory];
91
56
  }
@@ -94,20 +59,27 @@ export function normalizeItem(
94
59
 
95
60
  function normalizeSidebar(
96
61
  sidebar: SidebarConfig,
97
- options: NormalizeSidebarsParams,
62
+ place: string,
98
63
  ): NormalizedSidebar {
64
+ if (!Array.isArray(sidebar) && !isCategoriesShorthand(sidebar)) {
65
+ throw new Error(
66
+ logger.interpolate`Invalid sidebar items collection code=${JSON.stringify(
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.`,
69
+ );
70
+ }
71
+
99
72
  const normalizedSidebar = Array.isArray(sidebar)
100
73
  ? sidebar
101
- : normalizeCategoriesShorthand(sidebar, options);
74
+ : normalizeCategoriesShorthand(sidebar);
102
75
 
103
- return normalizedSidebar.flatMap((subItem) =>
104
- normalizeItem(subItem, options),
105
- );
76
+ return normalizedSidebar.flatMap((subItem) => normalizeItem(subItem));
106
77
  }
107
78
 
108
79
  export function normalizeSidebars(
109
80
  sidebars: SidebarsConfig,
110
- params: NormalizeSidebarsParams,
111
81
  ): NormalizedSidebars {
112
- return mapValues(sidebars, (items) => normalizeSidebar(items, params));
82
+ return _.mapValues(sidebars, (sidebar, id) =>
83
+ normalizeSidebar(sidebar, logger.interpolate`sidebar name=${id}`),
84
+ );
113
85
  }
@@ -0,0 +1,94 @@
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 {normalizeUrl} from '@docusaurus/utils';
9
+ import type {
10
+ SidebarItem,
11
+ Sidebars,
12
+ SidebarProcessorParams,
13
+ ProcessedSidebarItemCategory,
14
+ ProcessedSidebarItem,
15
+ ProcessedSidebars,
16
+ SidebarItemCategoryLink,
17
+ } from './types';
18
+ import _ from 'lodash';
19
+
20
+ function normalizeCategoryLink(
21
+ category: ProcessedSidebarItemCategory,
22
+ params: SidebarProcessorParams,
23
+ ): SidebarItemCategoryLink | undefined {
24
+ if (category.link?.type === 'generated-index') {
25
+ // default slug logic can be improved
26
+ const getDefaultSlug = () =>
27
+ `/category/${params.categoryLabelSlugger.slug(category.label)}`;
28
+ const slug = category.link.slug ?? getDefaultSlug();
29
+ const permalink = normalizeUrl([params.version.versionPath, slug]);
30
+ return {
31
+ ...category.link,
32
+ slug,
33
+ permalink,
34
+ };
35
+ }
36
+ return category.link;
37
+ }
38
+
39
+ function postProcessSidebarItem(
40
+ item: ProcessedSidebarItem,
41
+ params: SidebarProcessorParams,
42
+ ): SidebarItem {
43
+ if (item.type === 'category') {
44
+ const category = {
45
+ ...item,
46
+ collapsed: item.collapsed ?? params.sidebarOptions.sidebarCollapsed,
47
+ collapsible: item.collapsible ?? params.sidebarOptions.sidebarCollapsible,
48
+ link: normalizeCategoryLink(item, params),
49
+ items: item.items.map((subItem) =>
50
+ postProcessSidebarItem(subItem, params),
51
+ ),
52
+ };
53
+ // If the current category doesn't have subitems, we render a normal link
54
+ // instead.
55
+ if (category.items.length === 0) {
56
+ if (!category.link) {
57
+ throw new Error(
58
+ `Sidebar category ${item.label} has neither any subitem nor a link. This makes this item not able to link to anything.`,
59
+ );
60
+ }
61
+ switch (category.link.type) {
62
+ case 'doc':
63
+ return {
64
+ type: 'doc',
65
+ label: category.label,
66
+ id: category.link.id,
67
+ };
68
+ case 'generated-index':
69
+ return {
70
+ type: 'link',
71
+ label: category.label,
72
+ href: category.link.permalink,
73
+ };
74
+ default:
75
+ throw new Error('Unexpected sidebar category link type');
76
+ }
77
+ }
78
+ // A non-collapsible category can't be collapsed!
79
+ if (category.collapsible === false) {
80
+ category.collapsed = false;
81
+ }
82
+ return category;
83
+ }
84
+ return item;
85
+ }
86
+
87
+ export function postProcessSidebars(
88
+ sidebars: ProcessedSidebars,
89
+ params: SidebarProcessorParams,
90
+ ): Sidebars {
91
+ return _.mapValues(sidebars, (sidebar) =>
92
+ sidebar.map((item) => postProcessSidebarItem(item, params)),
93
+ );
94
+ }
@@ -7,44 +7,28 @@
7
7
 
8
8
  import type {DocMetadataBase, VersionMetadata} from '../types';
9
9
  import type {
10
- Sidebars,
11
- Sidebar,
12
- SidebarItem,
13
10
  NormalizedSidebarItem,
14
11
  NormalizedSidebar,
15
12
  NormalizedSidebars,
16
- SidebarItemsGeneratorOption,
17
13
  SidebarItemsGeneratorDoc,
18
14
  SidebarItemsGeneratorVersion,
19
- NormalizedSidebarItemCategory,
20
- SidebarItemCategory,
21
15
  SidebarItemAutogenerated,
16
+ ProcessedSidebarItem,
17
+ ProcessedSidebar,
18
+ ProcessedSidebars,
19
+ SidebarProcessorParams,
20
+ CategoryMetadataFile,
22
21
  } from './types';
23
- import {transformSidebarItems} from './utils';
24
22
  import {DefaultSidebarItemsGenerator} from './generator';
25
- import {mapValues, memoize, pick} from 'lodash';
23
+ import {validateSidebars} from './validation';
24
+ import _ from 'lodash';
26
25
  import combinePromises from 'combine-promises';
27
- import {normalizeItem} from './normalization';
28
26
  import {isCategoryIndex} from '../docs';
29
- import type {Slugger} from '@docusaurus/utils';
30
- import type {
31
- NumberPrefixParser,
32
- SidebarOptions,
33
- } from '@docusaurus/plugin-content-docs';
34
-
35
- export type SidebarProcessorParams = {
36
- sidebarItemsGenerator: SidebarItemsGeneratorOption;
37
- numberPrefixParser: NumberPrefixParser;
38
- docs: DocMetadataBase[];
39
- version: VersionMetadata;
40
- categoryLabelSlugger: Slugger;
41
- sidebarOptions: SidebarOptions;
42
- };
43
27
 
44
28
  function toSidebarItemsGeneratorDoc(
45
29
  doc: DocMetadataBase,
46
30
  ): SidebarItemsGeneratorDoc {
47
- return pick(doc, [
31
+ return _.pick(doc, [
48
32
  'id',
49
33
  'unversionedId',
50
34
  'frontMatter',
@@ -57,14 +41,16 @@ function toSidebarItemsGeneratorDoc(
57
41
  function toSidebarItemsGeneratorVersion(
58
42
  version: VersionMetadata,
59
43
  ): SidebarItemsGeneratorVersion {
60
- return pick(version, ['versionName', 'contentPath']);
44
+ return _.pick(version, ['versionName', 'contentPath']);
61
45
  }
62
46
 
63
- // Handle the generation of autogenerated sidebar items and other post-processing checks
47
+ // Handle the generation of autogenerated sidebar items and other
48
+ // post-processing checks
64
49
  async function processSidebar(
65
50
  unprocessedSidebar: NormalizedSidebar,
51
+ categoriesMetadata: Record<string, CategoryMetadataFile>,
66
52
  params: SidebarProcessorParams,
67
- ): Promise<Sidebar> {
53
+ ): Promise<ProcessedSidebar> {
68
54
  const {
69
55
  sidebarItemsGenerator,
70
56
  numberPrefixParser,
@@ -74,24 +60,14 @@ async function processSidebar(
74
60
  } = params;
75
61
 
76
62
  // Just a minor lazy transformation optimization
77
- const getSidebarItemsGeneratorDocsAndVersion = memoize(() => ({
63
+ const getSidebarItemsGeneratorDocsAndVersion = _.memoize(() => ({
78
64
  docs: docs.map(toSidebarItemsGeneratorDoc),
79
65
  version: toSidebarItemsGeneratorVersion(version),
80
66
  }));
81
67
 
82
- async function processCategoryItem(
83
- item: NormalizedSidebarItemCategory,
84
- ): Promise<SidebarItemCategory> {
85
- return {
86
- ...item,
87
- items: (await Promise.all(item.items.map(processItem))).flat(),
88
- };
89
- }
90
-
91
68
  async function processAutoGeneratedItem(
92
69
  item: SidebarItemAutogenerated,
93
- ): Promise<SidebarItem[]> {
94
- // TODO the returned type can't be trusted in practice (generator can be user-provided)
70
+ ): Promise<ProcessedSidebarItem[]> {
95
71
  const generatedItems = await sidebarItemsGenerator({
96
72
  item,
97
73
  numberPrefixParser,
@@ -99,22 +75,25 @@ async function processSidebar(
99
75
  isCategoryIndex,
100
76
  ...getSidebarItemsGeneratorDocsAndVersion(),
101
77
  options: sidebarOptions,
78
+ categoriesMetadata,
102
79
  });
103
- // TODO validate generated items: user can generate bad items
104
-
105
- const generatedItemsNormalized = generatedItems.flatMap((generatedItem) =>
106
- normalizeItem(generatedItem, {...params, ...sidebarOptions}),
107
- );
108
-
109
- // Process again... weird but sidebar item generated might generate some auto-generated items?
110
- return processItems(generatedItemsNormalized);
80
+ // Process again... weird but sidebar item generated might generate some
81
+ // auto-generated items?
82
+ // TODO repeatedly process & unwrap autogenerated items until there are no
83
+ // more autogenerated items, or when loop count (e.g. 10) is reached
84
+ return processItems(generatedItems);
111
85
  }
112
86
 
113
87
  async function processItem(
114
88
  item: NormalizedSidebarItem,
115
- ): Promise<SidebarItem[]> {
89
+ ): Promise<ProcessedSidebarItem[]> {
116
90
  if (item.type === 'category') {
117
- return [await processCategoryItem(item)];
91
+ return [
92
+ {
93
+ ...item,
94
+ items: (await Promise.all(item.items.map(processItem))).flat(),
95
+ },
96
+ ];
118
97
  }
119
98
  if (item.type === 'autogenerated') {
120
99
  return processAutoGeneratedItem(item);
@@ -124,32 +103,24 @@ async function processSidebar(
124
103
 
125
104
  async function processItems(
126
105
  items: NormalizedSidebarItem[],
127
- ): Promise<SidebarItem[]> {
106
+ ): Promise<ProcessedSidebarItem[]> {
128
107
  return (await Promise.all(items.map(processItem))).flat();
129
108
  }
130
109
 
131
110
  const processedSidebar = await processItems(unprocessedSidebar);
132
-
133
- const fixSidebarItemInconsistencies = (item: SidebarItem): SidebarItem => {
134
- // A non-collapsible category can't be collapsed!
135
- if (item.type === 'category' && !item.collapsible && item.collapsed) {
136
- return {
137
- ...item,
138
- collapsed: false,
139
- };
140
- }
141
- return item;
142
- };
143
- return transformSidebarItems(processedSidebar, fixSidebarItemInconsistencies);
111
+ return processedSidebar;
144
112
  }
145
113
 
146
114
  export async function processSidebars(
147
115
  unprocessedSidebars: NormalizedSidebars,
116
+ categoriesMetadata: Record<string, CategoryMetadataFile>,
148
117
  params: SidebarProcessorParams,
149
- ): Promise<Sidebars> {
150
- return combinePromises(
151
- mapValues(unprocessedSidebars, (unprocessedSidebar) =>
152
- processSidebar(unprocessedSidebar, params),
118
+ ): Promise<ProcessedSidebars> {
119
+ const processedSidebars = await combinePromises(
120
+ _.mapValues(unprocessedSidebars, (unprocessedSidebar) =>
121
+ processSidebar(unprocessedSidebar, categoriesMetadata, params),
153
122
  ),
154
123
  );
124
+ validateSidebars(processedSidebars);
125
+ return processedSidebars;
155
126
  }
@@ -12,6 +12,7 @@ import type {
12
12
  SidebarOptions,
13
13
  CategoryIndexMatcher,
14
14
  } from '@docusaurus/plugin-content-docs';
15
+ import type {Slugger} from '@docusaurus/utils';
15
16
 
16
17
  // Makes all properties visible when hovering over the type
17
18
  type Expand<T extends Record<string, unknown>> = {[P in keyof T]: T[P]};
@@ -27,6 +28,12 @@ export type SidebarItemDoc = SidebarItemBase & {
27
28
  id: string;
28
29
  };
29
30
 
31
+ export type SidebarItemHtml = SidebarItemBase & {
32
+ type: 'html';
33
+ value: string;
34
+ defaultStyle?: boolean;
35
+ };
36
+
30
37
  export type SidebarItemLink = SidebarItemBase & {
31
38
  type: 'link';
32
39
  href: string;
@@ -76,17 +83,18 @@ export type SidebarItemCategoryLink =
76
83
  // The user-given configuration in sidebars.js, before normalization
77
84
  export type SidebarItemCategoryConfig = Expand<
78
85
  Optional<SidebarItemCategoryBase, 'collapsed' | 'collapsible'> & {
79
- items: SidebarItemConfig[];
86
+ items: SidebarCategoriesShorthand | SidebarItemConfig[];
80
87
  link?: SidebarItemCategoryLinkConfig;
81
88
  }
82
89
  >;
83
90
 
84
91
  export type SidebarCategoriesShorthand = {
85
- [sidebarCategory: string]: SidebarItemConfig[];
92
+ [sidebarCategory: string]: SidebarCategoriesShorthand | SidebarItemConfig[];
86
93
  };
87
94
 
88
95
  export type SidebarItemConfig =
89
96
  | SidebarItemDoc
97
+ | SidebarItemHtml
90
98
  | SidebarItemLink
91
99
  | SidebarItemAutogenerated
92
100
  | SidebarItemCategoryConfig
@@ -100,14 +108,15 @@ export type SidebarsConfig = {
100
108
 
101
109
  // Normalized but still has 'autogenerated', which will be handled in processing
102
110
  export type NormalizedSidebarItemCategory = Expand<
103
- SidebarItemCategoryBase & {
111
+ Optional<SidebarItemCategoryBase, 'collapsed' | 'collapsible'> & {
104
112
  items: NormalizedSidebarItem[];
105
- link?: SidebarItemCategoryLink;
113
+ link?: SidebarItemCategoryLinkConfig;
106
114
  }
107
115
  >;
108
116
 
109
117
  export type NormalizedSidebarItem =
110
118
  | SidebarItemDoc
119
+ | SidebarItemHtml
111
120
  | SidebarItemLink
112
121
  | NormalizedSidebarItemCategory
113
122
  | SidebarItemAutogenerated;
@@ -117,6 +126,22 @@ export type NormalizedSidebars = {
117
126
  [sidebarId: string]: NormalizedSidebar;
118
127
  };
119
128
 
129
+ export type ProcessedSidebarItemCategory = Expand<
130
+ Optional<SidebarItemCategoryBase, 'collapsed' | 'collapsible'> & {
131
+ items: ProcessedSidebarItem[];
132
+ link?: SidebarItemCategoryLinkConfig;
133
+ }
134
+ >;
135
+ export type ProcessedSidebarItem =
136
+ | SidebarItemDoc
137
+ | SidebarItemHtml
138
+ | SidebarItemLink
139
+ | ProcessedSidebarItemCategory;
140
+ export type ProcessedSidebar = ProcessedSidebarItem[];
141
+ export type ProcessedSidebars = {
142
+ [sidebarId: string]: ProcessedSidebar;
143
+ };
144
+
120
145
  export type SidebarItemCategory = Expand<
121
146
  SidebarItemCategoryBase & {
122
147
  items: SidebarItem[];
@@ -131,6 +156,7 @@ export type SidebarItemCategoryWithGeneratedIndex =
131
156
 
132
157
  export type SidebarItem =
133
158
  | SidebarItemDoc
159
+ | SidebarItemHtml
134
160
  | SidebarItemLink
135
161
  | SidebarItemCategory;
136
162
 
@@ -158,12 +184,21 @@ export type PropSidebarItemLink = SidebarItemLink & {
158
184
  docId?: string;
159
185
  };
160
186
 
161
- export type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory;
187
+ export type PropSidebarItemHtml = SidebarItemHtml;
188
+
189
+ export type PropSidebarItem =
190
+ | PropSidebarItemLink
191
+ | PropSidebarItemCategory
192
+ | PropSidebarItemHtml;
162
193
  export type PropSidebar = PropSidebarItem[];
163
194
  export type PropSidebars = {
164
195
  [sidebarId: string]: PropSidebar;
165
196
  };
166
197
 
198
+ export type PropSidebarBreadcrumbsItem =
199
+ | PropSidebarItemLink
200
+ | PropSidebarItemCategory;
201
+
167
202
  export type PropVersionDoc = {
168
203
  id: string;
169
204
  title: string;
@@ -174,6 +209,20 @@ export type PropVersionDocs = {
174
209
  [docId: string]: PropVersionDoc;
175
210
  };
176
211
 
212
+ export type CategoryMetadataFile = {
213
+ label?: string;
214
+ position?: number;
215
+ collapsed?: boolean;
216
+ collapsible?: boolean;
217
+ className?: string;
218
+ link?: SidebarItemCategoryLinkConfig | null;
219
+
220
+ // TODO should we allow "items" here? how would this work? would an
221
+ // "autogenerated" type be allowed?
222
+ // This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
223
+ // cf comment: https://github.com/facebook/docusaurus/issues/3464#issuecomment-784765199
224
+ };
225
+
177
226
  // Reduce API surface for options.sidebarItemsGenerator
178
227
  // The user-provided generator fn should receive only a subset of metadata
179
228
  // A change to any of these metadata can be considered as a breaking change
@@ -197,19 +246,28 @@ export type SidebarItemsGeneratorArgs = {
197
246
  docs: SidebarItemsGeneratorDoc[];
198
247
  numberPrefixParser: NumberPrefixParser;
199
248
  isCategoryIndex: CategoryIndexMatcher;
249
+ categoriesMetadata: Record<string, CategoryMetadataFile>;
200
250
  options: SidebarOptions;
201
251
  };
202
252
  export type SidebarItemsGenerator = (
203
253
  generatorArgs: SidebarItemsGeneratorArgs,
204
- ) => // TODO TS issue: the generator can generate un-normalized items!
205
- Promise<SidebarItem[]>;
206
- // Promise<SidebarItemConfig[]>;
254
+ ) => Promise<NormalizedSidebar>;
207
255
 
208
- // Also inject the default generator to conveniently wrap/enhance/sort the default sidebar gen logic
256
+ // Also inject the default generator to conveniently wrap/enhance/sort the
257
+ // default sidebar gen logic
209
258
  // see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
210
259
  export type SidebarItemsGeneratorOptionArgs = {
211
260
  defaultSidebarItemsGenerator: SidebarItemsGenerator;
212
261
  } & SidebarItemsGeneratorArgs;
213
262
  export type SidebarItemsGeneratorOption = (
214
263
  generatorArgs: SidebarItemsGeneratorOptionArgs,
215
- ) => Promise<SidebarItem[]>;
264
+ ) => Promise<NormalizedSidebarItem[]>;
265
+
266
+ export type SidebarProcessorParams = {
267
+ sidebarItemsGenerator: SidebarItemsGeneratorOption;
268
+ numberPrefixParser: NumberPrefixParser;
269
+ docs: DocMetadataBase[];
270
+ version: VersionMetadata;
271
+ categoryLabelSlugger: Slugger;
272
+ sidebarOptions: SidebarOptions;
273
+ };