/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import type { NumberPrefixParser, DocMetadataBase, VersionMetadata, SidebarOptions, } from '../types'; import type { Sidebars, Sidebar, SidebarItem, NormalizedSidebarItem, NormalizedSidebar, NormalizedSidebars, SidebarItemsGeneratorOption, SidebarItemsGeneratorDoc, SidebarItemsGeneratorVersion, NormalizedSidebarItemCategory, SidebarItemCategory, SidebarItemAutogenerated, } from './types'; import {transformSidebarItems} from './utils'; import {DefaultSidebarItemsGenerator} from './generator'; import {mapValues, memoize, pick} from 'lodash'; import combinePromises from 'combine-promises'; import {normalizeItem} from './normalization'; import {Slugger} from '@docusaurus/utils'; export type SidebarProcessorParams = { sidebarItemsGenerator: SidebarItemsGeneratorOption; numberPrefixParser: NumberPrefixParser; docs: DocMetadataBase[]; version: VersionMetadata; categoryLabelSlugger: Slugger; sidebarOptions: SidebarOptions; }; function toSidebarItemsGeneratorDoc( doc: DocMetadataBase, ): SidebarItemsGeneratorDoc { return pick(doc, [ 'id', 'unversionedId', 'frontMatter', 'source', 'sourceDirName', 'sidebarPosition', ]); } function toSidebarItemsGeneratorVersion( version: VersionMetadata, ): SidebarItemsGeneratorVersion { return pick(version, ['versionName', 'contentPath']); } // Handle the generation of autogenerated sidebar items and other post-processing checks async function processSidebar( unprocessedSidebar: NormalizedSidebar, params: SidebarProcessorParams, ): Promise { const { sidebarItemsGenerator, numberPrefixParser, docs, version, sidebarOptions, } = params; // Just a minor lazy transformation optimization const getSidebarItemsGeneratorDocsAndVersion = memoize(() => ({ docs: docs.map(toSidebarItemsGeneratorDoc), version: toSidebarItemsGeneratorVersion(version), })); async function processCategoryItem( item: NormalizedSidebarItemCategory, ): Promise { return { ...item, items: (await Promise.all(item.items.map(processItem))).flat(), }; } async function processAutoGeneratedItem( item: SidebarItemAutogenerated, ): Promise { // TODO the returned type can't be trusted in practice (generator can be user-provided) const generatedItems = await sidebarItemsGenerator({ item, numberPrefixParser, defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator, ...getSidebarItemsGeneratorDocsAndVersion(), options: sidebarOptions, }); // 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); } async function processItem( item: NormalizedSidebarItem, ): Promise { if (item.type === 'category') { return [await processCategoryItem(item)]; } if (item.type === 'autogenerated') { return processAutoGeneratedItem(item); } return [item]; } async function processItems( items: NormalizedSidebarItem[], ): Promise { 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); } export async function processSidebars( unprocessedSidebars: NormalizedSidebars, params: SidebarProcessorParams, ): Promise { return combinePromises( mapValues(unprocessedSidebars, (unprocessedSidebar) => processSidebar(unprocessedSidebar, params), ), ); }