Replaced contentlayer with keystatic

This commit is contained in:
giancarlo
2024-04-10 14:52:18 +08:00
parent f729bf6077
commit 006c4d430f
37 changed files with 2842 additions and 2776 deletions

View File

@@ -0,0 +1,124 @@
import { Entry, createReader } from '@keystatic/core/reader';
import { Cms, CmsClient } from '@kit/cms';
import config from './keystatic.config';
const reader = createReader('.', config);
type EntryProps = Entry<(typeof config)['collections']['posts']>;
export class KeystaticClient implements CmsClient {
async getContentItems(options?: Cms.GetContentItemsOptions) {
const docs = await reader.collections.posts.all();
const startOffset = options?.offset ?? 0;
const endOffset = startOffset + (options?.limit ?? 10);
return Promise.all(
docs
.filter((item) => {
const categoryMatch = options?.categories
? options.categories.find((category) =>
item.entry.categories.includes(category),
)
: true;
if (!categoryMatch) {
return false;
}
const tagMatch = options?.tags
? options.tags.find((tag) => item.entry.tags.includes(tag))
: true;
if (!tagMatch) {
return false;
}
return true;
})
.slice(startOffset, endOffset)
.map(async (item) => {
const children = docs.filter(
(item) => item.entry.parent === item.slug,
);
console.log(item);
return this.mapPost(item, children);
}),
);
}
async getContentItemById(id: string) {
const doc = await reader.collections.posts.read(id);
if (!doc) {
return Promise.resolve(undefined);
}
return this.mapPost({ entry: doc, slug: id }, []);
}
async getCategories() {
return Promise.resolve([]);
}
async getTags() {
return Promise.resolve([]);
}
async getTagBySlug() {
return Promise.resolve(undefined);
}
async getCategoryBySlug() {
return Promise.resolve(undefined);
}
private async mapPost<
Type extends {
entry: EntryProps;
slug: string;
},
>(item: Type, children: Type[] = []): Promise<Cms.ContentItem> {
const publishedAt = item.entry.publishedAt
? new Date(item.entry.publishedAt)
: new Date();
const content = await item.entry.content();
return {
id: item.slug,
title: item.entry.title,
url: item.slug,
slug: item.slug,
description: item.entry.description,
publishedAt,
author: item.entry.author,
content,
image: item.entry.image ?? undefined,
categories:
item.entry.categories.map((item) => {
return {
id: item,
name: item,
slug: item,
};
}) ?? [],
tags: item.entry.tags.map((item) => {
return {
id: item,
name: item,
slug: item,
};
}),
parentId: item.entry.parent ?? undefined,
order: item.entry.order ?? 1,
children: await Promise.all(
children.map(async (child) => this.mapPost(child, [])),
),
};
}
}

View File

@@ -0,0 +1,6 @@
import { DocumentElement } from '@keystatic/core';
import { DocumentRenderer } from '@keystatic/core/renderer';
export function KeystaticDocumentRenderer(props: { content: unknown }) {
return <DocumentRenderer document={props.content as DocumentElement[]} />;
}

View File

@@ -0,0 +1,2 @@
export * from './client';
export * from './content-renderer';

View File

@@ -0,0 +1,7 @@
'use client';
import { makePage } from '@keystatic/next/ui/app';
import config from './keystatic.config';
export default makePage(config);

View File

@@ -0,0 +1,7 @@
import { makeRouteHandler } from '@keystatic/next/route-handler';
import keystaticConfig from './keystatic.config';
export const { POST, GET } = makeRouteHandler({
config: keystaticConfig,
});

View File

@@ -0,0 +1,57 @@
import { collection, config, fields } from '@keystatic/core';
import { z } from 'zod';
const path = z.string().parse(process.env.NEXT_PUBLIC_KEYSTATIC_CONTENT_PATH);
export default createKeyStaticConfig(path);
function createKeyStaticConfig(path: string) {
return config({
storage: {
kind: 'local',
},
collections: {
posts: collection({
label: 'Posts',
slugField: 'title',
path: `${path}/posts/*`,
format: { contentField: 'content' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
image: fields.image({
label: 'Image',
directory: 'public/site/images',
publicPath: '/site/images',
}),
categories: fields.array(fields.text({ label: 'Category' })),
tags: fields.array(fields.text({ label: 'Tag' })),
description: fields.text({ label: 'Description' }),
publishedAt: fields.date({ label: 'Published At' }),
author: fields.text({ label: 'Author' }),
parent: fields.relationship({
label: 'Parent',
collection: 'posts',
}),
order: fields.number({ label: 'Order' }),
content: fields.document({
label: 'Content',
formatting: true,
dividers: true,
links: true,
images: {
directory: 'public/site/images',
publicPath: '/site/images',
schema: {
title: fields.text({
label: 'Caption',
description:
'The text to display under the image in a caption.',
}),
},
},
}),
},
}),
},
});
}