mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-06 05:37:16 +02:00
feat(content-docs): autogenerate category with linked doc metadata as fallback (#6859)
This commit is contained in:
parent
f1bcdbff63
commit
b5ceead3b2
8 changed files with 62 additions and 23 deletions
|
@ -1407,6 +1407,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 0,
|
"sidebarPosition": 0,
|
||||||
"source": "@site/docs/3-API/01_Core APIs/0 --- Client API.md",
|
"source": "@site/docs/3-API/01_Core APIs/0 --- Client API.md",
|
||||||
"sourceDirName": "3-API/01_Core APIs",
|
"sourceDirName": "3-API/01_Core APIs",
|
||||||
|
"title": "Client API",
|
||||||
"unversionedId": "API/Core APIs/Client API",
|
"unversionedId": "API/Core APIs/Client API",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1415,6 +1416,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 1,
|
"sidebarPosition": 1,
|
||||||
"source": "@site/docs/3-API/01_Core APIs/1 --- Server API.md",
|
"source": "@site/docs/3-API/01_Core APIs/1 --- Server API.md",
|
||||||
"sourceDirName": "3-API/01_Core APIs",
|
"sourceDirName": "3-API/01_Core APIs",
|
||||||
|
"title": "Server API",
|
||||||
"unversionedId": "API/Core APIs/Server API",
|
"unversionedId": "API/Core APIs/Server API",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1423,6 +1425,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 0,
|
"sidebarPosition": 0,
|
||||||
"source": "@site/docs/3-API/02_Extension APIs/0. Plugin API.md",
|
"source": "@site/docs/3-API/02_Extension APIs/0. Plugin API.md",
|
||||||
"sourceDirName": "3-API/02_Extension APIs",
|
"sourceDirName": "3-API/02_Extension APIs",
|
||||||
|
"title": "Plugin API",
|
||||||
"unversionedId": "API/Extension APIs/Plugin API",
|
"unversionedId": "API/Extension APIs/Plugin API",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1431,6 +1434,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 1,
|
"sidebarPosition": 1,
|
||||||
"source": "@site/docs/3-API/02_Extension APIs/1. Theme API.md",
|
"source": "@site/docs/3-API/02_Extension APIs/1. Theme API.md",
|
||||||
"sourceDirName": "3-API/02_Extension APIs",
|
"sourceDirName": "3-API/02_Extension APIs",
|
||||||
|
"title": "Theme API",
|
||||||
"unversionedId": "API/Extension APIs/Theme API",
|
"unversionedId": "API/Extension APIs/Theme API",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1439,6 +1443,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 3,
|
"sidebarPosition": 3,
|
||||||
"source": "@site/docs/3-API/03_api-end.md",
|
"source": "@site/docs/3-API/03_api-end.md",
|
||||||
"sourceDirName": "3-API",
|
"sourceDirName": "3-API",
|
||||||
|
"title": "API End",
|
||||||
"unversionedId": "API/api-end",
|
"unversionedId": "API/api-end",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1447,6 +1452,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 0,
|
"sidebarPosition": 0,
|
||||||
"source": "@site/docs/3-API/00_api-overview.md",
|
"source": "@site/docs/3-API/00_api-overview.md",
|
||||||
"sourceDirName": "3-API",
|
"sourceDirName": "3-API",
|
||||||
|
"title": "API Overview",
|
||||||
"unversionedId": "API/api-overview",
|
"unversionedId": "API/api-overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1458,6 +1464,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 1,
|
"sidebarPosition": 1,
|
||||||
"source": "@site/docs/Guides/z-guide1.md",
|
"source": "@site/docs/Guides/z-guide1.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 1",
|
||||||
"unversionedId": "Guides/guide1",
|
"unversionedId": "Guides/guide1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1468,6 +1475,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 2,
|
"sidebarPosition": 2,
|
||||||
"source": "@site/docs/Guides/02-guide2.md",
|
"source": "@site/docs/Guides/02-guide2.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 2",
|
||||||
"unversionedId": "Guides/guide2",
|
"unversionedId": "Guides/guide2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1479,6 +1487,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 2.5,
|
"sidebarPosition": 2.5,
|
||||||
"source": "@site/docs/Guides/0-guide2.5.md",
|
"source": "@site/docs/Guides/0-guide2.5.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 2.5",
|
||||||
"unversionedId": "Guides/guide2.5",
|
"unversionedId": "Guides/guide2.5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1490,6 +1499,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 3,
|
"sidebarPosition": 3,
|
||||||
"source": "@site/docs/Guides/guide3.md",
|
"source": "@site/docs/Guides/guide3.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 3",
|
||||||
"unversionedId": "Guides/guide3",
|
"unversionedId": "Guides/guide3",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1500,6 +1510,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": undefined,
|
"sidebarPosition": undefined,
|
||||||
"source": "@site/docs/Guides/a-guide4.md",
|
"source": "@site/docs/Guides/a-guide4.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 4",
|
||||||
"unversionedId": "Guides/guide4",
|
"unversionedId": "Guides/guide4",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1510,6 +1521,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": undefined,
|
"sidebarPosition": undefined,
|
||||||
"source": "@site/docs/Guides/b-guide5.md",
|
"source": "@site/docs/Guides/b-guide5.md",
|
||||||
"sourceDirName": "Guides",
|
"sourceDirName": "Guides",
|
||||||
|
"title": "Guide 5",
|
||||||
"unversionedId": "Guides/guide5",
|
"unversionedId": "Guides/guide5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1518,6 +1530,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 0,
|
"sidebarPosition": 0,
|
||||||
"source": "@site/docs/0-getting-started.md",
|
"source": "@site/docs/0-getting-started.md",
|
||||||
"sourceDirName": ".",
|
"sourceDirName": ".",
|
||||||
|
"title": "Getting Started",
|
||||||
"unversionedId": "getting-started",
|
"unversionedId": "getting-started",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1526,6 +1539,7 @@ exports[`site with custom sidebar items generator sidebarItemsGenerator is calle
|
||||||
"sidebarPosition": 1,
|
"sidebarPosition": 1,
|
||||||
"source": "@site/docs/1-installation.md",
|
"source": "@site/docs/1-installation.md",
|
||||||
"sourceDirName": ".",
|
"sourceDirName": ".",
|
||||||
|
"title": "Installation",
|
||||||
"unversionedId": "installation",
|
"unversionedId": "installation",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -116,7 +116,7 @@ exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
|
||||||
"type": "doc",
|
"type": "doc",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"label": "subsubsubfolder3 (_category_.json label)",
|
"label": "Subsubsubfolder category label",
|
||||||
"link": {
|
"link": {
|
||||||
"id": "doc1",
|
"id": "doc1",
|
||||||
"type": "doc",
|
"type": "doc",
|
||||||
|
|
|
@ -234,7 +234,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
'subfolder/subsubfolder/subsubsubfolder3': {
|
'subfolder/subsubfolder/subsubsubfolder3': {
|
||||||
position: 1,
|
position: 1,
|
||||||
label: 'subsubsubfolder3 (_category_.json label)',
|
// This item's label is defined from the index doc instead
|
||||||
link: {
|
link: {
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
id: 'doc1', // This is a "fully-qualified" ID that can't be found locally
|
id: 'doc1', // This is a "fully-qualified" ID that can't be found locally
|
||||||
|
@ -246,6 +246,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
id: 'doc1',
|
id: 'doc1',
|
||||||
source: 'doc1.md',
|
source: 'doc1.md',
|
||||||
sourceDirName: 'subfolder/subsubfolder',
|
sourceDirName: 'subfolder/subsubfolder',
|
||||||
|
title: 'Subsubsubfolder category label',
|
||||||
sidebarPosition: undefined,
|
sidebarPosition: undefined,
|
||||||
frontMatter: {},
|
frontMatter: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -158,9 +158,6 @@ Available doc IDs:
|
||||||
): WithPosition<NormalizedSidebarItemCategory> {
|
): WithPosition<NormalizedSidebarItemCategory> {
|
||||||
const categoryMetadata =
|
const categoryMetadata =
|
||||||
categoriesMetadata[posixPath(path.join(autogenDir, fullPath))];
|
categoriesMetadata[posixPath(path.join(autogenDir, fullPath))];
|
||||||
const className = categoryMetadata?.className;
|
|
||||||
const customProps = categoryMetadata?.customProps;
|
|
||||||
const {filename, numberPrefix} = numberPrefixParser(folderName);
|
|
||||||
const allItems = Object.entries(dir).map(([key, content]) =>
|
const allItems = Object.entries(dir).map(([key, content]) =>
|
||||||
dirToItem(content, key, `${fullPath}/${key}`),
|
dirToItem(content, key, `${fullPath}/${key}`),
|
||||||
);
|
);
|
||||||
|
@ -184,41 +181,65 @@ Available doc IDs:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCategoryLinkedDocId(): string | undefined {
|
// In addition to the ID, this function also retrieves metadata of the
|
||||||
const link = categoryMetadata?.link;
|
// linked doc that could be used as fallback values for category metadata
|
||||||
if (link !== undefined) {
|
function getCategoryLinkedDocMetadata():
|
||||||
if (link && link.type === 'doc') {
|
| {
|
||||||
return findDocByLocalId(link.id)?.id || getDoc(link.id).id;
|
id: string;
|
||||||
|
position?: number;
|
||||||
|
label?: string;
|
||||||
|
customProps?: {[key: string]: unknown};
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
| undefined {
|
||||||
|
const link = categoryMetadata?.link;
|
||||||
|
if (link !== undefined && link?.type !== 'doc') {
|
||||||
// If a link is explicitly specified, we won't apply conventions
|
// If a link is explicitly specified, we won't apply conventions
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
// Apply default convention to pick index.md, README.md or
|
const id = link
|
||||||
// <categoryName>.md as the category doc
|
? findDocByLocalId(link.id)?.id ?? getDoc(link.id).id
|
||||||
return findConventionalCategoryDocLink()?.id;
|
: findConventionalCategoryDocLink()?.id;
|
||||||
|
if (!id) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const doc = getDoc(id);
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
position: doc.sidebarPosition,
|
||||||
|
label: doc.frontMatter.sidebar_label ?? doc.title,
|
||||||
|
customProps: doc.frontMatter.sidebar_custom_props,
|
||||||
|
className: doc.frontMatter.sidebar_class_name,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
const categoryLinkedDoc = getCategoryLinkedDocMetadata();
|
||||||
const categoryLinkedDocId = getCategoryLinkedDocId();
|
|
||||||
|
|
||||||
const link: SidebarItemCategoryLinkConfig | null | undefined =
|
const link: SidebarItemCategoryLinkConfig | null | undefined =
|
||||||
categoryLinkedDocId
|
categoryLinkedDoc
|
||||||
? {
|
? {
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
id: categoryLinkedDocId, // We "remap" a potentially "local id" to a "qualified id"
|
id: categoryLinkedDoc.id, // We "remap" a potentially "local id" to a "qualified id"
|
||||||
}
|
}
|
||||||
: categoryMetadata?.link;
|
: categoryMetadata?.link;
|
||||||
|
|
||||||
// If a doc is linked, remove it from the category subItems
|
// If a doc is linked, remove it from the category subItems
|
||||||
const items = allItems.filter(
|
const items = allItems.filter(
|
||||||
(item) => !(item.type === 'doc' && item.id === categoryLinkedDocId),
|
(item) => !(item.type === 'doc' && item.id === categoryLinkedDoc?.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const className =
|
||||||
|
categoryMetadata?.className ?? categoryLinkedDoc?.className;
|
||||||
|
const customProps =
|
||||||
|
categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
|
||||||
|
const {filename, numberPrefix} = numberPrefixParser(folderName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: categoryMetadata?.label ?? filename,
|
label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
|
||||||
collapsible: categoryMetadata?.collapsible,
|
collapsible: categoryMetadata?.collapsible,
|
||||||
collapsed: categoryMetadata?.collapsed,
|
collapsed: categoryMetadata?.collapsed,
|
||||||
position: categoryMetadata?.position ?? numberPrefix,
|
position:
|
||||||
|
categoryMetadata?.position ??
|
||||||
|
categoryLinkedDoc?.position ??
|
||||||
|
numberPrefix,
|
||||||
source: folderName,
|
source: folderName,
|
||||||
...(customProps !== undefined && {customProps}),
|
...(customProps !== undefined && {customProps}),
|
||||||
...(className !== undefined && {className}),
|
...(className !== undefined && {className}),
|
||||||
|
|
|
@ -31,6 +31,7 @@ function toSidebarItemsGeneratorDoc(
|
||||||
return _.pick(doc, [
|
return _.pick(doc, [
|
||||||
'id',
|
'id',
|
||||||
'unversionedId',
|
'unversionedId',
|
||||||
|
'title',
|
||||||
'frontMatter',
|
'frontMatter',
|
||||||
'source',
|
'source',
|
||||||
'sourceDirName',
|
'sourceDirName',
|
||||||
|
|
|
@ -231,6 +231,7 @@ export type SidebarItemsGeneratorDoc = Pick<
|
||||||
DocMetadataBase,
|
DocMetadataBase,
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'unversionedId'
|
| 'unversionedId'
|
||||||
|
| 'title'
|
||||||
| 'frontMatter'
|
| 'frontMatter'
|
||||||
| 'source'
|
| 'source'
|
||||||
| 'sourceDirName'
|
| 'sourceDirName'
|
||||||
|
|
|
@ -89,6 +89,7 @@ type SidebarGenerator = (generatorArgs: {
|
||||||
version: {contentPath: string; versionName: string}; // the current version
|
version: {contentPath: string; versionName: string}; // the current version
|
||||||
docs: Array<{
|
docs: Array<{
|
||||||
id: string;
|
id: string;
|
||||||
|
title: string;
|
||||||
frontMatter: DocFrontMatter & Record<string, unknown>;
|
frontMatter: DocFrontMatter & Record<string, unknown>;
|
||||||
source: string;
|
source: string;
|
||||||
sourceDirName: string;
|
sourceDirName: string;
|
||||||
|
|
|
@ -323,7 +323,7 @@ sidebar_class_name: green
|
||||||
This is the easy tutorial!
|
This is the easy tutorial!
|
||||||
```
|
```
|
||||||
|
|
||||||
**For categories**: add a `_category_.json` or `_category_.yml` file in the respective folder. You can specify any category metadata and also the `position` metadata.
|
**For categories**: add a `_category_.json` or `_category_.yml` file in the respective folder. You can specify any category metadata and also the `position` metadata. `label`, `className`, `position`, and `customProps` will default to the respective values of the category's linked doc, if there is one.
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem value="JSON">
|
<TabItem value="JSON">
|
||||||
|
|
Loading…
Add table
Reference in a new issue