mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 08:07:26 +02:00
refactor(content-docs): clean up sidebars logic; validate generator returns (#6596)
* refactor(content-docs): clean up sidebars logic; validate generator returns * remove another TODO * fix types * refactors * refactor...
This commit is contained in:
parent
d6bdf7e804
commit
e3fd3e74ce
23 changed files with 750 additions and 615 deletions
|
@ -7,41 +7,23 @@
|
|||
|
||||
import type {DocMetadataBase, VersionMetadata} from '../types';
|
||||
import type {
|
||||
Sidebars,
|
||||
Sidebar,
|
||||
SidebarItem,
|
||||
NormalizedSidebarItem,
|
||||
NormalizedSidebar,
|
||||
NormalizedSidebars,
|
||||
SidebarItemsGeneratorOption,
|
||||
SidebarItemsGeneratorDoc,
|
||||
SidebarItemsGeneratorVersion,
|
||||
NormalizedSidebarItemCategory,
|
||||
SidebarItemCategory,
|
||||
SidebarItemAutogenerated,
|
||||
ProcessedSidebarItem,
|
||||
ProcessedSidebar,
|
||||
ProcessedSidebars,
|
||||
SidebarProcessorParams,
|
||||
CategoryMetadataFile,
|
||||
} from './types';
|
||||
import {transformSidebarItems} from './utils';
|
||||
import {DefaultSidebarItemsGenerator} from './generator';
|
||||
import {validateSidebars} from './validation';
|
||||
import {mapValues, memoize, pick} from 'lodash';
|
||||
import combinePromises from 'combine-promises';
|
||||
import {normalizeItem} from './normalization';
|
||||
import {isCategoryIndex} from '../docs';
|
||||
import type {Slugger} from '@docusaurus/utils';
|
||||
import type {
|
||||
NumberPrefixParser,
|
||||
SidebarOptions,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
export type SidebarProcessorParams = {
|
||||
sidebarItemsGenerator: SidebarItemsGeneratorOption;
|
||||
numberPrefixParser: NumberPrefixParser;
|
||||
docs: DocMetadataBase[];
|
||||
version: VersionMetadata;
|
||||
categoryLabelSlugger: Slugger;
|
||||
sidebarOptions: SidebarOptions;
|
||||
categoriesMetadata: Record<string, CategoryMetadataFile>;
|
||||
};
|
||||
|
||||
function toSidebarItemsGeneratorDoc(
|
||||
doc: DocMetadataBase,
|
||||
|
@ -66,15 +48,15 @@ function toSidebarItemsGeneratorVersion(
|
|||
// post-processing checks
|
||||
async function processSidebar(
|
||||
unprocessedSidebar: NormalizedSidebar,
|
||||
categoriesMetadata: Record<string, CategoryMetadataFile>,
|
||||
params: SidebarProcessorParams,
|
||||
): Promise<Sidebar> {
|
||||
): Promise<ProcessedSidebar> {
|
||||
const {
|
||||
sidebarItemsGenerator,
|
||||
numberPrefixParser,
|
||||
docs,
|
||||
version,
|
||||
sidebarOptions,
|
||||
categoriesMetadata,
|
||||
} = params;
|
||||
|
||||
// Just a minor lazy transformation optimization
|
||||
|
@ -83,20 +65,9 @@ async function processSidebar(
|
|||
version: toSidebarItemsGeneratorVersion(version),
|
||||
}));
|
||||
|
||||
async function processCategoryItem(
|
||||
item: NormalizedSidebarItemCategory,
|
||||
): Promise<SidebarItemCategory> {
|
||||
return {
|
||||
...item,
|
||||
items: (await Promise.all(item.items.map(processItem))).flat(),
|
||||
};
|
||||
}
|
||||
|
||||
async function processAutoGeneratedItem(
|
||||
item: SidebarItemAutogenerated,
|
||||
): Promise<SidebarItem[]> {
|
||||
// TODO the returned type can't be trusted in practice (generator can be
|
||||
// user-provided)
|
||||
): Promise<ProcessedSidebarItem[]> {
|
||||
const generatedItems = await sidebarItemsGenerator({
|
||||
item,
|
||||
numberPrefixParser,
|
||||
|
@ -106,50 +77,23 @@ async function processSidebar(
|
|||
options: sidebarOptions,
|
||||
categoriesMetadata,
|
||||
});
|
||||
// TODO validate generated items: user can generate bad items
|
||||
|
||||
const generatedItemsNormalized = generatedItems.flatMap((generatedItem) =>
|
||||
normalizeItem(generatedItem, {...params, ...sidebarOptions}),
|
||||
);
|
||||
|
||||
// Process again... weird but sidebar item generated might generate some
|
||||
// auto-generated items?
|
||||
return processItems(generatedItemsNormalized);
|
||||
// TODO repeatedly process & unwrap autogenerated items until there are no
|
||||
// more autogenerated items, or when loop count (e.g. 10) is reached
|
||||
return processItems(generatedItems);
|
||||
}
|
||||
|
||||
async function processItem(
|
||||
item: NormalizedSidebarItem,
|
||||
): Promise<SidebarItem[]> {
|
||||
): Promise<ProcessedSidebarItem[]> {
|
||||
if (item.type === 'category') {
|
||||
// If the current category doesn't have subitems, we render a normal doc link instead.
|
||||
if (item.items.length === 0) {
|
||||
if (!item.link) {
|
||||
throw new Error(
|
||||
`Sidebar category ${item.label} has neither any subitem nor a link. This makes this item not able to link to anything.`,
|
||||
);
|
||||
}
|
||||
switch (item.link.type) {
|
||||
case 'doc':
|
||||
return [
|
||||
{
|
||||
type: 'doc',
|
||||
label: item.label,
|
||||
id: item.link.id,
|
||||
},
|
||||
];
|
||||
case 'generated-index':
|
||||
return [
|
||||
{
|
||||
type: 'link',
|
||||
label: item.label,
|
||||
href: item.link.permalink,
|
||||
},
|
||||
];
|
||||
default:
|
||||
throw new Error('Unexpected sidebar category link type');
|
||||
}
|
||||
}
|
||||
return [await processCategoryItem(item)];
|
||||
return [
|
||||
{
|
||||
...item,
|
||||
items: (await Promise.all(item.items.map(processItem))).flat(),
|
||||
},
|
||||
];
|
||||
}
|
||||
if (item.type === 'autogenerated') {
|
||||
return processAutoGeneratedItem(item);
|
||||
|
@ -159,32 +103,24 @@ async function processSidebar(
|
|||
|
||||
async function processItems(
|
||||
items: NormalizedSidebarItem[],
|
||||
): Promise<SidebarItem[]> {
|
||||
): Promise<ProcessedSidebarItem[]> {
|
||||
return (await Promise.all(items.map(processItem))).flat();
|
||||
}
|
||||
|
||||
const processedSidebar = await processItems(unprocessedSidebar);
|
||||
|
||||
const fixSidebarItemInconsistencies = (item: SidebarItem): SidebarItem => {
|
||||
// A non-collapsible category can't be collapsed!
|
||||
if (item.type === 'category' && !item.collapsible && item.collapsed) {
|
||||
return {
|
||||
...item,
|
||||
collapsed: false,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
};
|
||||
return transformSidebarItems(processedSidebar, fixSidebarItemInconsistencies);
|
||||
return processedSidebar;
|
||||
}
|
||||
|
||||
export async function processSidebars(
|
||||
unprocessedSidebars: NormalizedSidebars,
|
||||
categoriesMetadata: Record<string, CategoryMetadataFile>,
|
||||
params: SidebarProcessorParams,
|
||||
): Promise<Sidebars> {
|
||||
return combinePromises(
|
||||
): Promise<ProcessedSidebars> {
|
||||
const processedSidebars = await combinePromises(
|
||||
mapValues(unprocessedSidebars, (unprocessedSidebar) =>
|
||||
processSidebar(unprocessedSidebar, params),
|
||||
processSidebar(unprocessedSidebar, categoriesMetadata, params),
|
||||
),
|
||||
);
|
||||
validateSidebars(processedSidebars);
|
||||
return processedSidebars;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue