@docusaurus/plugin-content-docs 0.0.0-4547 → 0.0.0-4551

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.
@@ -8,7 +8,6 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.validateCategoryMetadataFile = exports.validateSidebars = void 0;
10
10
  const utils_validation_1 = require("@docusaurus/utils-validation");
11
- const utils_1 = require("./utils");
12
11
  // NOTE: we don't add any default values during validation on purpose!
13
12
  // Config types are exposed to users for typechecking and we use the same type
14
13
  // in normalization
@@ -31,7 +30,7 @@ const sidebarItemDocSchema = sidebarItemBaseSchema.append({
31
30
  const sidebarItemHtmlSchema = sidebarItemBaseSchema.append({
32
31
  type: 'html',
33
32
  value: utils_validation_1.Joi.string().required(),
34
- defaultStyle: utils_validation_1.Joi.boolean().default(false),
33
+ defaultStyle: utils_validation_1.Joi.boolean(),
35
34
  });
36
35
  const sidebarItemLinkSchema = sidebarItemBaseSchema.append({
37
36
  type: 'link',
@@ -65,23 +64,23 @@ const sidebarItemCategoryLinkSchema = utils_validation_1.Joi.object()
65
64
  }),
66
65
  },
67
66
  {
68
- is: utils_validation_1.Joi.string().required(),
67
+ is: utils_validation_1.Joi.required(),
69
68
  then: utils_validation_1.Joi.forbidden().messages({
70
69
  'any.unknown': 'Unknown sidebar category link type "{.type}".',
71
70
  }),
72
71
  },
73
72
  ],
74
- })
75
- .id('sidebarCategoryLinkSchema');
73
+ });
76
74
  const sidebarItemCategorySchema = sidebarItemBaseSchema.append({
77
75
  type: 'category',
78
76
  label: utils_validation_1.Joi.string()
79
77
  .required()
80
78
  .messages({ 'any.unknown': '"label" must be a string' }),
81
- // TODO: Joi doesn't allow mutual recursion. See https://github.com/sideway/joi/issues/2611
82
79
  items: utils_validation_1.Joi.array()
83
80
  .required()
84
81
  .messages({ 'any.unknown': '"items" must be an array' }),
82
+ // TODO: Joi doesn't allow mutual recursion. See https://github.com/sideway/joi/issues/2611
83
+ // .items(Joi.link('#sidebarItemSchema')),
85
84
  link: sidebarItemCategoryLinkSchema,
86
85
  collapsed: utils_validation_1.Joi.boolean().messages({
87
86
  'any.unknown': '"collapsed" must be a boolean',
@@ -90,8 +89,7 @@ const sidebarItemCategorySchema = sidebarItemBaseSchema.append({
90
89
  'any.unknown': '"collapsible" must be a boolean',
91
90
  }),
92
91
  });
93
- const sidebarItemSchema = utils_validation_1.Joi.object()
94
- .when('.type', {
92
+ const sidebarItemSchema = utils_validation_1.Joi.object().when('.type', {
95
93
  switch: [
96
94
  { is: 'link', then: sidebarItemLinkSchema },
97
95
  {
@@ -108,33 +106,20 @@ const sidebarItemSchema = utils_validation_1.Joi.object()
108
106
  }),
109
107
  },
110
108
  ],
111
- })
112
- .id('sidebarItemSchema');
109
+ });
110
+ // .id('sidebarItemSchema');
113
111
  function validateSidebarItem(item) {
114
- if (typeof item === 'string') {
115
- return;
116
- }
117
112
  // TODO: remove once with proper Joi support
118
113
  // Because we can't use Joi to validate nested items (see above), we do it
119
114
  // manually
120
- if ((0, utils_1.isCategoriesShorthand)(item)) {
121
- Object.values(item).forEach((category) => category.forEach(validateSidebarItem));
122
- }
123
- else {
124
- utils_validation_1.Joi.assert(item, sidebarItemSchema);
125
- if (item.type === 'category') {
126
- item.items.forEach(validateSidebarItem);
127
- }
115
+ utils_validation_1.Joi.assert(item, sidebarItemSchema);
116
+ if (item.type === 'category') {
117
+ item.items.forEach(validateSidebarItem);
128
118
  }
129
119
  }
130
120
  function validateSidebars(sidebars) {
131
121
  Object.values(sidebars).forEach((sidebar) => {
132
- if (Array.isArray(sidebar)) {
133
- sidebar.forEach(validateSidebarItem);
134
- }
135
- else {
136
- validateSidebarItem(sidebar);
137
- }
122
+ sidebar.forEach(validateSidebarItem);
138
123
  });
139
124
  }
140
125
  exports.validateSidebars = validateSidebars;
package/lib/types.d.ts CHANGED
@@ -5,9 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { Sidebars } from './sidebars/types';
8
- import type { Tag, FrontMatterTag, Slugger } from '@docusaurus/utils';
8
+ import type { Tag, FrontMatterTag } from '@docusaurus/utils';
9
9
  import type { BrokenMarkdownLink as IBrokenMarkdownLink, ContentPaths } from '@docusaurus/utils/lib/markdownLinks';
10
- import type { VersionBanner, SidebarOptions } from '@docusaurus/plugin-content-docs';
10
+ import type { VersionBanner } from '@docusaurus/plugin-content-docs';
11
11
  export declare type DocFile = {
12
12
  contentPath: string;
13
13
  filePath: string;
@@ -29,10 +29,6 @@ export declare type VersionMetadata = ContentPaths & {
29
29
  sidebarFilePath: string | false | undefined;
30
30
  routePriority: number | undefined;
31
31
  };
32
- export declare type NormalizeSidebarsParams = SidebarOptions & {
33
- version: VersionMetadata;
34
- categoryLabelSlugger: Slugger;
35
- };
36
32
  export declare type LastUpdateData = {
37
33
  lastUpdatedAt?: number;
38
34
  formattedLastUpdatedAt?: string;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-docs",
3
- "version": "0.0.0-4547",
3
+ "version": "0.0.0-4551",
4
4
  "description": "Docs plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
7
7
  "./client": "./lib/client/index.js",
8
- "./server": "./lib/server/index.js",
8
+ "./server": "./lib/server-export.js",
9
9
  ".": "./lib/index.js"
10
10
  },
11
11
  "types": "src/plugin-content-docs.d.ts",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
- "@docusaurus/core": "0.0.0-4547",
27
- "@docusaurus/logger": "0.0.0-4547",
28
- "@docusaurus/mdx-loader": "0.0.0-4547",
29
- "@docusaurus/utils": "0.0.0-4547",
30
- "@docusaurus/utils-validation": "0.0.0-4547",
26
+ "@docusaurus/core": "0.0.0-4551",
27
+ "@docusaurus/logger": "0.0.0-4551",
28
+ "@docusaurus/mdx-loader": "0.0.0-4551",
29
+ "@docusaurus/utils": "0.0.0-4551",
30
+ "@docusaurus/utils-validation": "0.0.0-4551",
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-4547",
44
- "@docusaurus/types": "0.0.0-4547",
43
+ "@docusaurus/module-type-aliases": "0.0.0-4551",
44
+ "@docusaurus/types": "0.0.0-4551",
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": "17925a2f808cffa695698b1fe049d25524a48982"
60
+ "gitHead": "a948920b43d31d651edbe5d642ccd9fb88b09ecd"
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 {loadSidebarsFile, resolveSidebarPathOption} from './sidebars';
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
- const sidebars = await loadSidebarsFile(sidebarPath);
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)
@@ -5,4 +5,100 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- export * from './globalDataHooks';
8
+ import {useLocation} from '@docusaurus/router';
9
+ import useGlobalData, {usePluginData} from '@docusaurus/useGlobalData';
10
+
11
+ import {
12
+ getActivePlugin,
13
+ getLatestVersion,
14
+ getActiveVersion,
15
+ getActiveDocContext,
16
+ getDocVersionSuggestions,
17
+ } from './docsClientUtils';
18
+ import type {
19
+ GlobalPluginData,
20
+ GlobalVersion,
21
+ ActivePlugin,
22
+ ActiveDocContext,
23
+ DocVersionSuggestions,
24
+ GetActivePluginOptions,
25
+ } from '@docusaurus/plugin-content-docs/client';
26
+
27
+ // Important to use a constant object to avoid React useEffect executions etc.
28
+ // see https://github.com/facebook/docusaurus/issues/5089
29
+ const StableEmptyObject = {};
30
+
31
+ // Not using useAllPluginInstancesData() because in blog-only mode, docs hooks
32
+ // are still used by the theme. We need a fail-safe fallback when the docs
33
+ // plugin is not in use
34
+ export const useAllDocsData = (): Record<string, GlobalPluginData> =>
35
+ useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject;
36
+
37
+ export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
38
+ usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData;
39
+
40
+ // TODO this feature should be provided by docusaurus core
41
+ export const useActivePlugin = (
42
+ options: GetActivePluginOptions = {},
43
+ ): ActivePlugin | undefined => {
44
+ const data = useAllDocsData();
45
+ const {pathname} = useLocation();
46
+ return getActivePlugin(data, pathname, options);
47
+ };
48
+
49
+ export const useActivePluginAndVersion = (
50
+ options: GetActivePluginOptions = {},
51
+ ):
52
+ | undefined
53
+ | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => {
54
+ const activePlugin = useActivePlugin(options);
55
+ const {pathname} = useLocation();
56
+ if (activePlugin) {
57
+ const activeVersion = getActiveVersion(activePlugin.pluginData, pathname);
58
+ return {
59
+ activePlugin,
60
+ activeVersion,
61
+ };
62
+ }
63
+ return undefined;
64
+ };
65
+
66
+ // versions are returned ordered (most recent first)
67
+ export const useVersions = (pluginId: string | undefined): GlobalVersion[] => {
68
+ const data = useDocsData(pluginId);
69
+ return data.versions;
70
+ };
71
+
72
+ export const useLatestVersion = (
73
+ pluginId: string | undefined,
74
+ ): GlobalVersion => {
75
+ const data = useDocsData(pluginId);
76
+ return getLatestVersion(data);
77
+ };
78
+
79
+ // Note: return undefined on doc-unrelated pages,
80
+ // because there's no version currently considered as active
81
+ export const useActiveVersion = (
82
+ pluginId: string | undefined,
83
+ ): GlobalVersion | undefined => {
84
+ const data = useDocsData(pluginId);
85
+ const {pathname} = useLocation();
86
+ return getActiveVersion(data, pathname);
87
+ };
88
+
89
+ export const useActiveDocContext = (
90
+ pluginId: string | undefined,
91
+ ): ActiveDocContext => {
92
+ const data = useDocsData(pluginId);
93
+ const {pathname} = useLocation();
94
+ return getActiveDocContext(data, pathname);
95
+ };
96
+
97
+ // Useful to say "hey, you are not on the latest docs version, please switch"
98
+ export const useDocVersionSuggestions = (
99
+ pluginId: string | undefined,
100
+ ): DocVersionSuggestions => {
101
+ const data = useDocsData(pluginId);
102
+ const {pathname} = useLocation();
103
+ return getDocVersionSuggestions(data, pathname);
104
+ };
@@ -6,7 +6,12 @@
6
6
  */
7
7
 
8
8
  // APIs available to Node.js
9
- export * from '../constants';
9
+ export {
10
+ CURRENT_VERSION_NAME,
11
+ VERSIONED_DOCS_DIR,
12
+ VERSIONED_SIDEBARS_DIR,
13
+ VERSIONS_JSON_FILE,
14
+ } from './constants';
10
15
 
11
16
  export {
12
17
  filterVersions,
@@ -16,4 +21,4 @@ export {
16
21
  getVersionsFilePath,
17
22
  readVersionsFile,
18
23
  readVersionNames,
19
- } from '../versions';
24
+ } from './versions';
@@ -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
- SidebarItemCategoryLink,
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(fsModel: Dir): Promise<WithPosition<SidebarItem>[]> {
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<SidebarItemCategory>> {
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) => item.type === 'doc' && getLocalDocId(item.id) === localId,
164
- ) as SidebarItemDoc | undefined;
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
- }) as SidebarItemDoc | undefined;
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: SidebarItemCategoryLink | undefined = categoryLinkedDocId
194
- ? {
195
- type: 'doc',
196
- id: categoryLinkedDocId, // We "remap" a potentially "local id" to a "qualified id"
197
- }
198
- : // TODO typing issue
199
- (categoryMetadata?.link as SidebarItemCategoryLink | undefined);
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
- categoryMetadata?.collapsible ?? options.sidebarCollapsible,
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<SidebarItem>> {
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(sidebarItems: WithPosition<SidebarItem>[]): SidebarItem[] {
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)};
@@ -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, NormalizedSidebars} from './types';
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, type SidebarProcessorParams} from './processor';
13
+ import {processSidebars} from './processor';
14
+ import {postProcessSidebars} from './postProcessor';
15
15
  import path from 'path';
16
- import {createSlugger, Globby} from '@docusaurus/utils';
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: Omit<SidebarProcessorParams, 'categoriesMetadata'>,
99
+ options: SidebarProcessorParams,
115
100
  ): Promise<Sidebars> {
116
- const normalizeSidebarsParams: NormalizeSidebarsParams = {
117
- ...options.sidebarOptions,
118
- version: options.version,
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
- return processSidebars(normalizedSidebars, {...options, categoriesMetadata});
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, options).flatMap((subItem) =>
76
- normalizeItem(subItem, options),
48
+ return normalizeCategoriesShorthand(item).flatMap((subItem) =>
49
+ normalizeItem(subItem),
77
50
  );
78
51
  }
79
52
  if (item.type === 'category') {
80
- const link = normalizeCategoryLink(item, options);
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
- link,
84
- items: (item.items ?? []).flatMap((subItem) =>
85
- normalizeItem(subItem, options),
86
- ),
87
- collapsible: item.collapsible ?? options.sidebarCollapsible,
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, options);
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, (items) => normalizeSidebar(items, params));
84
+ return mapValues(sidebars, normalizeSidebar);
113
85
  }