import type { StarlightUserConfig } from '@astrojs/starlight/types' interface CommonSidebarLinkInput { label?: string attrs?: Record badge?: any translations?: Record icon?: string } export type SidebarLinkInput = CommonSidebarLinkInput & ({ link: string, slug?: never } | { link?: never, slug: string }) export interface SidebarManualGroupInput { label: string collapsed?: boolean badge?: any translations?: Record items: SidebarInput[] } export interface SidebarAutogeneratedGroupInput { label: string collapsed?: boolean badge?: any translations?: Record autogenerate: { directory: string collapsed?: boolean attrs?: Record } } export interface SidebarLabelInput { label: string icon?: string badge?: any translations?: Record attrs?: Record } export type SidebarInput = | string | SidebarLinkInput | SidebarManualGroupInput | SidebarAutogeneratedGroupInput | SidebarLabelInput export type StarlightSidebar = NonNullable /** * Add icons to Starlight sidebar entries by mapping a friendly `icon` property * to `attrs["data-icon"]`, which is consumed by the overridden Sidebar component. * Keeps unknown fields intact and preserves the original structure. * * The function is intentionally generic to preserve the caller's type (e.g. Starlight's * own sidebar item types) and avoid narrowing to the helper's local types. */ export function withSidebarIcons(sidebar: T[]): T[] { return (sidebar as any[]).map((entry) => { if (typeof entry === 'string') return entry as T if (entry && typeof entry === 'object' && 'items' in (entry as any)) { const group: any = { ...(entry as any) } group.items = withSidebarIcons(group.items) return group as T } if (entry && typeof entry === 'object' && 'autogenerate' in (entry as any)) { return entry as T } if (entry && typeof entry === 'object') { const link: any = { ...(entry as any) } const hasLink = 'link' in link || 'slug' in link if (!hasLink) { if (typeof link.label !== 'string') { throw new TypeError('[starlight-plugin-icons] Sidebar item is invalid. Label-only entries require a string `label`.') } // Label-only entries are represented as `link: '#'` // and flagged for custom rendering in the overridden sidebar component. link.link = '#' link.attrs = { ...(link.attrs || {}), 'data-spi-label': 'true' } } const icon = link.icon as string | undefined if (icon) { const attrs = { ...(link.attrs || {}) } attrs['data-icon'] = icon delete link.icon link.attrs = attrs } return link as T } return entry as T }) } export function defineSidebar(sidebar: SidebarInput[]): StarlightSidebar { return withSidebarIcons(sidebar as unknown[]) as StarlightSidebar }