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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/cli.d.ts +1 -1
  3. package/lib/cli.js +18 -23
  4. package/lib/client/docsClientUtils.d.ts +0 -3
  5. package/lib/client/docsClientUtils.js +10 -7
  6. package/lib/docFrontMatter.js +6 -2
  7. package/lib/docs.d.ts +3 -1
  8. package/lib/docs.js +76 -25
  9. package/lib/index.js +70 -77
  10. package/lib/lastUpdate.js +4 -4
  11. package/lib/markdown/index.d.ts +3 -6
  12. package/lib/markdown/index.js +3 -3
  13. package/lib/markdown/linkify.js +2 -2
  14. package/lib/options.js +12 -4
  15. package/lib/props.d.ts +7 -2
  16. package/lib/props.js +26 -3
  17. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +2 -1
  18. package/lib/sidebars/generator.js +174 -0
  19. package/lib/sidebars/index.d.ts +14 -0
  20. package/lib/sidebars/index.js +64 -0
  21. package/lib/sidebars/normalization.d.ts +9 -0
  22. package/lib/sidebars/normalization.js +58 -0
  23. package/lib/sidebars/processor.d.ts +16 -0
  24. package/lib/sidebars/processor.js +70 -0
  25. package/lib/sidebars/types.d.ts +87 -0
  26. package/lib/sidebars/types.js +13 -0
  27. package/lib/sidebars/utils.d.ts +22 -0
  28. package/lib/sidebars/utils.js +101 -0
  29. package/lib/sidebars/validation.d.ts +8 -0
  30. package/lib/sidebars/validation.js +102 -0
  31. package/lib/slug.js +4 -4
  32. package/lib/tags.d.ts +8 -0
  33. package/lib/tags.js +22 -0
  34. package/lib/theme/hooks/useDocs.js +21 -21
  35. package/lib/translations.d.ts +1 -1
  36. package/lib/translations.js +13 -13
  37. package/lib/types.d.ts +29 -61
  38. package/lib/versions.d.ts +1 -1
  39. package/lib/versions.js +40 -20
  40. package/package.json +15 -14
  41. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +5 -0
  42. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +1 -0
  43. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +2 -0
  44. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +2 -0
  45. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +2 -0
  46. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +2 -0
  47. package/src/__tests__/__fixtures__/simple-site/sidebars.json +15 -1
  48. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +6 -0
  49. package/src/__tests__/__snapshots__/cli.test.ts.snap +28 -0
  50. package/src/__tests__/__snapshots__/docs.test.ts.snap +140 -0
  51. package/src/__tests__/__snapshots__/index.test.ts.snap +426 -25
  52. package/src/__tests__/docFrontMatter.test.ts +160 -45
  53. package/src/__tests__/docs.test.ts +167 -21
  54. package/src/__tests__/index.test.ts +53 -27
  55. package/src/__tests__/options.test.ts +4 -1
  56. package/src/__tests__/props.test.ts +62 -0
  57. package/src/__tests__/versions.test.ts +68 -63
  58. package/src/cli.ts +23 -30
  59. package/src/client/docsClientUtils.ts +1 -12
  60. package/src/docFrontMatter.ts +7 -2
  61. package/src/docs.ts +88 -9
  62. package/src/index.ts +77 -91
  63. package/src/markdown/index.ts +8 -12
  64. package/src/numberPrefix.ts +4 -2
  65. package/src/options.ts +13 -1
  66. package/src/plugin-content-docs.d.ts +107 -32
  67. package/src/props.ts +41 -5
  68. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -0
  69. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -0
  70. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -0
  71. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category.js +0 -0
  72. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -0
  73. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed.json +0 -0
  74. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -0
  75. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-first-level-not-category.js +0 -0
  76. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -0
  77. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -0
  78. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link.json +0 -0
  79. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-unknown-type.json +0 -0
  80. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-wrong-field.json +0 -0
  81. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars.json +0 -0
  82. package/src/{__tests__/__snapshots__/sidebars.test.ts.snap → sidebars/__tests__/__snapshots__/index.test.ts.snap} +6 -6
  83. package/src/{__tests__/sidebarItemsGenerator.test.ts → sidebars/__tests__/generator.test.ts} +2 -2
  84. package/src/sidebars/__tests__/index.test.ts +202 -0
  85. package/src/sidebars/__tests__/processor.test.ts +148 -0
  86. package/src/sidebars/__tests__/utils.test.ts +395 -0
  87. package/src/sidebars/generator.ts +253 -0
  88. package/src/sidebars/index.ts +84 -0
  89. package/src/sidebars/normalization.ts +88 -0
  90. package/src/sidebars/processor.ts +124 -0
  91. package/src/sidebars/types.ts +156 -0
  92. package/src/sidebars/utils.ts +146 -0
  93. package/src/sidebars/validation.ts +124 -0
  94. package/src/tags.ts +21 -0
  95. package/src/translations.ts +26 -36
  96. package/src/types.ts +35 -101
  97. package/src/versions.ts +51 -21
  98. package/lib/sidebarItemsGenerator.js +0 -215
  99. package/lib/sidebars.d.ts +0 -45
  100. package/lib/sidebars.js +0 -354
  101. package/src/__tests__/sidebars.test.ts +0 -746
  102. package/src/sidebarItemsGenerator.ts +0 -315
  103. package/src/sidebars.ts +0 -589
@@ -0,0 +1,84 @@
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 fs from 'fs-extra';
9
+ import importFresh from 'import-fresh';
10
+ import type {SidebarsConfig, Sidebars, NormalizedSidebars} from './types';
11
+ import type {PluginOptions} from '../types';
12
+ import {validateSidebars} from './validation';
13
+ import {normalizeSidebars} from './normalization';
14
+ import {processSidebars, SidebarProcessorProps} from './processor';
15
+ import path from 'path';
16
+
17
+ export const DefaultSidebars: SidebarsConfig = {
18
+ defaultSidebar: [
19
+ {
20
+ type: 'autogenerated',
21
+ dirName: '.',
22
+ },
23
+ ],
24
+ };
25
+
26
+ export const DisabledSidebars: SidebarsConfig = {};
27
+
28
+ // If a path is provided, make it absolute
29
+ // use this before loadSidebars()
30
+ export function resolveSidebarPathOption(
31
+ siteDir: string,
32
+ sidebarPathOption: PluginOptions['sidebarPath'],
33
+ ): PluginOptions['sidebarPath'] {
34
+ return sidebarPathOption
35
+ ? path.resolve(siteDir, sidebarPathOption)
36
+ : sidebarPathOption;
37
+ }
38
+
39
+ function loadSidebarFile(
40
+ sidebarFilePath: string | false | undefined,
41
+ ): SidebarsConfig {
42
+ // false => no sidebars
43
+ if (sidebarFilePath === false) {
44
+ return DisabledSidebars;
45
+ }
46
+
47
+ // undefined => defaults to autogenerated sidebars
48
+ if (typeof sidebarFilePath === 'undefined') {
49
+ return DefaultSidebars;
50
+ }
51
+
52
+ // Non-existent sidebars file: no sidebars
53
+ // Note: this edge case can happen on versioned docs, not current version
54
+ // We avoid creating empty versioned sidebars file with the CLI
55
+ if (!fs.existsSync(sidebarFilePath)) {
56
+ return DisabledSidebars;
57
+ }
58
+
59
+ // We don't want sidebars to be cached because of hot reloading.
60
+ return importFresh(sidebarFilePath);
61
+ }
62
+
63
+ export function loadUnprocessedSidebars(
64
+ sidebarFilePath: string | false | undefined,
65
+ options: SidebarProcessorProps['options'],
66
+ ): NormalizedSidebars {
67
+ const sidebarsConfig = loadSidebarFile(sidebarFilePath);
68
+ validateSidebars(sidebarsConfig);
69
+
70
+ const normalizedSidebars = normalizeSidebars(sidebarsConfig, options);
71
+ return normalizedSidebars;
72
+ }
73
+
74
+ // Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
75
+ export async function loadSidebars(
76
+ sidebarFilePath: string | false | undefined,
77
+ options: SidebarProcessorProps,
78
+ ): Promise<Sidebars> {
79
+ const unprocessedSidebars = loadUnprocessedSidebars(
80
+ sidebarFilePath,
81
+ options.options,
82
+ );
83
+ return processSidebars(unprocessedSidebars, options);
84
+ }
@@ -0,0 +1,88 @@
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 type {SidebarOptions} from '../types';
9
+ import {
10
+ NormalizedSidebarItem,
11
+ NormalizedSidebar,
12
+ NormalizedSidebars,
13
+ SidebarCategoriesShorthand,
14
+ SidebarItemCategoryConfig,
15
+ SidebarItemConfig,
16
+ SidebarConfig,
17
+ SidebarsConfig,
18
+ isCategoriesShorthand,
19
+ } from './types';
20
+ import {mapValues} from 'lodash';
21
+
22
+ function normalizeCategoriesShorthand(
23
+ sidebar: SidebarCategoriesShorthand,
24
+ options: SidebarOptions,
25
+ ): SidebarItemCategoryConfig[] {
26
+ return Object.entries(sidebar).map(([label, items]) => ({
27
+ type: 'category',
28
+ collapsed: options.sidebarCollapsed,
29
+ collapsible: options.sidebarCollapsible,
30
+ label,
31
+ items,
32
+ }));
33
+ }
34
+
35
+ /**
36
+ * Normalizes recursively item and all its children. Ensures that at the end
37
+ * each item will be an object with the corresponding type.
38
+ */
39
+ function normalizeItem(
40
+ item: SidebarItemConfig,
41
+ options: SidebarOptions,
42
+ ): NormalizedSidebarItem[] {
43
+ if (typeof item === 'string') {
44
+ return [
45
+ {
46
+ type: 'doc',
47
+ id: item,
48
+ },
49
+ ];
50
+ }
51
+ if (isCategoriesShorthand(item)) {
52
+ return normalizeCategoriesShorthand(item, options).flatMap((subitem) =>
53
+ normalizeItem(subitem, options),
54
+ );
55
+ }
56
+ return item.type === 'category'
57
+ ? [
58
+ {
59
+ ...item,
60
+ items: item.items.flatMap((subItem) =>
61
+ normalizeItem(subItem, options),
62
+ ),
63
+ collapsible: item.collapsible ?? options.sidebarCollapsible,
64
+ collapsed: item.collapsed ?? options.sidebarCollapsed,
65
+ },
66
+ ]
67
+ : [item];
68
+ }
69
+
70
+ function normalizeSidebar(
71
+ sidebar: SidebarConfig,
72
+ options: SidebarOptions,
73
+ ): NormalizedSidebar {
74
+ const normalizedSidebar = Array.isArray(sidebar)
75
+ ? sidebar
76
+ : normalizeCategoriesShorthand(sidebar, options);
77
+
78
+ return normalizedSidebar.flatMap((subitem) =>
79
+ normalizeItem(subitem, options),
80
+ );
81
+ }
82
+
83
+ export function normalizeSidebars(
84
+ sidebars: SidebarsConfig,
85
+ options: SidebarOptions,
86
+ ): NormalizedSidebars {
87
+ return mapValues(sidebars, (subitem) => normalizeSidebar(subitem, options));
88
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import type {
9
+ NumberPrefixParser,
10
+ DocMetadataBase,
11
+ VersionMetadata,
12
+ SidebarOptions,
13
+ } from '../types';
14
+ import type {
15
+ Sidebars,
16
+ Sidebar,
17
+ SidebarItem,
18
+ NormalizedSidebarItem,
19
+ NormalizedSidebar,
20
+ NormalizedSidebars,
21
+ SidebarItemsGeneratorOption,
22
+ SidebarItemsGeneratorDoc,
23
+ SidebarItemsGeneratorVersion,
24
+ } from './types';
25
+ import {transformSidebarItems} from './utils';
26
+ import {DefaultSidebarItemsGenerator} from './generator';
27
+ import {mapValues, memoize, pick} from 'lodash';
28
+ import combinePromises from 'combine-promises';
29
+
30
+ export type SidebarProcessorProps = {
31
+ sidebarItemsGenerator: SidebarItemsGeneratorOption;
32
+ numberPrefixParser: NumberPrefixParser;
33
+ docs: DocMetadataBase[];
34
+ version: VersionMetadata;
35
+ options: SidebarOptions;
36
+ };
37
+
38
+ function toSidebarItemsGeneratorDoc(
39
+ doc: DocMetadataBase,
40
+ ): SidebarItemsGeneratorDoc {
41
+ return pick(doc, [
42
+ 'id',
43
+ 'frontMatter',
44
+ 'source',
45
+ 'sourceDirName',
46
+ 'sidebarPosition',
47
+ ]);
48
+ }
49
+
50
+ function toSidebarItemsGeneratorVersion(
51
+ version: VersionMetadata,
52
+ ): SidebarItemsGeneratorVersion {
53
+ return pick(version, ['versionName', 'contentPath']);
54
+ }
55
+
56
+ // Handle the generation of autogenerated sidebar items and other post-processing checks
57
+ async function processSidebar(
58
+ unprocessedSidebar: NormalizedSidebar,
59
+ {
60
+ sidebarItemsGenerator,
61
+ numberPrefixParser,
62
+ docs,
63
+ version,
64
+ options,
65
+ }: SidebarProcessorProps,
66
+ ): Promise<Sidebar> {
67
+ // Just a minor lazy transformation optimization
68
+ const getSidebarItemsGeneratorDocsAndVersion = memoize(() => ({
69
+ docs: docs.map(toSidebarItemsGeneratorDoc),
70
+ version: toSidebarItemsGeneratorVersion(version),
71
+ }));
72
+
73
+ async function handleAutoGeneratedItems(
74
+ item: NormalizedSidebarItem,
75
+ ): Promise<SidebarItem[]> {
76
+ if (item.type === 'category') {
77
+ return [
78
+ {
79
+ ...item,
80
+ items: (
81
+ await Promise.all(item.items.map(handleAutoGeneratedItems))
82
+ ).flat(),
83
+ },
84
+ ];
85
+ }
86
+ if (item.type === 'autogenerated') {
87
+ return sidebarItemsGenerator({
88
+ item,
89
+ numberPrefixParser,
90
+ defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
91
+ ...getSidebarItemsGeneratorDocsAndVersion(),
92
+ options,
93
+ });
94
+ }
95
+ return [item];
96
+ }
97
+
98
+ const processedSidebar = (
99
+ await Promise.all(unprocessedSidebar.map(handleAutoGeneratedItems))
100
+ ).flat();
101
+
102
+ const fixSidebarItemInconsistencies = (item: SidebarItem): SidebarItem => {
103
+ // A non-collapsible category can't be collapsed!
104
+ if (item.type === 'category' && !item.collapsible && item.collapsed) {
105
+ return {
106
+ ...item,
107
+ collapsed: false,
108
+ };
109
+ }
110
+ return item;
111
+ };
112
+ return transformSidebarItems(processedSidebar, fixSidebarItemInconsistencies);
113
+ }
114
+
115
+ export async function processSidebars(
116
+ unprocessedSidebars: NormalizedSidebars,
117
+ props: SidebarProcessorProps,
118
+ ): Promise<Sidebars> {
119
+ return combinePromises(
120
+ mapValues(unprocessedSidebars, (unprocessedSidebar) =>
121
+ processSidebar(unprocessedSidebar, props),
122
+ ),
123
+ );
124
+ }
@@ -0,0 +1,156 @@
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 {Optional} from 'utility-types';
9
+ import type {
10
+ DocMetadataBase,
11
+ VersionMetadata,
12
+ NumberPrefixParser,
13
+ SidebarOptions,
14
+ } from '../types';
15
+
16
+ // Makes all properties visible when hovering over the type
17
+ type Expand<T extends Record<string, unknown>> = {[P in keyof T]: T[P]};
18
+
19
+ export type SidebarItemBase = {
20
+ className?: string;
21
+ customProps?: Record<string, unknown>;
22
+ };
23
+
24
+ export type SidebarItemDoc = SidebarItemBase & {
25
+ type: 'doc' | 'ref';
26
+ label?: string;
27
+ id: string;
28
+ };
29
+
30
+ export type SidebarItemLink = SidebarItemBase & {
31
+ type: 'link';
32
+ href: string;
33
+ label: string;
34
+ };
35
+
36
+ export type SidebarItemAutogenerated = SidebarItemBase & {
37
+ type: 'autogenerated';
38
+ dirName: string;
39
+ };
40
+
41
+ type SidebarItemCategoryBase = SidebarItemBase & {
42
+ type: 'category';
43
+ label: string;
44
+ collapsed: boolean;
45
+ collapsible: boolean;
46
+ };
47
+
48
+ // The user-given configuration in sidebars.js, before normalization
49
+ export type SidebarItemCategoryConfig = Expand<
50
+ Optional<SidebarItemCategoryBase, 'collapsed' | 'collapsible'> & {
51
+ items: SidebarItemConfig[];
52
+ }
53
+ >;
54
+
55
+ export type SidebarCategoriesShorthand = {
56
+ [sidebarCategory: string]: SidebarItemConfig[];
57
+ };
58
+
59
+ export function isCategoriesShorthand(
60
+ item: SidebarItemConfig,
61
+ ): item is SidebarCategoriesShorthand {
62
+ return typeof item !== 'string' && !item.type;
63
+ }
64
+
65
+ export type SidebarItemConfig =
66
+ | SidebarItemDoc
67
+ | SidebarItemLink
68
+ | SidebarItemAutogenerated
69
+ | SidebarItemCategoryConfig
70
+ | string
71
+ | SidebarCategoriesShorthand;
72
+
73
+ export type SidebarConfig = SidebarCategoriesShorthand | SidebarItemConfig[];
74
+ export type SidebarsConfig = {
75
+ [sidebarId: string]: SidebarConfig;
76
+ };
77
+
78
+ // Normalized but still has 'autogenerated', which will be handled in processing
79
+ export type NormalizedSidebarItemCategory = Expand<
80
+ SidebarItemCategoryBase & {
81
+ items: NormalizedSidebarItem[];
82
+ }
83
+ >;
84
+
85
+ export type NormalizedSidebarItem =
86
+ | SidebarItemDoc
87
+ | SidebarItemLink
88
+ | NormalizedSidebarItemCategory
89
+ | SidebarItemAutogenerated;
90
+
91
+ export type NormalizedSidebar = NormalizedSidebarItem[];
92
+ export type NormalizedSidebars = {
93
+ [sidebarId: string]: NormalizedSidebar;
94
+ };
95
+
96
+ export type SidebarItemCategory = Expand<
97
+ SidebarItemCategoryBase & {
98
+ items: SidebarItem[];
99
+ }
100
+ >;
101
+
102
+ export type SidebarItem =
103
+ | SidebarItemDoc
104
+ | SidebarItemLink
105
+ | SidebarItemCategory;
106
+
107
+ export type Sidebar = SidebarItem[];
108
+ export type SidebarItemType = SidebarItem['type'];
109
+ export type Sidebars = {
110
+ [sidebarId: string]: Sidebar;
111
+ };
112
+
113
+ // Doc links have been resolved to URLs, ready to be passed to the theme
114
+ export type PropSidebarItemCategory = Expand<
115
+ SidebarItemCategoryBase & {
116
+ items: PropSidebarItem[];
117
+ }
118
+ >;
119
+
120
+ export type PropSidebarItem = SidebarItemLink | PropSidebarItemCategory;
121
+ export type PropSidebar = PropSidebarItem[];
122
+ export type PropSidebars = {
123
+ [sidebarId: string]: PropSidebar;
124
+ };
125
+
126
+ // Reduce API surface for options.sidebarItemsGenerator
127
+ // The user-provided generator fn should receive only a subset of metadatas
128
+ // A change to any of these metadatas can be considered as a breaking change
129
+ export type SidebarItemsGeneratorDoc = Pick<
130
+ DocMetadataBase,
131
+ 'id' | 'frontMatter' | 'source' | 'sourceDirName' | 'sidebarPosition'
132
+ >;
133
+ export type SidebarItemsGeneratorVersion = Pick<
134
+ VersionMetadata,
135
+ 'versionName' | 'contentPath'
136
+ >;
137
+
138
+ export type SidebarItemsGeneratorArgs = {
139
+ item: SidebarItemAutogenerated;
140
+ version: SidebarItemsGeneratorVersion;
141
+ docs: SidebarItemsGeneratorDoc[];
142
+ numberPrefixParser: NumberPrefixParser;
143
+ options: SidebarOptions;
144
+ };
145
+ export type SidebarItemsGenerator = (
146
+ generatorArgs: SidebarItemsGeneratorArgs,
147
+ ) => Promise<SidebarItem[]>;
148
+
149
+ // Also inject the default generator to conveniently wrap/enhance/sort the default sidebar gen logic
150
+ // see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
151
+ export type SidebarItemsGeneratorOptionArgs = {
152
+ defaultSidebarItemsGenerator: SidebarItemsGenerator;
153
+ } & SidebarItemsGeneratorArgs;
154
+ export type SidebarItemsGeneratorOption = (
155
+ generatorArgs: SidebarItemsGeneratorOptionArgs,
156
+ ) => Promise<SidebarItem[]>;
@@ -0,0 +1,146 @@
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 type {
9
+ Sidebars,
10
+ Sidebar,
11
+ SidebarItem,
12
+ SidebarItemCategory,
13
+ SidebarItemLink,
14
+ SidebarItemDoc,
15
+ SidebarItemType,
16
+ } from './types';
17
+ import {mapValues, difference} from 'lodash';
18
+ import {getElementsAround, toMessageRelativeFilePath} from '@docusaurus/utils';
19
+
20
+ export function transformSidebarItems(
21
+ sidebar: Sidebar,
22
+ updateFn: (item: SidebarItem) => SidebarItem,
23
+ ): Sidebar {
24
+ function transformRecursive(item: SidebarItem): SidebarItem {
25
+ if (item.type === 'category') {
26
+ return updateFn({
27
+ ...item,
28
+ items: item.items.map(transformRecursive),
29
+ });
30
+ }
31
+ return updateFn(item);
32
+ }
33
+ return sidebar.map(transformRecursive);
34
+ }
35
+
36
+ function collectSidebarItemsOfType<
37
+ Type extends SidebarItemType,
38
+ Item extends SidebarItem & {type: SidebarItemType},
39
+ >(type: Type, sidebar: Sidebar): Item[] {
40
+ function collectRecursive(item: SidebarItem): Item[] {
41
+ const currentItemsCollected: Item[] =
42
+ item.type === type ? [item as Item] : [];
43
+
44
+ const childItemsCollected: Item[] =
45
+ item.type === 'category' ? item.items.flatMap(collectRecursive) : [];
46
+
47
+ return [...currentItemsCollected, ...childItemsCollected];
48
+ }
49
+
50
+ return sidebar.flatMap(collectRecursive);
51
+ }
52
+
53
+ export function collectSidebarDocItems(sidebar: Sidebar): SidebarItemDoc[] {
54
+ return collectSidebarItemsOfType('doc', sidebar);
55
+ }
56
+ export function collectSidebarCategories(
57
+ sidebar: Sidebar,
58
+ ): SidebarItemCategory[] {
59
+ return collectSidebarItemsOfType('category', sidebar);
60
+ }
61
+ export function collectSidebarLinks(sidebar: Sidebar): SidebarItemLink[] {
62
+ return collectSidebarItemsOfType('link', sidebar);
63
+ }
64
+
65
+ export function collectSidebarsDocIds(
66
+ sidebars: Sidebars,
67
+ ): Record<string, string[]> {
68
+ return mapValues(sidebars, (sidebar) => {
69
+ return collectSidebarDocItems(sidebar).map((docItem) => docItem.id);
70
+ });
71
+ }
72
+
73
+ export function createSidebarsUtils(sidebars: Sidebars): {
74
+ getFirstDocIdOfFirstSidebar: () => string | undefined;
75
+ getSidebarNameByDocId: (docId: string) => string | undefined;
76
+ getDocNavigation: (docId: string) => {
77
+ sidebarName: string | undefined;
78
+ previousId: string | undefined;
79
+ nextId: string | undefined;
80
+ };
81
+ checkSidebarsDocIds: (validDocIds: string[], sidebarFilePath: string) => void;
82
+ } {
83
+ const sidebarNameToDocIds = collectSidebarsDocIds(sidebars);
84
+ // Reverse mapping
85
+ const docIdToSidebarName = Object.fromEntries(
86
+ Object.entries(sidebarNameToDocIds).flatMap(([sidebarName, docIds]) =>
87
+ docIds.map((docId) => [docId, sidebarName]),
88
+ ),
89
+ );
90
+
91
+ function getFirstDocIdOfFirstSidebar(): string | undefined {
92
+ return Object.values(sidebarNameToDocIds)[0]?.[0];
93
+ }
94
+
95
+ function getSidebarNameByDocId(docId: string): string | undefined {
96
+ return docIdToSidebarName[docId];
97
+ }
98
+
99
+ function getDocNavigation(docId: string): {
100
+ sidebarName: string | undefined;
101
+ previousId: string | undefined;
102
+ nextId: string | undefined;
103
+ } {
104
+ const sidebarName = getSidebarNameByDocId(docId);
105
+ if (sidebarName) {
106
+ const docIds = sidebarNameToDocIds[sidebarName];
107
+ const currentIndex = docIds.indexOf(docId);
108
+ const {previous, next} = getElementsAround(docIds, currentIndex);
109
+ return {
110
+ sidebarName,
111
+ previousId: previous,
112
+ nextId: next,
113
+ };
114
+ } else {
115
+ return {
116
+ sidebarName: undefined,
117
+ previousId: undefined,
118
+ nextId: undefined,
119
+ };
120
+ }
121
+ }
122
+
123
+ function checkSidebarsDocIds(validDocIds: string[], sidebarFilePath: string) {
124
+ const allSidebarDocIds = Object.values(sidebarNameToDocIds).flat();
125
+ const invalidSidebarDocIds = difference(allSidebarDocIds, validDocIds);
126
+ if (invalidSidebarDocIds.length > 0) {
127
+ throw new Error(
128
+ `Invalid sidebar file at "${toMessageRelativeFilePath(
129
+ sidebarFilePath,
130
+ )}".
131
+ These sidebar document ids do not exist:
132
+ - ${invalidSidebarDocIds.sort().join('\n- ')}
133
+
134
+ Available document ids are:
135
+ - ${validDocIds.sort().join('\n- ')}`,
136
+ );
137
+ }
138
+ }
139
+
140
+ return {
141
+ getFirstDocIdOfFirstSidebar,
142
+ getSidebarNameByDocId,
143
+ getDocNavigation,
144
+ checkSidebarsDocIds,
145
+ };
146
+ }