Add new Starlight-GhostCMS plugin #66
|
@ -26,8 +26,9 @@ export default function starlightBlogPlugin(userConfig?: StarlightGhostConfig):
|
||||||
hooks: {
|
hooks: {
|
||||||
'astro:config:setup': ({ injectRoute, updateConfig }) => {
|
'astro:config:setup': ({ injectRoute, updateConfig }) => {
|
||||||
injectRoute({
|
injectRoute({
|
||||||
pattern: '',
|
pattern: '@matthiesenxyz/starlight-ghostcms/routes/index.astro',
|
||||||
entrypoint: ''
|
entrypoint: '/blog',
|
||||||
|
prerender: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
updateConfig({
|
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 StarlightMarkdownContent from '@astrojs/starlight/components/MarkdownContent.astro'
|
||||||
import type { Props } from '@astrojs/starlight/props'
|
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 { isAnyBlogPostPage } from '../utils/page'
|
||||||
import Metadata from '../components/Metadata.astro'
|
import Metadata from '../components/Metadata.astro'
|
||||||
|
import type { Post } from '../schemas/posts'
|
||||||
|
import { getSluggedPost } from '../utils/api'
|
||||||
|
|
||||||
const isBlogPost = isAnyBlogPostPage(Astro.props.slug)
|
const isBlogPost = isAnyBlogPostPage(Astro.props.slug)
|
||||||
let blogEntry: StarlightBlogEntryPaginated | undefined = undefined
|
let blogEntry: Post | undefined = undefined
|
||||||
|
|
||||||
if (isBlogPost) {
|
if (isBlogPost) {
|
||||||
blogEntry = await getBlogEntry(Astro.url.pathname)
|
blogEntry = await getSluggedPost(Astro.props.slug)
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<StarlightMarkdownContent {...Astro.props}>
|
<StarlightMarkdownContent {...Astro.props}>
|
||||||
{isBlogPost && blogEntry ? <Metadata entry={blogEntry.entry} /> : null}
|
{isBlogPost && blogEntry ? <Metadata entry={blogEntry} /> : null}
|
||||||
<slot />
|
|
||||||
|
<div> PlaceHolder for HTML </div>
|
||||||
|
|
||||||
{
|
{
|
||||||
isBlogPost && blogEntry ? (
|
isBlogPost && blogEntry ? (
|
||||||
<div class="post-footer">
|
<div class="post-footer">
|
||||||
<PrevNextLinks next={blogEntry.nextLink} prev={blogEntry.prevLink} />
|
<!-- PREV - Next Links /-->
|
||||||
</div>
|
</div>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
---
|
---
|
||||||
import type { InferGetStaticPropsType } from 'astro'
|
//import type { InferGetStaticPropsType } from 'astro'
|
||||||
import config from 'virtual:starlight-ghost-config'
|
import config from 'virtual:starlight-ghost-config'
|
||||||
|
|
||||||
import Page from '../components/Page.astro'
|
import Page from '../components/Page.astro'
|
||||||
import Posts from '../components/Posts.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 { getPageProps } from '../utils/page'
|
||||||
|
import { getAllPosts } from '../utils/api'
|
||||||
|
|
||||||
export const prerender = true
|
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)
|
const pageProps = getPageProps(config.title)
|
||||||
---
|
---
|
||||||
|
@ -22,7 +20,7 @@ const pageProps = getPageProps(config.title)
|
||||||
<Page {...pageProps}>
|
<Page {...pageProps}>
|
||||||
<Posts {entries} />
|
<Posts {entries} />
|
||||||
<footer class="not-content">
|
<footer class="not-content">
|
||||||
<PrevNextLinks next={nextLink} prev={prevLink} />
|
<!--PrevNextLinks next={nextLink} prev={prevLink} /-->
|
||||||
</footer>
|
</footer>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,23 @@ export const getAllPosts = async () => {
|
||||||
return posts;
|
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 () => {
|
export const getAllPages = async () => {
|
||||||
const pages: Page[] = [];
|
const pages: Page[] = [];
|
||||||
let cursor = await api.pages
|
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