@docusaurus/plugin-content-docs 0.0.0-4545 → 0.0.0-4549
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/cli.js +2 -1
- package/lib/sidebars/generator.js +6 -7
- package/lib/sidebars/index.d.ts +3 -6
- package/lib/sidebars/index.js +8 -18
- package/lib/sidebars/normalization.d.ts +2 -3
- package/lib/sidebars/normalization.js +14 -32
- package/lib/sidebars/postProcessor.d.ts +8 -0
- package/lib/sidebars/postProcessor.js +71 -0
- package/lib/sidebars/processor.d.ts +2 -14
- package/lib/sidebars/processor.js +17 -55
- package/lib/sidebars/types.d.ts +24 -6
- package/lib/sidebars/validation.d.ts +2 -2
- package/lib/sidebars/validation.js +12 -27
- package/lib/types.d.ts +2 -6
- package/package.json +9 -9
- package/src/cli.ts +3 -2
- package/src/sidebars/README.md +9 -0
- package/src/sidebars/generator.ts +25 -22
- package/src/sidebars/index.ts +15 -31
- package/src/sidebars/normalization.ts +18 -46
- package/src/sidebars/postProcessor.ts +94 -0
- package/src/sidebars/processor.ts +26 -90
- package/src/sidebars/types.ts +32 -8
- package/src/sidebars/validation.ts +38 -50
- package/src/types.ts +2 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-content-docs",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-4549",
|
|
4
4
|
"description": "Docs plugin for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@docusaurus/core": "0.0.0-
|
|
27
|
-
"@docusaurus/logger": "0.0.0-
|
|
28
|
-
"@docusaurus/mdx-loader": "0.0.0-
|
|
29
|
-
"@docusaurus/utils": "0.0.0-
|
|
30
|
-
"@docusaurus/utils-validation": "0.0.0-
|
|
26
|
+
"@docusaurus/core": "0.0.0-4549",
|
|
27
|
+
"@docusaurus/logger": "0.0.0-4549",
|
|
28
|
+
"@docusaurus/mdx-loader": "0.0.0-4549",
|
|
29
|
+
"@docusaurus/utils": "0.0.0-4549",
|
|
30
|
+
"@docusaurus/utils-validation": "0.0.0-4549",
|
|
31
31
|
"combine-promises": "^1.1.0",
|
|
32
32
|
"fs-extra": "^10.0.0",
|
|
33
33
|
"import-fresh": "^3.2.2",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"webpack": "^5.68.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@docusaurus/module-type-aliases": "0.0.0-
|
|
44
|
-
"@docusaurus/types": "0.0.0-
|
|
43
|
+
"@docusaurus/module-type-aliases": "0.0.0-4549",
|
|
44
|
+
"@docusaurus/types": "0.0.0-4549",
|
|
45
45
|
"@types/js-yaml": "^4.0.0",
|
|
46
46
|
"@types/picomatch": "^2.2.1",
|
|
47
47
|
"commander": "^5.1.0",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=14"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "ed41c3cb08710f3a6211b2a17ff658f9dd41f164"
|
|
61
61
|
}
|
package/src/cli.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
PathOptions,
|
|
17
17
|
SidebarOptions,
|
|
18
18
|
} from '@docusaurus/plugin-content-docs';
|
|
19
|
-
import {
|
|
19
|
+
import {loadSidebarsFileUnsafe, resolveSidebarPathOption} from './sidebars';
|
|
20
20
|
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
|
21
21
|
import logger from '@docusaurus/logger';
|
|
22
22
|
|
|
@@ -34,7 +34,8 @@ async function createVersionedSidebarFile({
|
|
|
34
34
|
// Load current sidebar and create a new versioned sidebars file (if needed).
|
|
35
35
|
// Note: we don't need the sidebars file to be normalized: it's ok to let
|
|
36
36
|
// plugin option changes to impact older, versioned sidebars
|
|
37
|
-
|
|
37
|
+
// We don't validate here, assuming the user has already built the version
|
|
38
|
+
const sidebars = await loadSidebarsFileUnsafe(sidebarPath);
|
|
38
39
|
|
|
39
40
|
// Do not create a useless versioned sidebars file if sidebars file is empty
|
|
40
41
|
// or sidebars are disabled/false)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Sidebars
|
|
2
|
+
|
|
3
|
+
This part is very complicated and hard to navigate. Sidebars are loaded through the following steps:
|
|
4
|
+
|
|
5
|
+
1. **Loading**. The sidebars file is read. Returns `SidebarsConfig`.
|
|
6
|
+
2. **Normalization**. The shorthands are expanded. This step is very lenient about the sidebars' shapes. Returns `NormalizedSidebars`.
|
|
7
|
+
3. **Validation**. The normalized sidebars are validated. This step happens after normalization, because the normalized sidebars are easier to validate, and allows us to repeatedly validate & generate in the future.
|
|
8
|
+
4. **Generation**. This step is done through the "processor" (naming is hard). The `autogenerated` items are unwrapped. In the future, steps 3 and 4 may be repeatedly done until all autogenerated items are unwrapped. Returns `ProcessedSidebars`.
|
|
9
|
+
5. **Post-processing**. Defaults are applied (collapsed states), category links are resolved, empty categories are flattened. Returns `Sidebars`.
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
|
-
SidebarItem,
|
|
10
9
|
SidebarItemDoc,
|
|
11
|
-
SidebarItemCategory,
|
|
12
10
|
SidebarItemsGenerator,
|
|
13
11
|
SidebarItemsGeneratorDoc,
|
|
14
|
-
|
|
12
|
+
NormalizedSidebarItemCategory,
|
|
13
|
+
NormalizedSidebarItem,
|
|
14
|
+
SidebarItemCategoryLinkConfig,
|
|
15
15
|
} from './types';
|
|
16
16
|
import {sortBy, last} from 'lodash';
|
|
17
17
|
import {addTrailingSlash, posixPath} from '@docusaurus/utils';
|
|
@@ -48,7 +48,6 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
48
48
|
numberPrefixParser,
|
|
49
49
|
isCategoryIndex,
|
|
50
50
|
docs: allDocs,
|
|
51
|
-
options,
|
|
52
51
|
item: {dirName: autogenDir},
|
|
53
52
|
categoriesMetadata,
|
|
54
53
|
}) => {
|
|
@@ -125,7 +124,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
125
124
|
* Step 3. Recursively transform the tree-like structure to sidebar items.
|
|
126
125
|
* (From a record to an array of items, akin to normalizing shorthand)
|
|
127
126
|
*/
|
|
128
|
-
function generateSidebar(
|
|
127
|
+
function generateSidebar(
|
|
128
|
+
fsModel: Dir,
|
|
129
|
+
): Promise<WithPosition<NormalizedSidebarItem>[]> {
|
|
129
130
|
function createDocItem(id: string): WithPosition<SidebarItemDoc> {
|
|
130
131
|
const {
|
|
131
132
|
sidebarPosition: position,
|
|
@@ -145,7 +146,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
145
146
|
dir: Dir,
|
|
146
147
|
fullPath: string,
|
|
147
148
|
folderName: string,
|
|
148
|
-
): Promise<WithPosition<
|
|
149
|
+
): Promise<WithPosition<NormalizedSidebarItemCategory>> {
|
|
149
150
|
const categoryMetadata =
|
|
150
151
|
categoriesMetadata[posixPath(path.join(autogenDir, fullPath))];
|
|
151
152
|
const className = categoryMetadata?.className;
|
|
@@ -160,18 +161,19 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
160
161
|
// using the "local id" (myDoc) or "qualified id" (dirName/myDoc)
|
|
161
162
|
function findDocByLocalId(localId: string): SidebarItemDoc | undefined {
|
|
162
163
|
return allItems.find(
|
|
163
|
-
(item)
|
|
164
|
-
|
|
164
|
+
(item): item is SidebarItemDoc =>
|
|
165
|
+
item.type === 'doc' && getLocalDocId(item.id) === localId,
|
|
166
|
+
);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
function findConventionalCategoryDocLink(): SidebarItemDoc | undefined {
|
|
168
|
-
return allItems.find((item) => {
|
|
170
|
+
return allItems.find((item): item is SidebarItemDoc => {
|
|
169
171
|
if (item.type !== 'doc') {
|
|
170
172
|
return false;
|
|
171
173
|
}
|
|
172
174
|
const doc = getDoc(item.id);
|
|
173
175
|
return isCategoryIndex(toCategoryIndexMatcherParam(doc));
|
|
174
|
-
})
|
|
176
|
+
});
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
function getCategoryLinkedDocId(): string | undefined {
|
|
@@ -190,13 +192,13 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
190
192
|
|
|
191
193
|
const categoryLinkedDocId = getCategoryLinkedDocId();
|
|
192
194
|
|
|
193
|
-
const link:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
const link: SidebarItemCategoryLinkConfig | null | undefined =
|
|
196
|
+
categoryLinkedDocId
|
|
197
|
+
? {
|
|
198
|
+
type: 'doc',
|
|
199
|
+
id: categoryLinkedDocId, // We "remap" a potentially "local id" to a "qualified id"
|
|
200
|
+
}
|
|
201
|
+
: categoryMetadata?.link;
|
|
200
202
|
|
|
201
203
|
// If a doc is linked, remove it from the category subItems
|
|
202
204
|
const items = allItems.filter(
|
|
@@ -206,9 +208,8 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
206
208
|
return {
|
|
207
209
|
type: 'category',
|
|
208
210
|
label: categoryMetadata?.label ?? filename,
|
|
209
|
-
collapsible:
|
|
210
|
-
|
|
211
|
-
collapsed: categoryMetadata?.collapsed ?? options.sidebarCollapsed,
|
|
211
|
+
collapsible: categoryMetadata?.collapsible,
|
|
212
|
+
collapsed: categoryMetadata?.collapsed,
|
|
212
213
|
position: categoryMetadata?.position ?? numberPrefix,
|
|
213
214
|
...(className !== undefined && {className}),
|
|
214
215
|
items,
|
|
@@ -219,7 +220,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
219
220
|
dir: Dir | null, // The directory item to be transformed.
|
|
220
221
|
itemKey: string, // For docs, it's the doc ID; for categories, it's used to generate the next `relativePath`.
|
|
221
222
|
fullPath: string, // `dir`'s full path relative to the autogen dir.
|
|
222
|
-
): Promise<WithPosition<
|
|
223
|
+
): Promise<WithPosition<NormalizedSidebarItem>> {
|
|
223
224
|
return dir
|
|
224
225
|
? createCategoryItem(dir, fullPath, itemKey)
|
|
225
226
|
: createDocItem(itemKey.substring(docIdPrefix.length));
|
|
@@ -238,7 +239,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|
|
238
239
|
* consecutive sidebar slices (i.e. a whole category composed of multiple
|
|
239
240
|
* autogenerated items)
|
|
240
241
|
*/
|
|
241
|
-
function sortItems(
|
|
242
|
+
function sortItems(
|
|
243
|
+
sidebarItems: WithPosition<NormalizedSidebarItem>[],
|
|
244
|
+
): NormalizedSidebarItem[] {
|
|
242
245
|
const processedSidebarItems = sidebarItems.map((item) => {
|
|
243
246
|
if (item.type === 'category') {
|
|
244
247
|
return {...item, items: sortItems(item.items)};
|
package/src/sidebars/index.ts
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
import importFresh from 'import-fresh';
|
|
10
|
-
import type {SidebarsConfig, Sidebars,
|
|
11
|
-
import type {NormalizeSidebarsParams} from '../types';
|
|
10
|
+
import type {SidebarsConfig, Sidebars, SidebarProcessorParams} from './types';
|
|
12
11
|
import {validateSidebars, validateCategoryMetadataFile} from './validation';
|
|
13
12
|
import {normalizeSidebars} from './normalization';
|
|
14
|
-
import {processSidebars
|
|
13
|
+
import {processSidebars} from './processor';
|
|
14
|
+
import {postProcessSidebars} from './postProcessor';
|
|
15
15
|
import path from 'path';
|
|
16
|
-
import {
|
|
16
|
+
import {Globby} from '@docusaurus/utils';
|
|
17
17
|
import logger from '@docusaurus/logger';
|
|
18
18
|
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
|
|
19
19
|
import Yaml from 'js-yaml';
|
|
@@ -69,7 +69,7 @@ async function readCategoriesMetadata(contentPath: string) {
|
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
async function loadSidebarsFileUnsafe(
|
|
72
|
+
export async function loadSidebarsFileUnsafe(
|
|
73
73
|
sidebarFilePath: string | false | undefined,
|
|
74
74
|
): Promise<SidebarsConfig> {
|
|
75
75
|
// false => no sidebars
|
|
@@ -93,37 +93,21 @@ async function loadSidebarsFileUnsafe(
|
|
|
93
93
|
return importFresh(sidebarFilePath);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export async function loadSidebarsFile(
|
|
97
|
-
sidebarFilePath: string | false | undefined,
|
|
98
|
-
): Promise<SidebarsConfig> {
|
|
99
|
-
const sidebarsConfig = await loadSidebarsFileUnsafe(sidebarFilePath);
|
|
100
|
-
validateSidebars(sidebarsConfig);
|
|
101
|
-
return sidebarsConfig;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function loadNormalizedSidebars(
|
|
105
|
-
sidebarFilePath: string | false | undefined,
|
|
106
|
-
params: NormalizeSidebarsParams,
|
|
107
|
-
): Promise<NormalizedSidebars> {
|
|
108
|
-
return normalizeSidebars(await loadSidebarsFile(sidebarFilePath), params);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
96
|
// Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
|
|
112
97
|
export async function loadSidebars(
|
|
113
98
|
sidebarFilePath: string | false | undefined,
|
|
114
|
-
options:
|
|
99
|
+
options: SidebarProcessorParams,
|
|
115
100
|
): Promise<Sidebars> {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
categoryLabelSlugger: createSlugger(),
|
|
120
|
-
};
|
|
121
|
-
const normalizedSidebars = await loadNormalizedSidebars(
|
|
122
|
-
sidebarFilePath,
|
|
123
|
-
normalizeSidebarsParams,
|
|
124
|
-
);
|
|
101
|
+
const sidebarsConfig = await loadSidebarsFileUnsafe(sidebarFilePath);
|
|
102
|
+
const normalizedSidebars = normalizeSidebars(sidebarsConfig);
|
|
103
|
+
validateSidebars(normalizedSidebars);
|
|
125
104
|
const categoriesMetadata = await readCategoriesMetadata(
|
|
126
105
|
options.version.contentPath,
|
|
127
106
|
);
|
|
128
|
-
|
|
107
|
+
const processedSidebars = await processSidebars(
|
|
108
|
+
normalizedSidebars,
|
|
109
|
+
categoriesMetadata,
|
|
110
|
+
options,
|
|
111
|
+
);
|
|
112
|
+
return postProcessSidebars(processedSidebars, options);
|
|
129
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,16 @@ 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
20
|
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
|
-
}
|
|
44
21
|
|
|
45
22
|
function normalizeCategoriesShorthand(
|
|
46
23
|
sidebar: SidebarCategoriesShorthand,
|
|
47
|
-
options: SidebarOptions,
|
|
48
24
|
): SidebarItemCategoryConfig[] {
|
|
49
25
|
return Object.entries(sidebar).map(([label, items]) => ({
|
|
50
26
|
type: 'category',
|
|
51
|
-
collapsed: options.sidebarCollapsed,
|
|
52
|
-
collapsible: options.sidebarCollapsible,
|
|
53
27
|
label,
|
|
54
28
|
items,
|
|
55
29
|
}));
|
|
@@ -61,7 +35,6 @@ function normalizeCategoriesShorthand(
|
|
|
61
35
|
*/
|
|
62
36
|
export function normalizeItem(
|
|
63
37
|
item: SidebarItemConfig,
|
|
64
|
-
options: NormalizeSidebarsParams,
|
|
65
38
|
): NormalizedSidebarItem[] {
|
|
66
39
|
if (typeof item === 'string') {
|
|
67
40
|
return [
|
|
@@ -72,42 +45,41 @@ export function normalizeItem(
|
|
|
72
45
|
];
|
|
73
46
|
}
|
|
74
47
|
if (isCategoriesShorthand(item)) {
|
|
75
|
-
return normalizeCategoriesShorthand(item
|
|
76
|
-
normalizeItem(subItem
|
|
48
|
+
return normalizeCategoriesShorthand(item).flatMap((subItem) =>
|
|
49
|
+
normalizeItem(subItem),
|
|
77
50
|
);
|
|
78
51
|
}
|
|
79
52
|
if (item.type === 'category') {
|
|
80
|
-
|
|
53
|
+
if (typeof item.items !== 'undefined' && typeof item.items !== 'object') {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Invalid category ${JSON.stringify(
|
|
56
|
+
item,
|
|
57
|
+
)}: items must be an array of sidebar items or a category shorthand`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
81
60
|
const normalizedCategory: NormalizedSidebarItemCategory = {
|
|
82
61
|
...item,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
collapsed: item.collapsed ?? options.sidebarCollapsed,
|
|
62
|
+
items: Array.isArray(item.items)
|
|
63
|
+
? item.items.flatMap((subItem) => normalizeItem(subItem))
|
|
64
|
+
: normalizeCategoriesShorthand(item.items).flatMap((subItem) =>
|
|
65
|
+
normalizeItem(subItem),
|
|
66
|
+
),
|
|
89
67
|
};
|
|
90
68
|
return [normalizedCategory];
|
|
91
69
|
}
|
|
92
70
|
return [item];
|
|
93
71
|
}
|
|
94
72
|
|
|
95
|
-
function normalizeSidebar(
|
|
96
|
-
sidebar: SidebarConfig,
|
|
97
|
-
options: NormalizeSidebarsParams,
|
|
98
|
-
): NormalizedSidebar {
|
|
73
|
+
function normalizeSidebar(sidebar: SidebarConfig): NormalizedSidebar {
|
|
99
74
|
const normalizedSidebar = Array.isArray(sidebar)
|
|
100
75
|
? sidebar
|
|
101
|
-
: normalizeCategoriesShorthand(sidebar
|
|
76
|
+
: normalizeCategoriesShorthand(sidebar);
|
|
102
77
|
|
|
103
|
-
return normalizedSidebar.flatMap((subItem) =>
|
|
104
|
-
normalizeItem(subItem, options),
|
|
105
|
-
);
|
|
78
|
+
return normalizedSidebar.flatMap((subItem) => normalizeItem(subItem));
|
|
106
79
|
}
|
|
107
80
|
|
|
108
81
|
export function normalizeSidebars(
|
|
109
82
|
sidebars: SidebarsConfig,
|
|
110
|
-
params: NormalizeSidebarsParams,
|
|
111
83
|
): NormalizedSidebars {
|
|
112
|
-
return mapValues(sidebars,
|
|
84
|
+
return mapValues(sidebars, normalizeSidebar);
|
|
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 {mapValues} 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,41 +7,23 @@
|
|
|
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,
|
|
22
20
|
CategoryMetadataFile,
|
|
23
21
|
} from './types';
|
|
24
|
-
import {transformSidebarItems} from './utils';
|
|
25
22
|
import {DefaultSidebarItemsGenerator} from './generator';
|
|
23
|
+
import {validateSidebars} from './validation';
|
|
26
24
|
import {mapValues, memoize, pick} from 'lodash';
|
|
27
25
|
import combinePromises from 'combine-promises';
|
|
28
|
-
import {normalizeItem} from './normalization';
|
|
29
26
|
import {isCategoryIndex} from '../docs';
|
|
30
|
-
import type {Slugger} from '@docusaurus/utils';
|
|
31
|
-
import type {
|
|
32
|
-
NumberPrefixParser,
|
|
33
|
-
SidebarOptions,
|
|
34
|
-
} from '@docusaurus/plugin-content-docs';
|
|
35
|
-
|
|
36
|
-
export type SidebarProcessorParams = {
|
|
37
|
-
sidebarItemsGenerator: SidebarItemsGeneratorOption;
|
|
38
|
-
numberPrefixParser: NumberPrefixParser;
|
|
39
|
-
docs: DocMetadataBase[];
|
|
40
|
-
version: VersionMetadata;
|
|
41
|
-
categoryLabelSlugger: Slugger;
|
|
42
|
-
sidebarOptions: SidebarOptions;
|
|
43
|
-
categoriesMetadata: Record<string, CategoryMetadataFile>;
|
|
44
|
-
};
|
|
45
27
|
|
|
46
28
|
function toSidebarItemsGeneratorDoc(
|
|
47
29
|
doc: DocMetadataBase,
|
|
@@ -66,15 +48,15 @@ function toSidebarItemsGeneratorVersion(
|
|
|
66
48
|
// post-processing checks
|
|
67
49
|
async function processSidebar(
|
|
68
50
|
unprocessedSidebar: NormalizedSidebar,
|
|
51
|
+
categoriesMetadata: Record<string, CategoryMetadataFile>,
|
|
69
52
|
params: SidebarProcessorParams,
|
|
70
|
-
): Promise<
|
|
53
|
+
): Promise<ProcessedSidebar> {
|
|
71
54
|
const {
|
|
72
55
|
sidebarItemsGenerator,
|
|
73
56
|
numberPrefixParser,
|
|
74
57
|
docs,
|
|
75
58
|
version,
|
|
76
59
|
sidebarOptions,
|
|
77
|
-
categoriesMetadata,
|
|
78
60
|
} = params;
|
|
79
61
|
|
|
80
62
|
// Just a minor lazy transformation optimization
|
|
@@ -83,20 +65,9 @@ async function processSidebar(
|
|
|
83
65
|
version: toSidebarItemsGeneratorVersion(version),
|
|
84
66
|
}));
|
|
85
67
|
|
|
86
|
-
async function processCategoryItem(
|
|
87
|
-
item: NormalizedSidebarItemCategory,
|
|
88
|
-
): Promise<SidebarItemCategory> {
|
|
89
|
-
return {
|
|
90
|
-
...item,
|
|
91
|
-
items: (await Promise.all(item.items.map(processItem))).flat(),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
68
|
async function processAutoGeneratedItem(
|
|
96
69
|
item: SidebarItemAutogenerated,
|
|
97
|
-
): Promise<
|
|
98
|
-
// TODO the returned type can't be trusted in practice (generator can be
|
|
99
|
-
// user-provided)
|
|
70
|
+
): Promise<ProcessedSidebarItem[]> {
|
|
100
71
|
const generatedItems = await sidebarItemsGenerator({
|
|
101
72
|
item,
|
|
102
73
|
numberPrefixParser,
|
|
@@ -106,50 +77,23 @@ async function processSidebar(
|
|
|
106
77
|
options: sidebarOptions,
|
|
107
78
|
categoriesMetadata,
|
|
108
79
|
});
|
|
109
|
-
// TODO validate generated items: user can generate bad items
|
|
110
|
-
|
|
111
|
-
const generatedItemsNormalized = generatedItems.flatMap((generatedItem) =>
|
|
112
|
-
normalizeItem(generatedItem, {...params, ...sidebarOptions}),
|
|
113
|
-
);
|
|
114
|
-
|
|
115
80
|
// Process again... weird but sidebar item generated might generate some
|
|
116
81
|
// auto-generated items?
|
|
117
|
-
|
|
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);
|
|
118
85
|
}
|
|
119
86
|
|
|
120
87
|
async function processItem(
|
|
121
88
|
item: NormalizedSidebarItem,
|
|
122
|
-
): Promise<
|
|
89
|
+
): Promise<ProcessedSidebarItem[]> {
|
|
123
90
|
if (item.type === 'category') {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
switch (item.link.type) {
|
|
132
|
-
case 'doc':
|
|
133
|
-
return [
|
|
134
|
-
{
|
|
135
|
-
type: 'doc',
|
|
136
|
-
label: item.label,
|
|
137
|
-
id: item.link.id,
|
|
138
|
-
},
|
|
139
|
-
];
|
|
140
|
-
case 'generated-index':
|
|
141
|
-
return [
|
|
142
|
-
{
|
|
143
|
-
type: 'link',
|
|
144
|
-
label: item.label,
|
|
145
|
-
href: item.link.permalink,
|
|
146
|
-
},
|
|
147
|
-
];
|
|
148
|
-
default:
|
|
149
|
-
throw new Error('Unexpected sidebar category link type');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return [await processCategoryItem(item)];
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
...item,
|
|
94
|
+
items: (await Promise.all(item.items.map(processItem))).flat(),
|
|
95
|
+
},
|
|
96
|
+
];
|
|
153
97
|
}
|
|
154
98
|
if (item.type === 'autogenerated') {
|
|
155
99
|
return processAutoGeneratedItem(item);
|
|
@@ -159,32 +103,24 @@ async function processSidebar(
|
|
|
159
103
|
|
|
160
104
|
async function processItems(
|
|
161
105
|
items: NormalizedSidebarItem[],
|
|
162
|
-
): Promise<
|
|
106
|
+
): Promise<ProcessedSidebarItem[]> {
|
|
163
107
|
return (await Promise.all(items.map(processItem))).flat();
|
|
164
108
|
}
|
|
165
109
|
|
|
166
110
|
const processedSidebar = await processItems(unprocessedSidebar);
|
|
167
|
-
|
|
168
|
-
const fixSidebarItemInconsistencies = (item: SidebarItem): SidebarItem => {
|
|
169
|
-
// A non-collapsible category can't be collapsed!
|
|
170
|
-
if (item.type === 'category' && !item.collapsible && item.collapsed) {
|
|
171
|
-
return {
|
|
172
|
-
...item,
|
|
173
|
-
collapsed: false,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
return item;
|
|
177
|
-
};
|
|
178
|
-
return transformSidebarItems(processedSidebar, fixSidebarItemInconsistencies);
|
|
111
|
+
return processedSidebar;
|
|
179
112
|
}
|
|
180
113
|
|
|
181
114
|
export async function processSidebars(
|
|
182
115
|
unprocessedSidebars: NormalizedSidebars,
|
|
116
|
+
categoriesMetadata: Record<string, CategoryMetadataFile>,
|
|
183
117
|
params: SidebarProcessorParams,
|
|
184
|
-
): Promise<
|
|
185
|
-
|
|
118
|
+
): Promise<ProcessedSidebars> {
|
|
119
|
+
const processedSidebars = await combinePromises(
|
|
186
120
|
mapValues(unprocessedSidebars, (unprocessedSidebar) =>
|
|
187
|
-
processSidebar(unprocessedSidebar, params),
|
|
121
|
+
processSidebar(unprocessedSidebar, categoriesMetadata, params),
|
|
188
122
|
),
|
|
189
123
|
);
|
|
124
|
+
validateSidebars(processedSidebars);
|
|
125
|
+
return processedSidebars;
|
|
190
126
|
}
|