@docusaurus/plugin-content-docs 0.0.0-4540 → 0.0.0-4546

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/lastUpdate.js CHANGED
@@ -10,6 +10,7 @@ exports.getFileLastUpdate = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const shelljs_1 = (0, tslib_1.__importDefault)(require("shelljs"));
12
12
  const logger_1 = (0, tslib_1.__importDefault)(require("@docusaurus/logger"));
13
+ const path_1 = (0, tslib_1.__importDefault)(require("path"));
13
14
  const GIT_COMMIT_TIMESTAMP_AUTHOR_REGEX = /^(?<timestamp>\d+),(?<author>.+)$/;
14
15
  let showedGitRequirementError = false;
15
16
  async function getFileLastUpdate(filePath) {
@@ -36,7 +37,13 @@ async function getFileLastUpdate(filePath) {
36
37
  }
37
38
  return null;
38
39
  }
39
- const result = shelljs_1.default.exec(`git log -1 --format=%ct,%an "${filePath}"`, {
40
+ if (!shelljs_1.default.test('-f', filePath)) {
41
+ throw new Error(`Retrieval of git history failed at "${filePath}" because the file does not exist.`);
42
+ }
43
+ const fileBasename = path_1.default.basename(filePath);
44
+ const fileDirname = path_1.default.dirname(filePath);
45
+ const result = shelljs_1.default.exec(`git log --max-count=1 --format=%ct,%an -- "${fileBasename}"`, {
46
+ cwd: fileDirname,
40
47
  silent: true,
41
48
  });
42
49
  if (result.code !== 0) {
@@ -4,15 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import type { SidebarItemsGenerator, SidebarItemCategoryLinkConfig } from './types';
7
+ import type { SidebarItemsGenerator } from './types';
8
8
  export declare const CategoryMetadataFilenameBase = "_category_";
9
9
  export declare const CategoryMetadataFilenamePattern = "_category_.{json,yml,yaml}";
10
- export declare type CategoryMetadataFile = {
11
- label?: string;
12
- position?: number;
13
- collapsed?: boolean;
14
- collapsible?: boolean;
15
- className?: string;
16
- link?: SidebarItemCategoryLinkConfig | null;
17
- };
18
10
  export declare const DefaultSidebarItemsGenerator: SidebarItemsGenerator;
@@ -12,9 +12,6 @@ const lodash_1 = require("lodash");
12
12
  const utils_1 = require("@docusaurus/utils");
13
13
  const logger_1 = (0, tslib_1.__importDefault)(require("@docusaurus/logger"));
14
14
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
15
- const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
16
- const js_yaml_1 = (0, tslib_1.__importDefault)(require("js-yaml"));
17
- const validation_1 = require("./validation");
18
15
  const docs_1 = require("../docs");
19
16
  const BreadcrumbSeparator = '/';
20
17
  // To avoid possible name clashes with a folder of the same name as the ID
@@ -25,30 +22,8 @@ function getLocalDocId(docId) {
25
22
  }
26
23
  exports.CategoryMetadataFilenameBase = '_category_';
27
24
  exports.CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
28
- // TODO I now believe we should read all the category metadata files ahead of
29
- // time: we may need this metadata to customize docs metadata
30
- // Example use-case being able to disable number prefix parsing at the folder
31
- // level, or customize the default base slug for an intermediate directory
32
- // TODO later if there is `CategoryFolder/with-category-name-doc.md`, we may
33
- // want to read the metadata as yaml on it
34
- // see https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
35
- async function readCategoryMetadataFile(categoryDirPath) {
36
- async function tryReadFile(filePath) {
37
- const contentString = await fs_extra_1.default.readFile(filePath, { encoding: 'utf8' });
38
- const unsafeContent = js_yaml_1.default.load(contentString);
39
- try {
40
- return (0, validation_1.validateCategoryMetadataFile)(unsafeContent);
41
- }
42
- catch (e) {
43
- logger_1.default.error `The docs sidebar category metadata file path=${filePath} looks invalid!`;
44
- throw e;
45
- }
46
- }
47
- const filePath = await (0, utils_1.findAsyncSequential)(['.json', '.yml', '.yaml'].map((ext) => (0, utils_1.posixPath)(path_1.default.join(categoryDirPath, `${exports.CategoryMetadataFilenameBase}${ext}`))), fs_extra_1.default.pathExists);
48
- return filePath ? tryReadFile(filePath) : null;
49
- }
50
25
  // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
51
- const DefaultSidebarItemsGenerator = async ({ numberPrefixParser, isCategoryIndex, docs: allDocs, options, item: { dirName: autogenDir }, version, }) => {
26
+ const DefaultSidebarItemsGenerator = async ({ numberPrefixParser, isCategoryIndex, docs: allDocs, options, item: { dirName: autogenDir }, categoriesMetadata, }) => {
52
27
  const docsById = (0, docs_1.createDocsByIdIndex)(allDocs);
53
28
  const findDoc = (docId) => docsById[docId];
54
29
  const getDoc = (docId) => {
@@ -127,8 +102,7 @@ const DefaultSidebarItemsGenerator = async ({ numberPrefixParser, isCategoryInde
127
102
  }
128
103
  async function createCategoryItem(dir, fullPath, folderName) {
129
104
  var _a, _b, _c, _d;
130
- const categoryPath = path_1.default.join(version.contentPath, autogenDir, fullPath);
131
- const categoryMetadata = await readCategoryMetadataFile(categoryPath);
105
+ const categoryMetadata = categoriesMetadata[(0, utils_1.posixPath)(path_1.default.join(autogenDir, fullPath))];
132
106
  const className = categoryMetadata === null || categoryMetadata === void 0 ? void 0 : categoryMetadata.className;
133
107
  const { filename, numberPrefix } = numberPrefixParser(folderName);
134
108
  const allItems = await Promise.all(Object.entries(dir).map(([key, content]) => dirToItem(content, key, `${fullPath}/${key}`)));
@@ -13,4 +13,4 @@ export declare const DisabledSidebars: SidebarsConfig;
13
13
  export declare function resolveSidebarPathOption(siteDir: string, sidebarPathOption: PluginOptions['sidebarPath']): PluginOptions['sidebarPath'];
14
14
  export declare function loadSidebarsFile(sidebarFilePath: string | false | undefined): Promise<SidebarsConfig>;
15
15
  export declare function loadNormalizedSidebars(sidebarFilePath: string | false | undefined, params: NormalizeSidebarsParams): Promise<NormalizedSidebars>;
16
- export declare function loadSidebars(sidebarFilePath: string | false | undefined, options: SidebarProcessorParams): Promise<Sidebars>;
16
+ export declare function loadSidebars(sidebarFilePath: string | false | undefined, options: Omit<SidebarProcessorParams, 'categoriesMetadata'>): Promise<Sidebars>;
@@ -15,6 +15,10 @@ const normalization_1 = require("./normalization");
15
15
  const processor_1 = require("./processor");
16
16
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
17
17
  const utils_1 = require("@docusaurus/utils");
18
+ const logger_1 = (0, tslib_1.__importDefault)(require("@docusaurus/logger"));
19
+ const js_yaml_1 = (0, tslib_1.__importDefault)(require("js-yaml"));
20
+ const lodash_1 = require("lodash");
21
+ const combine_promises_1 = (0, tslib_1.__importDefault)(require("combine-promises"));
18
22
  exports.DefaultSidebars = {
19
23
  defaultSidebar: [
20
24
  {
@@ -32,6 +36,26 @@ function resolveSidebarPathOption(siteDir, sidebarPathOption) {
32
36
  : sidebarPathOption;
33
37
  }
34
38
  exports.resolveSidebarPathOption = resolveSidebarPathOption;
39
+ async function readCategoriesMetadata(contentPath) {
40
+ const categoryFiles = await (0, utils_1.Globby)('**/_category_.{json,yml,yaml}', {
41
+ cwd: contentPath,
42
+ });
43
+ const categoryToFile = (0, lodash_1.groupBy)(categoryFiles, path_1.default.dirname);
44
+ return (0, combine_promises_1.default)((0, lodash_1.mapValues)(categoryToFile, async (files, folder) => {
45
+ const [filePath] = files;
46
+ if (files.length > 1) {
47
+ logger_1.default.warn `There are more than one category metadata files for path=${folder}: ${files.join(', ')}. The behavior is undetermined.`;
48
+ }
49
+ const content = await fs_extra_1.default.readFile(path_1.default.join(contentPath, filePath), 'utf-8');
50
+ try {
51
+ return (0, validation_1.validateCategoryMetadataFile)(js_yaml_1.default.load(content));
52
+ }
53
+ catch (e) {
54
+ logger_1.default.error `The docs sidebar category metadata file path=${filePath} looks invalid!`;
55
+ throw e;
56
+ }
57
+ }));
58
+ }
35
59
  async function loadSidebarsFileUnsafe(sidebarFilePath) {
36
60
  // false => no sidebars
37
61
  if (sidebarFilePath === false) {
@@ -68,6 +92,7 @@ async function loadSidebars(sidebarFilePath, options) {
68
92
  categoryLabelSlugger: (0, utils_1.createSlugger)(),
69
93
  };
70
94
  const normalizedSidebars = await loadNormalizedSidebars(sidebarFilePath, normalizeSidebarsParams);
71
- return (0, processor_1.processSidebars)(normalizedSidebars, options);
95
+ const categoriesMetadata = await readCategoriesMetadata(options.version.contentPath);
96
+ return (0, processor_1.processSidebars)(normalizedSidebars, { ...options, categoriesMetadata });
72
97
  }
73
98
  exports.loadSidebars = loadSidebars;
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { DocMetadataBase, VersionMetadata } from '../types';
8
- import type { Sidebars, NormalizedSidebars, SidebarItemsGeneratorOption } from './types';
8
+ import type { Sidebars, NormalizedSidebars, SidebarItemsGeneratorOption, CategoryMetadataFile } from './types';
9
9
  import type { Slugger } from '@docusaurus/utils';
10
10
  import type { NumberPrefixParser, SidebarOptions } from '@docusaurus/plugin-content-docs';
11
11
  export declare type SidebarProcessorParams = {
@@ -15,5 +15,6 @@ export declare type SidebarProcessorParams = {
15
15
  version: VersionMetadata;
16
16
  categoryLabelSlugger: Slugger;
17
17
  sidebarOptions: SidebarOptions;
18
+ categoriesMetadata: Record<string, CategoryMetadataFile>;
18
19
  };
19
20
  export declare function processSidebars(unprocessedSidebars: NormalizedSidebars, params: SidebarProcessorParams): Promise<Sidebars>;
@@ -30,7 +30,7 @@ function toSidebarItemsGeneratorVersion(version) {
30
30
  // Handle the generation of autogenerated sidebar items and other
31
31
  // post-processing checks
32
32
  async function processSidebar(unprocessedSidebar, params) {
33
- const { sidebarItemsGenerator, numberPrefixParser, docs, version, sidebarOptions, } = params;
33
+ const { sidebarItemsGenerator, numberPrefixParser, docs, version, sidebarOptions, categoriesMetadata, } = params;
34
34
  // Just a minor lazy transformation optimization
35
35
  const getSidebarItemsGeneratorDocsAndVersion = (0, lodash_1.memoize)(() => ({
36
36
  docs: docs.map(toSidebarItemsGeneratorDoc),
@@ -52,6 +52,7 @@ async function processSidebar(unprocessedSidebar, params) {
52
52
  isCategoryIndex: docs_1.isCategoryIndex,
53
53
  ...getSidebarItemsGeneratorDocsAndVersion(),
54
54
  options: sidebarOptions,
55
+ categoriesMetadata,
55
56
  });
56
57
  // TODO validate generated items: user can generate bad items
57
58
  const generatedItemsNormalized = generatedItems.flatMap((generatedItem) => (0, normalization_1.normalizeItem)(generatedItem, { ...params, ...sidebarOptions }));
@@ -120,6 +120,14 @@ export declare type PropVersionDoc = {
120
120
  export declare type PropVersionDocs = {
121
121
  [docId: string]: PropVersionDoc;
122
122
  };
123
+ export declare type CategoryMetadataFile = {
124
+ label?: string;
125
+ position?: number;
126
+ collapsed?: boolean;
127
+ collapsible?: boolean;
128
+ className?: string;
129
+ link?: SidebarItemCategoryLinkConfig | null;
130
+ };
123
131
  export declare type SidebarItemsGeneratorDoc = Pick<DocMetadataBase, 'id' | 'unversionedId' | 'frontMatter' | 'source' | 'sourceDirName' | 'sidebarPosition'>;
124
132
  export declare type SidebarItemsGeneratorVersion = Pick<VersionMetadata, 'versionName' | 'contentPath'>;
125
133
  export declare type SidebarItemsGeneratorArgs = {
@@ -128,6 +136,7 @@ export declare type SidebarItemsGeneratorArgs = {
128
136
  docs: SidebarItemsGeneratorDoc[];
129
137
  numberPrefixParser: NumberPrefixParser;
130
138
  isCategoryIndex: CategoryIndexMatcher;
139
+ categoriesMetadata: Record<string, CategoryMetadataFile>;
131
140
  options: SidebarOptions;
132
141
  };
133
142
  export declare type SidebarItemsGenerator = (generatorArgs: SidebarItemsGeneratorArgs) => Promise<SidebarItem[]>;
@@ -4,7 +4,6 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import type { SidebarsConfig } from './types';
8
- import type { CategoryMetadataFile } from './generator';
7
+ import type { SidebarsConfig, CategoryMetadataFile } from './types';
9
8
  export declare function validateSidebars(sidebars: unknown): asserts sidebars is SidebarsConfig;
10
9
  export declare function validateCategoryMetadataFile(unsafeContent: unknown): CategoryMetadataFile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-docs",
3
- "version": "0.0.0-4540",
3
+ "version": "0.0.0-4546",
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-4540",
27
- "@docusaurus/logger": "0.0.0-4540",
28
- "@docusaurus/mdx-loader": "0.0.0-4540",
29
- "@docusaurus/utils": "0.0.0-4540",
30
- "@docusaurus/utils-validation": "0.0.0-4540",
26
+ "@docusaurus/core": "0.0.0-4546",
27
+ "@docusaurus/logger": "0.0.0-4546",
28
+ "@docusaurus/mdx-loader": "0.0.0-4546",
29
+ "@docusaurus/utils": "0.0.0-4546",
30
+ "@docusaurus/utils-validation": "0.0.0-4546",
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-4540",
44
- "@docusaurus/types": "0.0.0-4540",
43
+ "@docusaurus/module-type-aliases": "0.0.0-4546",
44
+ "@docusaurus/types": "0.0.0-4546",
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": "3bb46cc9766cd9a6e87fee3407d038b45eeee93a"
60
+ "gitHead": "f4dc68b452de796a2afbc90dfa03322c3c579b03"
61
61
  }
package/src/lastUpdate.ts CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import shell from 'shelljs';
9
9
  import logger from '@docusaurus/logger';
10
+ import path from 'path';
10
11
 
11
12
  type FileLastUpdateData = {timestamp?: number; author?: string};
12
13
 
@@ -43,9 +44,21 @@ export async function getFileLastUpdate(
43
44
  return null;
44
45
  }
45
46
 
46
- const result = shell.exec(`git log -1 --format=%ct,%an "${filePath}"`, {
47
- silent: true,
48
- });
47
+ if (!shell.test('-f', filePath)) {
48
+ throw new Error(
49
+ `Retrieval of git history failed at "${filePath}" because the file does not exist.`,
50
+ );
51
+ }
52
+
53
+ const fileBasename = path.basename(filePath);
54
+ const fileDirname = path.dirname(filePath);
55
+ const result = shell.exec(
56
+ `git log --max-count=1 --format=%ct,%an -- "${fileBasename}"`,
57
+ {
58
+ cwd: fileDirname, // this is needed: https://github.com/facebook/docusaurus/pull/5048
59
+ silent: true,
60
+ },
61
+ );
49
62
  if (result.code !== 0) {
50
63
  throw new Error(
51
64
  `Retrieval of git history failed at "${filePath}" with exit code ${result.code}: ${result.stderr}`,
@@ -12,19 +12,11 @@ import type {
12
12
  SidebarItemsGenerator,
13
13
  SidebarItemsGeneratorDoc,
14
14
  SidebarItemCategoryLink,
15
- SidebarItemCategoryLinkConfig,
16
15
  } from './types';
17
16
  import {sortBy, last} from 'lodash';
18
- import {
19
- addTrailingSlash,
20
- posixPath,
21
- findAsyncSequential,
22
- } from '@docusaurus/utils';
17
+ import {addTrailingSlash, posixPath} from '@docusaurus/utils';
23
18
  import logger from '@docusaurus/logger';
24
19
  import path from 'path';
25
- import fs from 'fs-extra';
26
- import Yaml from 'js-yaml';
27
- import {validateCategoryMetadataFile} from './validation';
28
20
  import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs';
29
21
 
30
22
  const BreadcrumbSeparator = '/';
@@ -39,20 +31,6 @@ function getLocalDocId(docId: string): string {
39
31
  export const CategoryMetadataFilenameBase = '_category_';
40
32
  export const CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
41
33
 
42
- export type CategoryMetadataFile = {
43
- label?: string;
44
- position?: number;
45
- collapsed?: boolean;
46
- collapsible?: boolean;
47
- className?: string;
48
- link?: SidebarItemCategoryLinkConfig | null;
49
-
50
- // TODO should we allow "items" here? how would this work? would an
51
- // "autogenerated" type be allowed?
52
- // This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
53
- // cf comment: https://github.com/facebook/docusaurus/issues/3464#issuecomment-784765199
54
- };
55
-
56
34
  type WithPosition<T> = T & {position?: number};
57
35
 
58
36
  /**
@@ -65,37 +43,6 @@ type Dir = {
65
43
  [item: string]: Dir | null;
66
44
  };
67
45
 
68
- // TODO I now believe we should read all the category metadata files ahead of
69
- // time: we may need this metadata to customize docs metadata
70
- // Example use-case being able to disable number prefix parsing at the folder
71
- // level, or customize the default base slug for an intermediate directory
72
- // TODO later if there is `CategoryFolder/with-category-name-doc.md`, we may
73
- // want to read the metadata as yaml on it
74
- // see https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
75
- async function readCategoryMetadataFile(
76
- categoryDirPath: string,
77
- ): Promise<CategoryMetadataFile | null> {
78
- async function tryReadFile(filePath: string): Promise<CategoryMetadataFile> {
79
- const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
80
- const unsafeContent = Yaml.load(contentString);
81
- try {
82
- return validateCategoryMetadataFile(unsafeContent);
83
- } catch (e) {
84
- logger.error`The docs sidebar category metadata file path=${filePath} looks invalid!`;
85
- throw e;
86
- }
87
- }
88
- const filePath = await findAsyncSequential(
89
- ['.json', '.yml', '.yaml'].map((ext) =>
90
- posixPath(
91
- path.join(categoryDirPath, `${CategoryMetadataFilenameBase}${ext}`),
92
- ),
93
- ),
94
- fs.pathExists,
95
- );
96
- return filePath ? tryReadFile(filePath) : null;
97
- }
98
-
99
46
  // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
100
47
  export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
101
48
  numberPrefixParser,
@@ -103,7 +50,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
103
50
  docs: allDocs,
104
51
  options,
105
52
  item: {dirName: autogenDir},
106
- version,
53
+ categoriesMetadata,
107
54
  }) => {
108
55
  const docsById = createDocsByIdIndex(allDocs);
109
56
  const findDoc = (docId: string): SidebarItemsGeneratorDoc | undefined =>
@@ -199,8 +146,8 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
199
146
  fullPath: string,
200
147
  folderName: string,
201
148
  ): Promise<WithPosition<SidebarItemCategory>> {
202
- const categoryPath = path.join(version.contentPath, autogenDir, fullPath);
203
- const categoryMetadata = await readCategoryMetadataFile(categoryPath);
149
+ const categoryMetadata =
150
+ categoriesMetadata[posixPath(path.join(autogenDir, fullPath))];
204
151
  const className = categoryMetadata?.className;
205
152
  const {filename, numberPrefix} = numberPrefixParser(folderName);
206
153
  const allItems = await Promise.all(
@@ -9,12 +9,16 @@ import fs from 'fs-extra';
9
9
  import importFresh from 'import-fresh';
10
10
  import type {SidebarsConfig, Sidebars, NormalizedSidebars} from './types';
11
11
  import type {NormalizeSidebarsParams} from '../types';
12
- import {validateSidebars} from './validation';
12
+ import {validateSidebars, validateCategoryMetadataFile} from './validation';
13
13
  import {normalizeSidebars} from './normalization';
14
14
  import {processSidebars, type SidebarProcessorParams} from './processor';
15
15
  import path from 'path';
16
- import {createSlugger} from '@docusaurus/utils';
16
+ import {createSlugger, 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 {groupBy, mapValues} from 'lodash';
21
+ import combinePromises from 'combine-promises';
18
22
 
19
23
  export const DefaultSidebars: SidebarsConfig = {
20
24
  defaultSidebar: [
@@ -38,6 +42,33 @@ export function resolveSidebarPathOption(
38
42
  : sidebarPathOption;
39
43
  }
40
44
 
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 (e) {
65
+ logger.error`The docs sidebar category metadata file path=${filePath} looks invalid!`;
66
+ throw e;
67
+ }
68
+ }),
69
+ );
70
+ }
71
+
41
72
  async function loadSidebarsFileUnsafe(
42
73
  sidebarFilePath: string | false | undefined,
43
74
  ): Promise<SidebarsConfig> {
@@ -80,7 +111,7 @@ export async function loadNormalizedSidebars(
80
111
  // Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
81
112
  export async function loadSidebars(
82
113
  sidebarFilePath: string | false | undefined,
83
- options: SidebarProcessorParams,
114
+ options: Omit<SidebarProcessorParams, 'categoriesMetadata'>,
84
115
  ): Promise<Sidebars> {
85
116
  const normalizeSidebarsParams: NormalizeSidebarsParams = {
86
117
  ...options.sidebarOptions,
@@ -91,5 +122,8 @@ export async function loadSidebars(
91
122
  sidebarFilePath,
92
123
  normalizeSidebarsParams,
93
124
  );
94
- return processSidebars(normalizedSidebars, options);
125
+ const categoriesMetadata = await readCategoriesMetadata(
126
+ options.version.contentPath,
127
+ );
128
+ return processSidebars(normalizedSidebars, {...options, categoriesMetadata});
95
129
  }
@@ -19,6 +19,7 @@ import type {
19
19
  NormalizedSidebarItemCategory,
20
20
  SidebarItemCategory,
21
21
  SidebarItemAutogenerated,
22
+ CategoryMetadataFile,
22
23
  } from './types';
23
24
  import {transformSidebarItems} from './utils';
24
25
  import {DefaultSidebarItemsGenerator} from './generator';
@@ -39,6 +40,7 @@ export type SidebarProcessorParams = {
39
40
  version: VersionMetadata;
40
41
  categoryLabelSlugger: Slugger;
41
42
  sidebarOptions: SidebarOptions;
43
+ categoriesMetadata: Record<string, CategoryMetadataFile>;
42
44
  };
43
45
 
44
46
  function toSidebarItemsGeneratorDoc(
@@ -72,6 +74,7 @@ async function processSidebar(
72
74
  docs,
73
75
  version,
74
76
  sidebarOptions,
77
+ categoriesMetadata,
75
78
  } = params;
76
79
 
77
80
  // Just a minor lazy transformation optimization
@@ -101,6 +104,7 @@ async function processSidebar(
101
104
  isCategoryIndex,
102
105
  ...getSidebarItemsGeneratorDocsAndVersion(),
103
106
  options: sidebarOptions,
107
+ categoriesMetadata,
104
108
  });
105
109
  // TODO validate generated items: user can generate bad items
106
110
 
@@ -188,6 +188,20 @@ export type PropVersionDocs = {
188
188
  [docId: string]: PropVersionDoc;
189
189
  };
190
190
 
191
+ export type CategoryMetadataFile = {
192
+ label?: string;
193
+ position?: number;
194
+ collapsed?: boolean;
195
+ collapsible?: boolean;
196
+ className?: string;
197
+ link?: SidebarItemCategoryLinkConfig | null;
198
+
199
+ // TODO should we allow "items" here? how would this work? would an
200
+ // "autogenerated" type be allowed?
201
+ // This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
202
+ // cf comment: https://github.com/facebook/docusaurus/issues/3464#issuecomment-784765199
203
+ };
204
+
191
205
  // Reduce API surface for options.sidebarItemsGenerator
192
206
  // The user-provided generator fn should receive only a subset of metadata
193
207
  // A change to any of these metadata can be considered as a breaking change
@@ -211,6 +225,7 @@ export type SidebarItemsGeneratorArgs = {
211
225
  docs: SidebarItemsGeneratorDoc[];
212
226
  numberPrefixParser: NumberPrefixParser;
213
227
  isCategoryIndex: CategoryIndexMatcher;
228
+ categoriesMetadata: Record<string, CategoryMetadataFile>;
214
229
  options: SidebarOptions;
215
230
  };
216
231
  export type SidebarItemsGenerator = (
@@ -19,9 +19,9 @@ import type {
19
19
  SidebarsConfig,
20
20
  SidebarItemCategoryLinkDoc,
21
21
  SidebarItemCategoryLinkGeneratedIndex,
22
+ CategoryMetadataFile,
22
23
  } from './types';
23
24
  import {isCategoriesShorthand} from './utils';
24
- import type {CategoryMetadataFile} from './generator';
25
25
 
26
26
  // NOTE: we don't add any default values during validation on purpose!
27
27
  // Config types are exposed to users for typechecking and we use the same type