145 lines
4.1 KiB
TypeScript
145 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 url = new URL(req.url);
|
|
const searchParams = Object.fromEntries(url.searchParams.entries());
|
|
|
|
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" },
|
|
});
|
|
}
|
|
},
|
|
});
|