@docusaurus/plugin-content-docs 0.0.0-4539 → 0.0.0-4545
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 +8 -1
- package/lib/sidebars/generator.d.ts +1 -9
- package/lib/sidebars/generator.js +2 -28
- package/lib/sidebars/index.d.ts +1 -1
- package/lib/sidebars/index.js +26 -1
- package/lib/sidebars/processor.d.ts +2 -1
- package/lib/sidebars/processor.js +2 -1
- package/lib/sidebars/types.d.ts +19 -4
- package/lib/sidebars/validation.d.ts +1 -2
- package/lib/sidebars/validation.js +6 -0
- package/package.json +9 -9
- package/src/lastUpdate.ts +16 -3
- package/src/sidebars/generator.ts +4 -57
- package/src/sidebars/index.ts +38 -4
- package/src/sidebars/processor.ts +4 -0
- package/src/sidebars/types.ts +30 -1
- package/src/sidebars/validation.ts +9 -1
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
|
-
|
|
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
|
|
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 },
|
|
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
|
|
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}`)));
|
package/lib/sidebars/index.d.ts
CHANGED
|
@@ -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>;
|
package/lib/sidebars/index.js
CHANGED
|
@@ -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
|
-
|
|
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 }));
|
package/lib/sidebars/types.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export declare type SidebarItemDoc = SidebarItemBase & {
|
|
|
19
19
|
label?: string;
|
|
20
20
|
id: string;
|
|
21
21
|
};
|
|
22
|
+
export declare type SidebarItemHtml = SidebarItemBase & {
|
|
23
|
+
type: 'html';
|
|
24
|
+
value: string;
|
|
25
|
+
defaultStyle?: boolean;
|
|
26
|
+
};
|
|
22
27
|
export declare type SidebarItemLink = SidebarItemBase & {
|
|
23
28
|
type: 'link';
|
|
24
29
|
href: string;
|
|
@@ -64,7 +69,7 @@ export declare type SidebarItemCategoryConfig = Expand<Optional<SidebarItemCateg
|
|
|
64
69
|
export declare type SidebarCategoriesShorthand = {
|
|
65
70
|
[sidebarCategory: string]: SidebarItemConfig[];
|
|
66
71
|
};
|
|
67
|
-
export declare type SidebarItemConfig = SidebarItemDoc | SidebarItemLink | SidebarItemAutogenerated | SidebarItemCategoryConfig | string | SidebarCategoriesShorthand;
|
|
72
|
+
export declare type SidebarItemConfig = SidebarItemDoc | SidebarItemHtml | SidebarItemLink | SidebarItemAutogenerated | SidebarItemCategoryConfig | string | SidebarCategoriesShorthand;
|
|
68
73
|
export declare type SidebarConfig = SidebarCategoriesShorthand | SidebarItemConfig[];
|
|
69
74
|
export declare type SidebarsConfig = {
|
|
70
75
|
[sidebarId: string]: SidebarConfig;
|
|
@@ -73,7 +78,7 @@ export declare type NormalizedSidebarItemCategory = Expand<SidebarItemCategoryBa
|
|
|
73
78
|
items: NormalizedSidebarItem[];
|
|
74
79
|
link?: SidebarItemCategoryLink;
|
|
75
80
|
}>;
|
|
76
|
-
export declare type NormalizedSidebarItem = SidebarItemDoc | SidebarItemLink | NormalizedSidebarItemCategory | SidebarItemAutogenerated;
|
|
81
|
+
export declare type NormalizedSidebarItem = SidebarItemDoc | SidebarItemHtml | SidebarItemLink | NormalizedSidebarItemCategory | SidebarItemAutogenerated;
|
|
77
82
|
export declare type NormalizedSidebar = NormalizedSidebarItem[];
|
|
78
83
|
export declare type NormalizedSidebars = {
|
|
79
84
|
[sidebarId: string]: NormalizedSidebar;
|
|
@@ -86,7 +91,7 @@ export declare type SidebarItemCategoryWithLink = Required<SidebarItemCategory,
|
|
|
86
91
|
export declare type SidebarItemCategoryWithGeneratedIndex = SidebarItemCategoryWithLink & {
|
|
87
92
|
link: SidebarItemCategoryLinkGeneratedIndex;
|
|
88
93
|
};
|
|
89
|
-
export declare type SidebarItem = SidebarItemDoc | SidebarItemLink | SidebarItemCategory;
|
|
94
|
+
export declare type SidebarItem = SidebarItemDoc | SidebarItemHtml | SidebarItemLink | SidebarItemCategory;
|
|
90
95
|
export declare type SidebarNavigationItem = SidebarItemDoc | SidebarItemCategoryWithLink;
|
|
91
96
|
export declare type Sidebar = SidebarItem[];
|
|
92
97
|
export declare type SidebarItemType = SidebarItem['type'];
|
|
@@ -100,7 +105,8 @@ export declare type PropSidebarItemCategory = Expand<SidebarItemCategoryBase & {
|
|
|
100
105
|
export declare type PropSidebarItemLink = SidebarItemLink & {
|
|
101
106
|
docId?: string;
|
|
102
107
|
};
|
|
103
|
-
export declare type
|
|
108
|
+
export declare type PropSidebarItemHtml = SidebarItemHtml;
|
|
109
|
+
export declare type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory | PropSidebarItemHtml;
|
|
104
110
|
export declare type PropSidebar = PropSidebarItem[];
|
|
105
111
|
export declare type PropSidebars = {
|
|
106
112
|
[sidebarId: string]: PropSidebar;
|
|
@@ -114,6 +120,14 @@ export declare type PropVersionDoc = {
|
|
|
114
120
|
export declare type PropVersionDocs = {
|
|
115
121
|
[docId: string]: PropVersionDoc;
|
|
116
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
|
+
};
|
|
117
131
|
export declare type SidebarItemsGeneratorDoc = Pick<DocMetadataBase, 'id' | 'unversionedId' | 'frontMatter' | 'source' | 'sourceDirName' | 'sidebarPosition'>;
|
|
118
132
|
export declare type SidebarItemsGeneratorVersion = Pick<VersionMetadata, 'versionName' | 'contentPath'>;
|
|
119
133
|
export declare type SidebarItemsGeneratorArgs = {
|
|
@@ -122,6 +136,7 @@ export declare type SidebarItemsGeneratorArgs = {
|
|
|
122
136
|
docs: SidebarItemsGeneratorDoc[];
|
|
123
137
|
numberPrefixParser: NumberPrefixParser;
|
|
124
138
|
isCategoryIndex: CategoryIndexMatcher;
|
|
139
|
+
categoriesMetadata: Record<string, CategoryMetadataFile>;
|
|
125
140
|
options: SidebarOptions;
|
|
126
141
|
};
|
|
127
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;
|
|
@@ -28,6 +28,11 @@ const sidebarItemDocSchema = sidebarItemBaseSchema.append({
|
|
|
28
28
|
id: utils_validation_1.Joi.string().required(),
|
|
29
29
|
label: utils_validation_1.Joi.string(),
|
|
30
30
|
});
|
|
31
|
+
const sidebarItemHtmlSchema = sidebarItemBaseSchema.append({
|
|
32
|
+
type: 'html',
|
|
33
|
+
value: utils_validation_1.Joi.string().required(),
|
|
34
|
+
defaultStyle: utils_validation_1.Joi.boolean().default(false),
|
|
35
|
+
});
|
|
31
36
|
const sidebarItemLinkSchema = sidebarItemBaseSchema.append({
|
|
32
37
|
type: 'link',
|
|
33
38
|
href: utils_validation_1.URISchema.required(),
|
|
@@ -93,6 +98,7 @@ const sidebarItemSchema = utils_validation_1.Joi.object()
|
|
|
93
98
|
is: utils_validation_1.Joi.string().valid('doc', 'ref').required(),
|
|
94
99
|
then: sidebarItemDocSchema,
|
|
95
100
|
},
|
|
101
|
+
{ is: 'html', then: sidebarItemHtmlSchema },
|
|
96
102
|
{ is: 'autogenerated', then: sidebarItemAutogeneratedSchema },
|
|
97
103
|
{ is: 'category', then: sidebarItemCategorySchema },
|
|
98
104
|
{
|
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-4545",
|
|
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-4545",
|
|
27
|
+
"@docusaurus/logger": "0.0.0-4545",
|
|
28
|
+
"@docusaurus/mdx-loader": "0.0.0-4545",
|
|
29
|
+
"@docusaurus/utils": "0.0.0-4545",
|
|
30
|
+
"@docusaurus/utils-validation": "0.0.0-4545",
|
|
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-4545",
|
|
44
|
+
"@docusaurus/types": "0.0.0-4545",
|
|
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": "2fd48f443b78b58b3b511f797a0050d407370f07"
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
|
203
|
-
|
|
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(
|
package/src/sidebars/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
package/src/sidebars/types.ts
CHANGED
|
@@ -27,6 +27,12 @@ export type SidebarItemDoc = SidebarItemBase & {
|
|
|
27
27
|
id: string;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
export type SidebarItemHtml = SidebarItemBase & {
|
|
31
|
+
type: 'html';
|
|
32
|
+
value: string;
|
|
33
|
+
defaultStyle?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
30
36
|
export type SidebarItemLink = SidebarItemBase & {
|
|
31
37
|
type: 'link';
|
|
32
38
|
href: string;
|
|
@@ -87,6 +93,7 @@ export type SidebarCategoriesShorthand = {
|
|
|
87
93
|
|
|
88
94
|
export type SidebarItemConfig =
|
|
89
95
|
| SidebarItemDoc
|
|
96
|
+
| SidebarItemHtml
|
|
90
97
|
| SidebarItemLink
|
|
91
98
|
| SidebarItemAutogenerated
|
|
92
99
|
| SidebarItemCategoryConfig
|
|
@@ -108,6 +115,7 @@ export type NormalizedSidebarItemCategory = Expand<
|
|
|
108
115
|
|
|
109
116
|
export type NormalizedSidebarItem =
|
|
110
117
|
| SidebarItemDoc
|
|
118
|
+
| SidebarItemHtml
|
|
111
119
|
| SidebarItemLink
|
|
112
120
|
| NormalizedSidebarItemCategory
|
|
113
121
|
| SidebarItemAutogenerated;
|
|
@@ -131,6 +139,7 @@ export type SidebarItemCategoryWithGeneratedIndex =
|
|
|
131
139
|
|
|
132
140
|
export type SidebarItem =
|
|
133
141
|
| SidebarItemDoc
|
|
142
|
+
| SidebarItemHtml
|
|
134
143
|
| SidebarItemLink
|
|
135
144
|
| SidebarItemCategory;
|
|
136
145
|
|
|
@@ -158,7 +167,12 @@ export type PropSidebarItemLink = SidebarItemLink & {
|
|
|
158
167
|
docId?: string;
|
|
159
168
|
};
|
|
160
169
|
|
|
161
|
-
export type
|
|
170
|
+
export type PropSidebarItemHtml = SidebarItemHtml;
|
|
171
|
+
|
|
172
|
+
export type PropSidebarItem =
|
|
173
|
+
| PropSidebarItemLink
|
|
174
|
+
| PropSidebarItemCategory
|
|
175
|
+
| PropSidebarItemHtml;
|
|
162
176
|
export type PropSidebar = PropSidebarItem[];
|
|
163
177
|
export type PropSidebars = {
|
|
164
178
|
[sidebarId: string]: PropSidebar;
|
|
@@ -174,6 +188,20 @@ export type PropVersionDocs = {
|
|
|
174
188
|
[docId: string]: PropVersionDoc;
|
|
175
189
|
};
|
|
176
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
|
+
|
|
177
205
|
// Reduce API surface for options.sidebarItemsGenerator
|
|
178
206
|
// The user-provided generator fn should receive only a subset of metadata
|
|
179
207
|
// A change to any of these metadata can be considered as a breaking change
|
|
@@ -197,6 +225,7 @@ export type SidebarItemsGeneratorArgs = {
|
|
|
197
225
|
docs: SidebarItemsGeneratorDoc[];
|
|
198
226
|
numberPrefixParser: NumberPrefixParser;
|
|
199
227
|
isCategoryIndex: CategoryIndexMatcher;
|
|
228
|
+
categoriesMetadata: Record<string, CategoryMetadataFile>;
|
|
200
229
|
options: SidebarOptions;
|
|
201
230
|
};
|
|
202
231
|
export type SidebarItemsGenerator = (
|
|
@@ -12,15 +12,16 @@ import type {
|
|
|
12
12
|
SidebarItemBase,
|
|
13
13
|
SidebarItemAutogenerated,
|
|
14
14
|
SidebarItemDoc,
|
|
15
|
+
SidebarItemHtml,
|
|
15
16
|
SidebarItemLink,
|
|
16
17
|
SidebarItemCategoryConfig,
|
|
17
18
|
SidebarItemCategoryLink,
|
|
18
19
|
SidebarsConfig,
|
|
19
20
|
SidebarItemCategoryLinkDoc,
|
|
20
21
|
SidebarItemCategoryLinkGeneratedIndex,
|
|
22
|
+
CategoryMetadataFile,
|
|
21
23
|
} from './types';
|
|
22
24
|
import {isCategoriesShorthand} from './utils';
|
|
23
|
-
import type {CategoryMetadataFile} from './generator';
|
|
24
25
|
|
|
25
26
|
// NOTE: we don't add any default values during validation on purpose!
|
|
26
27
|
// Config types are exposed to users for typechecking and we use the same type
|
|
@@ -48,6 +49,12 @@ const sidebarItemDocSchema = sidebarItemBaseSchema.append<SidebarItemDoc>({
|
|
|
48
49
|
label: Joi.string(),
|
|
49
50
|
});
|
|
50
51
|
|
|
52
|
+
const sidebarItemHtmlSchema = sidebarItemBaseSchema.append<SidebarItemHtml>({
|
|
53
|
+
type: 'html',
|
|
54
|
+
value: Joi.string().required(),
|
|
55
|
+
defaultStyle: Joi.boolean().default(false),
|
|
56
|
+
});
|
|
57
|
+
|
|
51
58
|
const sidebarItemLinkSchema = sidebarItemBaseSchema.append<SidebarItemLink>({
|
|
52
59
|
type: 'link',
|
|
53
60
|
href: URISchema.required(),
|
|
@@ -117,6 +124,7 @@ const sidebarItemSchema: Joi.Schema<SidebarItemConfig> = Joi.object()
|
|
|
117
124
|
is: Joi.string().valid('doc', 'ref').required(),
|
|
118
125
|
then: sidebarItemDocSchema,
|
|
119
126
|
},
|
|
127
|
+
{is: 'html', then: sidebarItemHtmlSchema},
|
|
120
128
|
{is: 'autogenerated', then: sidebarItemAutogeneratedSchema},
|
|
121
129
|
{is: 'category', then: sidebarItemCategorySchema},
|
|
122
130
|
{
|