UNPKG

starlight-sidebar-topics

Version:

Starlight plugin to split your documentation into different sections, each with its own sidebar.

178 lines (133 loc) 5.16 kB
import type { StarlightRouteData } from '@astrojs/starlight/route-data' import type { StarlightSidebarTopicsSharedConfig } from './config' import { isStarlightEntryWithTopic, type StarlightEntry } from './content' import { getLocaleFromSlug, getLocalizedSlug } from './i18n' import { arePathnamesEqual, stripLeadingAndTrailingSlashes } from './pathname' const absoluteLinkRegex = /^https?:\/\// export function getCurrentTopic( config: StarlightSidebarTopicsSharedConfig, sidebar: SidebarEntry[], currentSlug: string, entry: StarlightEntry, ): Topic | undefined { // If the current page has a topic ID, use it to find the topic. const topicId = getTopicIdFromEntry(entry) if (topicId) return getTopicById(config, sidebar, topicId) // Start by checking if the current page is a topic root. const topicFromSlug = getTopicFromSlug(config, sidebar, currentSlug) if (topicFromSlug) return topicFromSlug // Otherwise, find the current topic by looking for the current page in the sidebar. const currentSidebarTopic = getCurrentSidebarTopic(sidebar) if (!currentSidebarTopic) return const currentTopicConfig = config[Number.parseInt(currentSidebarTopic.label, 10)] if (!currentTopicConfig) return return { config: currentTopicConfig, sidebar: currentSidebarTopic.entries } } export function isTopicFirstPage(sidebar: SidebarEntry[], currentSlug: string): boolean { const currentSidebarTopic = getCurrentSidebarTopic(sidebar) if (!currentSidebarTopic) return false const firstPage = getSidebarFirstPage(currentSidebarTopic.entries) if (!firstPage) return false return arePathnamesEqual(stripLeadingAndTrailingSlashes(firstPage.href), currentSlug) } export function isTopicLastPage(sidebar: SidebarEntry[], currentSlug: string): boolean { const currentSidebarTopic = getCurrentSidebarTopic(sidebar) if (!currentSidebarTopic) return false const lastPage = getSidebarLastPage(currentSidebarTopic.entries) if (!lastPage) return false return arePathnamesEqual(stripLeadingAndTrailingSlashes(lastPage.href), currentSlug) } function getSidebarFirstPage(sidebar: SidebarEntry[]) { const entry = sidebar[0] if (!entry) return if (entry.type === 'link') return entry return getSidebarFirstPage(entry.entries) } function getSidebarLastPage(sidebar: SidebarEntry[]) { const entry = sidebar.at(-1) if (!entry) return if (entry.type === 'link') return entry return getSidebarLastPage(entry.entries) } function getTopicFromSlug( config: StarlightSidebarTopicsSharedConfig, sidebar: SidebarEntry[], slug: string, ): Topic | undefined { let topicConfig: Topic['config'] | undefined let topicSidebar: Topic['sidebar'] | undefined // Start by checking if the current page is a topic homepage. let groupTopicIndex = -1 const slugLocale = getLocaleFromSlug(slug) for (const topic of config) { if (topic.type === 'group') groupTopicIndex++ if ( !absoluteLinkRegex.test(topic.link) && arePathnamesEqual(getLocalizedSlug(stripLeadingAndTrailingSlashes(topic.link), slugLocale), slug) && groupTopicIndex !== -1 ) { const sidebarTopic = sidebar[groupTopicIndex] if (sidebarTopic?.type === 'group') { topicConfig = topic topicSidebar = sidebarTopic.entries } break } } if (!topicConfig || !topicSidebar) return return { config: topicConfig, sidebar: topicSidebar } } export function getTopicById( config: StarlightSidebarTopicsSharedConfig, sidebar: SidebarEntry[], id: string, ): Topic | undefined { let topicConfig: Topic['config'] | undefined let topicSidebar: Topic['sidebar'] | undefined let groupTopicIndex = -1 for (const topic of config) { if (topic.type === 'group') groupTopicIndex++ if (topic.type === 'group' && topic.id === id) { const sidebarTopic = sidebar[groupTopicIndex] if (sidebarTopic?.type === 'group') { topicConfig = topic topicSidebar = sidebarTopic.entries } break } } if (!topicConfig || !topicSidebar) return return { config: topicConfig, sidebar: topicSidebar } } function getCurrentSidebarTopic(sidebar: SidebarEntry[]): SidebarTopic | undefined { let currentSidebarTopic: SidebarTopic | undefined for (const topic of sidebar) { if (topic.type === 'link') continue const currentSidebarEntry = getCurrentSidebarEntry(topic.entries) if (currentSidebarEntry) { currentSidebarTopic = topic break } } return currentSidebarTopic } function getCurrentSidebarEntry(sidebar: SidebarEntry[]): SidebarEntry | undefined { return sidebar.find((entry) => { if (entry.type === 'link') { return entry.isCurrent } return getCurrentSidebarEntry(entry.entries) }) } function getTopicIdFromEntry(entry: StarlightEntry): string | undefined { return isStarlightEntryWithTopic(entry) ? entry.data.topic : undefined } type SidebarEntry = StarlightRouteData['sidebar'][number] interface SidebarTopic { label: string entries: SidebarEntry[] } export interface Topic { config: StarlightSidebarTopicsSharedConfig[number] sidebar: SidebarEntry[] }