Most all posts into memory at startup and render those on requests.

This commit is contained in:
Dave Smith-Hayes 2024-07-26 22:31:09 -04:00
parent 3c4be7f78f
commit 6e400f4263
10 changed files with 87 additions and 49 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,17 +1,25 @@
import { Hono } from 'hono'; import { Hono } from "hono";
import { Home } from '@blog/templates/Pages/Home'; import { Home } from "@blog/templates/Pages/Home";
import { getPostList } from '@blog/post/post-reader'; import { PostMeta } from "@blog/models/PostMeta";
import { PostMeta } from '@blog/model/PostMeta'; import { Post } from '@blog/models/Post';
import { PostFileService } from '@blog/services/post-file';
const app = new Hono(); type Posts = {
postService: PostFileService
};
app.get('/', async (c) => { const app = new Hono<{ Variables: Posts }>();
const postDir = __dirname + '/../../posts';
const postList: PostMeta[] = await getPostList(postDir);
return c.render(<Home posts={postList}/>, { meta: { app.get("/", async (c) => {
description: "The blog for Dave Smith-Hayes, a dad and developer." const postService: PostFileService = c.get('postService');
}}); const posts = postService.getPosts();
const postList: PostMeta[] = Array.from(posts.values()).map((p: Post) => p.meta);
return c.render(<Home posts={postList} />, {
meta: {
description: "The blog for Dave Smith-Hayes, a dad and developer.",
},
});
}); });
export default app; export default app;

View File

@ -1,34 +1,37 @@
import { Hono } from 'hono'; import { Hono } from 'hono';
import { PostPage } from '@blog/templates/Pages/PostPage'; import { PostPage } from '@blog/templates/Pages/PostPage';
import { FourOhFour } from '@blog/templates/Pages/FourOhFour'; import { FourOhFour } from '@blog/templates/Pages/FourOhFour';
import { readdir } from 'node:fs/promises'; import { SiteMeta } from '@blog/models/SiteMeta';
import { SiteMeta } from '@blog/model/SiteMeta'; import { PostFileService } from '@blog/services/post-file';
import { readPostMarkdown } from '@blog/post/post-reader';
const app = new Hono(); type Posts = {
postService: PostFileService
}
const app = new Hono<{ Variables: Posts }>();
app.get('/:slug', async (c) => { app.get('/:slug', async (c) => {
const postSlug: string = c.req.param("slug"); const postSlug: string = c.req.param("slug");
const fileName: string = postSlug + '.md'; const postService: PostFileService = c.get('postService');
const files = await readdir(__dirname + "/../../posts");
const postFile = files.find(f => f === fileName); try {
if (!postFile) { const post = postService.getPost(postSlug);
c.status(404);
const meta: SiteMeta = { const meta: SiteMeta = {
description: "Page does not exist." description: post.meta.description,
tags: post.meta.tags,
author: "Dave Smith-Hayes"
}; };
return c.render(<PostPage post={post} />, { meta });
} catch (e) {
const description: string = "Page does not exist.";
console.error(description);
console.error(e);
c.status(404);
const meta: SiteMeta = { description };
return c.render(<FourOhFour />, { meta }); return c.render(<FourOhFour />, { meta });
} }
const post = await readPostMarkdown(__dirname + "/../../posts/" + postFile);
const meta: SiteMeta = {
description: post.meta.description,
tags: post.meta.tags,
author: "Dave Smith-Hayes"
};
return c.render(<PostPage post={post} />, { meta });
}); });
export default app; export default app;

View File

@ -4,6 +4,8 @@ import { Page } from '@blog/templates/Page';
import home from '@blog/handlers/home'; import home from '@blog/handlers/home';
import posts from '@blog/handlers/posts'; import posts from '@blog/handlers/posts';
import type { SiteMeta } from '@blog/models/SiteMeta'; import type { SiteMeta } from '@blog/models/SiteMeta';
import { postFileMiddleware } from '@blog/middleware/post-service';
import { logger } from 'hono/logger';
declare module 'hono' { declare module 'hono' {
interface ContextRenderer { interface ContextRenderer {
@ -13,7 +15,10 @@ declare module 'hono' {
const app = new Hono(); const app = new Hono();
// Render the JSX views app.use(postFileMiddleware);
app.use(logger());
// JSX Rendering for Templates
app.get( app.get(
'*', '*',
jsxRenderer( jsxRenderer(
@ -22,7 +27,7 @@ app.get(
) )
); );
console.log({ message: "Bootstrapping the routes..." }); console.log(JSON.stringify({ message: "Bootstrapping the routes..." }));
app.route('/', home); app.route('/', home);
app.route('/posts', posts); app.route('/posts', posts);

View File

@ -0,0 +1,14 @@
import { createFactory } from 'hono/factory';
import { PostFileService } from '@blog/services/post-file';
const factory = createFactory();
let postFileService: PostFileService;
export const postFileMiddleware = factory.createMiddleware(async (c, next) => {
if (!postFileService) {
postFileService = await PostFileService.create();
}
c.set('postService', postFileService);
await next();
});

View File

@ -1,7 +1,7 @@
import { PostMeta } from '@blog/model/PostMeta'; import { PostMeta } from "@blog/models/PostMeta";
export type Post = { export type Post = {
meta: PostMeta, meta: PostMeta;
content: string, content: string;
html?: string html?: string;
}; };

View File

@ -1,4 +1,3 @@
import { Database } from "bun:sqlite"; import { Database } from "bun:sqlite";
import { SQLITE_DATABASE_FILE } from "@blog/config"; import { SQLITE_DATABASE_FILE } from "@blog/config";
export const db = new Database(SQLITE_DATABASE_FILE); export const db = new Database(SQLITE_DATABASE_FILE);

View File

@ -1,19 +1,19 @@
import { POST_PATH, POST_ROUTE_PREFIX } from '@blog/config'; import { POST_PATH } from "@blog/config";
import { readdir } from 'node:fs/promises'; import { readdir } from "node:fs/promises";
import type { Post } from '@blog/models/Post'; import type { Post } from "@blog/models/Post";
import * as yamlFront from 'yaml-front-matter'; import * as yamlFront from "yaml-front-matter";
import { marked } from 'marked'; import { marked } from "marked";
export async function readPostFile(path: string): Promise<Post> { export async function readPostFile(path: string): Promise<Post> {
const file = Bun.file(path); const file = Bun.file(path);
const fileContent = await file.text(); const fileContent = await file.text();
const parsedData = yamlFront.loadFront(fileContent); const parsedData = yamlFront.loadFront(fileContent);
const postHtml = await marked.parse(parsedData.__content); const postHtml = await marked.parse(parsedData.__content);
let slug = parsedData.slug; let slug = parsedData.slug;
if (!slug) { if (!slug) {
slug = path.split('/').pop().slice(0, -3); slug = path.split("/").pop()?.slice(0, -3);
} }
return { return {
@ -23,15 +23,15 @@ export async function readPostFile(path: string): Promise<Post> {
date: new Date(parsedData.date), date: new Date(parsedData.date),
draft: parsedData?.draft || false, draft: parsedData?.draft || false,
tags: parsedData.tags, tags: parsedData.tags,
slug: slug slug: slug,
}, },
content: parsedData.__content, content: parsedData.__content,
html: postHtml html: postHtml,
}; };
} }
export class PostFileService { export class PostFileService {
private posts: Map<string, Post>; private posts: Map<string, Post>;
public constructor(posts: Map<string, Post>) { public constructor(posts: Map<string, Post>) {
this.posts = posts; this.posts = posts;

View File

@ -11,7 +11,16 @@ WHERE id = $id
const query = db.query(queryString); const query = db.query(queryString);
const results = query.get({ $id: id }); const results = query.get({ $id: id });
return {} return {
meta: {
title: results.title,
slug: results.slug,
description: results.description,
date: new Date(results.published_date),
draft: results.is_draft,
},
content: results.raw_content,
}
} }
public async getPostBySlug(slug: string): Post { public async getPostBySlug(slug: string): Post {

View File

@ -6,7 +6,7 @@ export function PostList({ posts }: { posts: PostMeta[] }) {
{posts.map(p => { {posts.map(p => {
return ( return (
<li> <li>
<a href={p.slug}>{p.title}</a> <a href={"/posts/" + p.slug}>{p.title}</a>
</li> </li>
); );
})} })}