starlight-blog
Version:
Starlight plugin to add a blog to your documentation.
188 lines (156 loc) • 5.43 kB
text/typescript
import { defineRouteMiddleware, type StarlightRouteData } from '@astrojs/starlight/route-data'
import type { APIContext, AstroBuiltinAttributes } from 'astro'
import type { HTMLAttributes } from 'astro/types'
import type { StarlightBlogData } from './data'
import { getAllAuthors } from './libs/authors'
import { getBlogEntries, getBlogEntryMetadata, getSidebarBlogEntries } from './libs/content'
import type { Locale } from './libs/i18n'
import {
getPathWithLocale,
getRelativeBlogUrl,
getRelativeUrl,
getSidebarProps,
isAnyBlogPage,
isBlogAuthorPage,
isBlogRoot,
isBlogTagPage,
} from './libs/page'
import { getAllTags, getEntryTags } from './libs/tags'
import { getBlogTitle } from './libs/title'
const blogDataPerLocale = new Map<Locale, StarlightBlogData>()
export const onRequest = defineRouteMiddleware(async (context) => {
const { starlightRoute } = context.locals
const { id, locale } = starlightRoute
context.locals.starlightBlog = await getBlogData(starlightRoute)
const isBlog = isAnyBlogPage(id)
if (!isBlog) {
starlightRoute.sidebar.unshift(
makeSidebarLink(getBlogTitle(locale), getRelativeBlogUrl('/', locale), false, { class: 'sl-blog-mobile-link' }),
)
return
}
starlightRoute.sidebar = await getBlogSidebar(context)
})
export async function getBlogData({ locale }: StarlightRouteData): Promise<StarlightBlogData> {
if (blogDataPerLocale.has(locale)) {
return blogDataPerLocale.get(locale) as StarlightBlogData
}
const blogData: StarlightBlogData = {
posts: await getBlogPostsData(locale),
}
blogDataPerLocale.set(locale, blogData)
return blogData
}
async function getBlogPostsData(locale: Locale): Promise<StarlightBlogData['posts']> {
const entries = await getBlogEntries(locale)
return entries.map((entry) => {
const { authors } = getBlogEntryMetadata(entry, locale)
const tags = getEntryTags(entry)
const postsData: StarlightBlogData['posts'][number] = {
authors: authors.map(({ name, title, url }) => ({
name,
title,
url,
})),
cover: entry.data.cover,
createdAt: entry.data.date,
draft: entry.data.draft,
entry: entry,
featured: entry.data.featured === true,
href: getRelativeUrl(`/${getPathWithLocale(entry.id, locale)}`),
tags: tags.map(({ label, slug }) => ({
label,
href: getRelativeBlogUrl(`/tags/${slug}`, locale),
})),
title: entry.data.title,
}
if (entry.data.lastUpdated && typeof entry.data.lastUpdated !== 'boolean') {
postsData.updatedAt = entry.data.lastUpdated
}
return postsData
})
}
async function getBlogSidebar(context: APIContext): Promise<StarlightRouteData['sidebar']> {
const { starlightRoute, t } = context.locals
const { id, locale } = starlightRoute
const { featured, recent } = await getSidebarBlogEntries(locale)
const sidebar: StarlightRouteData['sidebar'] = [
makeSidebarLink(t('starlightBlog.sidebar.all'), getRelativeBlogUrl('/', locale), isBlogRoot(id)),
]
if (featured.length > 0) {
sidebar.push(makeSidebarGroup(t('starlightBlog.sidebar.featured'), getSidebarProps(id, featured, locale)))
}
sidebar.push(makeSidebarGroup(t('starlightBlog.sidebar.recent'), getSidebarProps(id, recent, locale)))
const tags = await getAllTags(locale)
if (tags.size > 0) {
sidebar.push(
makeSidebarGroup(
t('starlightBlog.sidebar.tags'),
[...tags]
.sort(([, a], [, b]) => {
if (a.entries.length === b.entries.length) {
return a.label.localeCompare(b.label)
}
return b.entries.length - a.entries.length
})
.map(([tagSlug, { entries, label }]) =>
makeSidebarLink(
`${label} (${entries.length})`,
getRelativeBlogUrl(`/tags/${tagSlug}`, locale),
isBlogTagPage(id, tagSlug),
),
),
),
)
}
const authors = await getAllAuthors(locale)
if (authors.size > 1) {
sidebar.push(
makeSidebarGroup(
t('starlightBlog.sidebar.authors'),
[...authors]
.sort(([, a], [, b]) => {
if (a.entries.length === b.entries.length) {
return a.author.name.localeCompare(b.author.name)
}
return b.entries.length - a.entries.length
})
.map(([, { author, entries }]) =>
makeSidebarLink(
`${author.name} (${entries.length})`,
getRelativeBlogUrl(`/authors/${author.slug}`, locale),
isBlogAuthorPage(id, author.slug),
),
),
),
)
}
if (context.site) {
sidebar.push(makeSidebarLink(t('starlightBlog.sidebar.rss'), getRelativeBlogUrl('/rss.xml', locale, true), false))
}
return sidebar
}
function makeSidebarLink(
label: string,
href: string,
isCurrent: boolean,
attributes?: Omit<HTMLAttributes<'a'>, keyof AstroBuiltinAttributes | 'children'>,
) {
return {
attrs: attributes ?? {},
badge: undefined,
href,
isCurrent,
label,
type: 'link',
} satisfies StarlightRouteData['sidebar'][number]
}
function makeSidebarGroup(label: string, entries: StarlightRouteData['sidebar']) {
return {
badge: undefined,
collapsed: false,
entries,
label,
type: 'group',
} satisfies StarlightRouteData['sidebar'][number]
}