Blog/index.tsx

144 lines
4.1 KiB
TypeScript

import React from "react";
// Database connection is now handled by the centralized db module
import { renderToString } from "react-dom/server";
import { AppShell } from "./src/frontend/AppShell";
import { Home } from "./src/frontend/pages/home";
import { NotFound } from "./src/frontend/pages/not-found";
import demo from "./temp/appshell.html";
import { Post } from "./src/frontend/pages/post";
import { getPostWithTags, getAdjacentPosts } from "./src/db/index";
async function blogPosts(hmr: boolean) {
const glob = new Bun.Glob("**/*.md");
const blogPosts: Record<string, any> = {};
for await (const file of glob.scan("./content")) {
const post = await import(`./content/${file}`, { with: { type: "html" } });
const route = `/${file.replace(/\.md$/, "")}`;
if (hmr) {
// Use Bun Importer plugin for hot reloading in the browser
blogPosts[`/hmr${route}`] = post.default;
} else {
// Use the Database for sending just the HTML or the HTML and AppShell
blogPosts[route] = (req: Request) => {
const path = new URL(req.url).pathname;
const post = getPostWithTags(path);
if (!post)
return new Response(renderToString(<NotFound />), {
status: 404,
headers: { "Content-Type": "text/html" },
});
// Get adjacent posts for navigation
const { previousPost, nextPost } = getAdjacentPosts(post.path);
const data = {
title: post.title,
summary: post.summary,
date: new Date(post.date),
readingTime: post.reading_time,
tags: post.tags || [],
previousPost,
nextPost,
};
// AppShell is already loaded, just send the <main> content
if (req.headers.get("shell-loaded") === "true") {
return new Response(
renderToString(<Post meta={data} children={post.content} />),
{
headers: {
"Content-Type": "text/html",
},
},
);
}
// AppShell is not loaded, send the <AppShell> with the <main> content inside
return new Response(
renderToString(
<AppShell>
<Post
meta={data}
children={post.content}
/>
</AppShell>,
),
{
headers: {
"Content-Type": "text/html",
},
},
);
};
}
}
Object.keys(blogPosts).map((route) => {
console.info(route);
});
return blogPosts;
}
Bun.serve({
development: {
hmr: true,
console: true,
},
routes: {
// standard mounting of blog posts
...(await blogPosts(false)),
// hot module replacement in development mode
...(process.env.NODE_ENV === "development" ? (await blogPosts(true)) : []),
// Home page
"/": (req: Request) => {
// Extract URL parameters from the request to pass to the component
const searchParams = new URLSearchParams(req.url.split('?')[1]);
if (req.headers.get("shell-loaded") === "true") {
return new Response(renderToString(<Home searchParams={searchParams} />), {
headers: {
"Content-Type": "text/html",
},
});
}
return new Response(
renderToString(
<AppShell>
<Home searchParams={searchParams} />
</AppShell>,
),
{
headers: {
"Content-Type": "text/html",
},
},
);
},
"/target": demo,
"/profile-picture.webp": () => {
return new Response(Bun.file("./src/public/profile-picture.webp"), {
headers: {
"Content-Type": "image/webp",
},
});
},
"/*": (req) => {
if(req.headers.get("shell-loaded") === "true") {
return new Response(renderToString(<NotFound />), {
status: 404,
headers: { "Content-Type": "text/html" },
});
}
return new Response(renderToString(<AppShell><NotFound /></AppShell>), {
status: 404,
headers: { "Content-Type": "text/html" },
});
}
},
});