export function createSidebarsUtils()

in packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts [166:350]


export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
  const sidebarNameToDocIds = collectSidebarsDocIds(sidebars);
  const sidebarNameToNavigationItems = collectSidebarsNavigations(sidebars);

  // Reverse mapping
  const docIdToSidebarName = Object.fromEntries(
    Object.entries(sidebarNameToDocIds).flatMap(([sidebarName, docIds]) =>
      docIds.map((docId) => [docId, sidebarName]),
    ),
  );

  function getFirstDocIdOfFirstSidebar(): string | undefined {
    return Object.values(sidebarNameToDocIds)[0]?.[0];
  }

  function getSidebarNameByDocId(docId: string): string | undefined {
    return docIdToSidebarName[docId];
  }

  function emptySidebarNavigation(): SidebarNavigation {
    return {
      sidebarName: undefined,
      previous: undefined,
      next: undefined,
    };
  }

  function getDocNavigation(
    unversionedId: string,
    versionedId: string,
    displayedSidebar: string | null | undefined,
  ): SidebarNavigation {
    // TODO legacy id retro-compatibility!
    let docId = unversionedId;
    let sidebarName =
      displayedSidebar === undefined
        ? getSidebarNameByDocId(docId)
        : displayedSidebar;
    if (sidebarName === undefined) {
      docId = versionedId;
      sidebarName = getSidebarNameByDocId(docId);
    }

    if (!sidebarName) {
      return emptySidebarNavigation();
    }
    const navigationItems = sidebarNameToNavigationItems[sidebarName];
    if (!navigationItems) {
      throw new Error(
        `Doc with ID ${docId} wants to display sidebar ${sidebarName} but a sidebar with this name doesn't exist`,
      );
    }
    const currentItemIndex = navigationItems.findIndex((item) => {
      if (item.type === 'doc') {
        return item.id === docId;
      }
      if (item.type === 'category' && item.link.type === 'doc') {
        return item.link.id === docId;
      }
      return false;
    });
    if (currentItemIndex === -1) {
      return {sidebarName, next: undefined, previous: undefined};
    }

    return {
      sidebarName,
      previous: navigationItems[currentItemIndex - 1],
      next: navigationItems[currentItemIndex + 1],
    };
  }

  function getCategoryGeneratedIndexList(): SidebarItemCategoryWithGeneratedIndex[] {
    return Object.values(sidebarNameToNavigationItems)
      .flat()
      .flatMap((item) => {
        if (item.type === 'category' && item.link.type === 'generated-index') {
          return [item as SidebarItemCategoryWithGeneratedIndex];
        }
        return [];
      });
  }

  /**
   * We identity the category generated index by its permalink (should be
   * unique). More reliable than using object identity
   */
  function getCategoryGeneratedIndexNavigation(
    categoryGeneratedIndexPermalink: string,
  ): SidebarNavigation {
    function isCurrentCategoryGeneratedIndexItem(
      item: SidebarNavigationItem,
    ): boolean {
      return (
        item.type === 'category' &&
        item.link?.type === 'generated-index' &&
        item.link.permalink === categoryGeneratedIndexPermalink
      );
    }

    const sidebarName = Object.entries(sidebarNameToNavigationItems).find(
      ([, navigationItems]) =>
        navigationItems.find(isCurrentCategoryGeneratedIndexItem),
    )![0];
    const navigationItems = sidebarNameToNavigationItems[sidebarName]!;
    const currentItemIndex = navigationItems.findIndex(
      isCurrentCategoryGeneratedIndexItem,
    );
    return {
      sidebarName,
      previous: navigationItems[currentItemIndex - 1],
      next: navigationItems[currentItemIndex + 1],
    };
  }

  function checkSidebarsDocIds(validDocIds: string[], sidebarFilePath: string) {
    const allSidebarDocIds = Object.values(sidebarNameToDocIds).flat();
    const invalidSidebarDocIds = _.difference(allSidebarDocIds, validDocIds);
    if (invalidSidebarDocIds.length > 0) {
      throw new Error(
        `Invalid sidebar file at "${toMessageRelativeFilePath(
          sidebarFilePath,
        )}".
These sidebar document ids do not exist:
- ${invalidSidebarDocIds.sort().join('\n- ')}

Available document ids are:
- ${_.uniq(validDocIds).sort().join('\n- ')}`,
      );
    }
  }

  function getFirstLink(sidebar: Sidebar):
    | {
        type: 'doc';
        id: string;
        label: string;
      }
    | {
        type: 'generated-index';
        permalink: string;
        label: string;
      }
    | undefined {
    for (const item of sidebar) {
      if (item.type === 'doc') {
        return {
          type: 'doc',
          id: item.id,
          label: item.label ?? item.id,
        };
      } else if (item.type === 'category') {
        if (item.link?.type === 'doc') {
          return {
            type: 'doc',
            id: item.link.id,
            label: item.label,
          };
        } else if (item.link?.type === 'generated-index') {
          return {
            type: 'generated-index',
            permalink: item.link.permalink,
            label: item.label,
          };
        }
        const firstSubItem = getFirstLink(item.items);
        if (firstSubItem) {
          return firstSubItem;
        }
      }
    }
    return undefined;
  }

  return {
    sidebars,
    getFirstDocIdOfFirstSidebar,
    getSidebarNameByDocId,
    getDocNavigation,
    getCategoryGeneratedIndexList,
    getCategoryGeneratedIndexNavigation,
    checkSidebarsDocIds,
    getFirstLink: (id) => getFirstLink(sidebars[id]!),
  };
}