Add new Starlight-GhostCMS plugin #66
|
@ -26,8 +26,9 @@ export default function starlightBlogPlugin(userConfig?: StarlightGhostConfig):
|
|||
hooks: {
|
||||
'astro:config:setup': ({ injectRoute, updateConfig }) => {
|
||||
injectRoute({
|
||||
pattern: '',
|
||||
entrypoint: ''
|
||||
pattern: '@matthiesenxyz/starlight-ghostcms/routes/index.astro',
|
||||
entrypoint: '/blog',
|
||||
prerender: true,
|
||||
})
|
||||
|
||||
updateConfig({
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
---
|
||||
import type { StarlightBlogLink } from '../utils/content'
|
||||
|
||||
interface Props {
|
||||
next: StarlightBlogLink | undefined
|
||||
prev: StarlightBlogLink | undefined
|
||||
}
|
||||
|
||||
const { next, prev } = Astro.props
|
||||
---
|
||||
|
||||
{
|
||||
prev || next ? (
|
||||
<div class="pagination not-content">
|
||||
{prev && (
|
||||
<a href={prev.href} rel="prev">
|
||||
« {prev.label}
|
||||
</a>
|
||||
)}
|
||||
{next && (
|
||||
<a href={next.href} rel="next">
|
||||
{next.label} »
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
<style>
|
||||
.pagination {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
a {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--sl-color-gray-5);
|
||||
box-shadow: var(--sl-shadow-md);
|
||||
color: var(--sl-color-white);
|
||||
font-size: var(--sl-text-lg);
|
||||
line-height: 1.4;
|
||||
padding: 1rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-color: var(--sl-color-gray-2);
|
||||
}
|
||||
|
||||
[rel='next'] {
|
||||
justify-content: end;
|
||||
grid-column: 2;
|
||||
text-align: end;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
</style>
|
|
@ -2,26 +2,28 @@
|
|||
import StarlightMarkdownContent from '@astrojs/starlight/components/MarkdownContent.astro'
|
||||
import type { Props } from '@astrojs/starlight/props'
|
||||
|
||||
import PrevNextLinks from '../components/PrevNextLinks.astro'
|
||||
import { getBlogEntry, type StarlightBlogEntryPaginated } from '../utils/content'
|
||||
import { isAnyBlogPostPage } from '../utils/page'
|
||||
import Metadata from '../components/Metadata.astro'
|
||||
import type { Post } from '../schemas/posts'
|
||||
import { getSluggedPost } from '../utils/api'
|
||||
|
||||
const isBlogPost = isAnyBlogPostPage(Astro.props.slug)
|
||||
let blogEntry: StarlightBlogEntryPaginated | undefined = undefined
|
||||
let blogEntry: Post | undefined = undefined
|
||||
|
||||
if (isBlogPost) {
|
||||
blogEntry = await getBlogEntry(Astro.url.pathname)
|
||||
blogEntry = await getSluggedPost(Astro.props.slug)
|
||||
}
|
||||
---
|
||||
|
||||
<StarlightMarkdownContent {...Astro.props}>
|
||||
{isBlogPost && blogEntry ? <Metadata entry={blogEntry.entry} /> : null}
|
||||
<slot />
|
||||
{isBlogPost && blogEntry ? <Metadata entry={blogEntry} /> : null}
|
||||
|
||||
<div> PlaceHolder for HTML </div>
|
||||
|
||||
{
|
||||
isBlogPost && blogEntry ? (
|
||||
<div class="post-footer">
|
||||
<PrevNextLinks next={blogEntry.nextLink} prev={blogEntry.prevLink} />
|
||||
<!-- PREV - Next Links /-->
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
---
|
||||
import type { InferGetStaticPropsType } from 'astro'
|
||||
//import type { InferGetStaticPropsType } from 'astro'
|
||||
import config from 'virtual:starlight-ghost-config'
|
||||
|
||||
import Page from '../components/Page.astro'
|
||||
import Posts from '../components/Posts.astro'
|
||||
import PrevNextLinks from '../components/PrevNextLinks.astro'
|
||||
//import PrevNextLinks from '../components/PrevNextLinks.astro'
|
||||
import { getPageProps } from '../utils/page'
|
||||
import { getAllPosts } from '../utils/api'
|
||||
|
||||
export const prerender = true
|
||||
const entries = await getAllPosts();
|
||||
|
||||
export function getStaticPaths() {
|
||||
}
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>
|
||||
|
||||
const { entries, nextLink, prevLink } = Astro.props
|
||||
//const { entries, nextLink, prevLink } = Astro.props
|
||||
|
||||
const pageProps = getPageProps(config.title)
|
||||
---
|
||||
|
@ -22,7 +20,7 @@ const pageProps = getPageProps(config.title)
|
|||
<Page {...pageProps}>
|
||||
<Posts {entries} />
|
||||
<footer class="not-content">
|
||||
<PrevNextLinks next={nextLink} prev={prevLink} />
|
||||
<!--PrevNextLinks next={nextLink} prev={prevLink} /-->
|
||||
</footer>
|
||||
</Page>
|
||||
|
||||
|
|
|
@ -67,6 +67,23 @@ export const getAllPosts = async () => {
|
|||
return posts;
|
||||
};
|
||||
|
||||
export const getSluggedPost = async (slug:string) => {
|
||||
const result = await api.posts
|
||||
.read({slug: slug})
|
||||
.include({
|
||||
authors: true,
|
||||
tags: true,
|
||||
}).fetch()
|
||||
|
||||
if (result.success) {
|
||||
const post: Post = result.data;
|
||||
return post
|
||||
}
|
||||
if (result.errors) {
|
||||
console.log(result.errors.map((e) => e.message).join("\n"));
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllPages = async () => {
|
||||
const pages: Page[] = [];
|
||||
let cursor = await api.pages
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
import type { z } from 'astro/zod'
|
||||
import { getCollection, type AstroCollectionEntry } from 'astro:content'
|
||||
import starlightConfig from 'virtual:starlight/user-config'
|
||||
import config from 'virtual:starlight-ghost-config'
|
||||
|
||||
import type { Author } from '../schemas/authors'
|
||||
|
||||
export async function getBlogStaticPaths() {
|
||||
const entries = await getBlogEntries()
|
||||
|
||||
const entryPages: StarlightBlogEntry[][] = []
|
||||
|
||||
for (const entry of entries) {
|
||||
const lastPage = entryPages.at(-1)
|
||||
|
||||
if (!lastPage || lastPage.length === config.postCount) {
|
||||
entryPages.push([entry])
|
||||
} else {
|
||||
lastPage.push(entry)
|
||||
}
|
||||
}
|
||||
|
||||
if (entryPages.length === 0) {
|
||||
entryPages.push([])
|
||||
}
|
||||
|
||||
return entryPages.map((entries, index) => {
|
||||
const prevPage = index === 0 ? undefined : entryPages.at(index - 1)
|
||||
const nextPage = entryPages.at(index + 1)
|
||||
|
||||
return {
|
||||
params: {
|
||||
page: index === 0 ? undefined : index + 1,
|
||||
},
|
||||
props: {
|
||||
entries,
|
||||
nextLink: nextPage ? { href: `/blog/${index + 2}`, label: 'Older posts' } : undefined,
|
||||
prevLink: prevPage ? { href: index === 1 ? '/blog' : `/blog/${index}`, label: 'Newer posts' } : undefined,
|
||||
} satisfies StarlightBlogStaticProps,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function getRecentBlogEntries() {
|
||||
const entries = await getBlogEntries()
|
||||
|
||||
return entries.slice(0, config.recentPostCount)
|
||||
}
|
||||
|
||||
export async function getBlogEntry(slug: string): Promise<StarlightBlogEntryPaginated> {
|
||||
const entries = await getBlogEntries()
|
||||
|
||||
const entryIndex = entries.findIndex((entry) => entry.slug === slug.replace(/^\//, '').replace(/\/$/, ''))
|
||||
const entry = entries[entryIndex]
|
||||
|
||||
if (!entry) {
|
||||
throw new Error(`Blog post with slug '${slug}' not found.`)
|
||||
}
|
||||
|
||||
const prevEntry = entries[entryIndex - 1]
|
||||
const nextEntry = entries[entryIndex + 1]
|
||||
|
||||
return {
|
||||
entry,
|
||||
nextLink: nextEntry ? { href: `/${nextEntry.slug}`, label: nextEntry.data.title } : undefined,
|
||||
prevLink: prevEntry ? { href: `/${prevEntry.slug}`, label: prevEntry.data.title } : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export function getBlogEntryMetadata(entry: StarlightBlogEntry): StarlightBlogEntryMetadata {
|
||||
const authors: Author[] = []
|
||||
|
||||
if (!entry.data.authors) {
|
||||
authors.push(...Object.values(authors))
|
||||
} else {
|
||||
authors.push(entry.data.authors)
|
||||
}
|
||||
|
||||
return {
|
||||
authors,
|
||||
date: entry.data.date.toLocaleDateString(starlightConfig.defaultLocale.lang, { dateStyle: 'medium' }),
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBlogEntries() {
|
||||
const entries = await getCollection<StarlightEntryData>('docs', ({ id }) => {
|
||||
return id.startsWith('blog/') && id !== 'blog/index.mdx'
|
||||
})
|
||||
|
||||
return entries.sort((a, b) => {
|
||||
return b.data.date.getTime() - a.data.date.getTime()
|
||||
})
|
||||
}
|
||||
|
||||
export async function getBlogEntryExcerpt(entry: StarlightBlogEntry) {
|
||||
if (entry.data.excerpt) {
|
||||
return entry.data.excerpt
|
||||
}
|
||||
|
||||
const { Content } = await entry.render()
|
||||
|
||||
return Content
|
||||
}
|
||||
|
||||
type StarlightEntryData = z.infer<ReturnType<typeof blogSchema>> & { title: string }
|
||||
type StarlightEntry = AstroCollectionEntry<StarlightEntryData>
|
||||
|
||||
export type StarlightBlogEntry = StarlightEntry & {
|
||||
data: {
|
||||
date: Date
|
||||
}
|
||||
}
|
||||
|
||||
export interface StarlightBlogLink {
|
||||
href: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface StarlightBlogEntryPaginated {
|
||||
entry: StarlightBlogEntry
|
||||
nextLink: StarlightBlogLink | undefined
|
||||
prevLink: StarlightBlogLink | undefined
|
||||
}
|
||||
|
||||
interface StarlightBlogEntryMetadata {
|
||||
authors: Author[]
|
||||
date: string
|
||||
}
|
||||
|
||||
|
||||
interface StarlightBlogStaticProps {
|
||||
entries: StarlightBlogEntry[]
|
||||
nextLink: StarlightBlogLink | undefined
|
||||
prevLink: StarlightBlogLink | undefined
|
||||
}
|
Loading…
Reference in New Issue