Add basic blog functionality

This commit is contained in:
Jade Ellis
2024-07-15 13:58:21 +01:00
parent 0bfb620d2f
commit e823b1d676
13 changed files with 514 additions and 115 deletions
+2 -2
View File
@@ -24,8 +24,8 @@
</a>
</li>
<!-- <li><a href="/about/" class="navbar-link">About</a></li>
<li><a href="/art/" class="navbar-link">Art</a></li>
<li><a href="/blog/" class="navbar-link">Blog</a></li> -->
<li><a href="/art/" class="navbar-link">Art</a></li> -->
<li><a href="/blog/" class="navbar-link">Blog</a></li>
</ul>
</nav>
</div>
+3
View File
@@ -0,0 +1,3 @@
export function match(value) {
return /^\d+$/.test(value);
}
@@ -0,0 +1,8 @@
import { pages } from './posts'
export async function load({ params }) {
return {
pages
}
}
@@ -0,0 +1,31 @@
<script lang="ts">
import { page } from "$app/stores";
export let data;
let { pages } = data;
$: console.log(data);
</script>
<section role="feed">
<h1>Blog Posts</h1>
{#each pages as post, index}
<article aria-posinset={index + 1} aria-setsize={pages.length}>
<div class="content" data-sveltekit-preload-data="hover">
<h2>
<a href="/blog/{post.canonical}" class="text-link">
{post.title}
</a>
</h2>
<span class="quiet">{post.date}</span>
{#if post.description}
<p>{post.description}</p>
{/if}
</div>
</article>
{:else}
<p>No posts yet!</p>
{/each}
<!-- {#if showPosts < postCount}
<button type="submit" on:click={handleClick}>See more {H_ELLIPSIS_ENTITY}</button>
{/if} -->
</section>
@@ -0,0 +1,43 @@
import { pages } from '../../posts'
import { error } from '@sveltejs/kit'
import TTLCache, { } from "@isaacs/ttlcache";
import { parse } from "@tusbar/cache-control";
const cache = new TTLCache({ max: 10000, ttl: 1000 })
import type { Endpoints } from "@octokit/types";
let repoRegex = new RegExp("https?://github\.com/(?<repo>[a-zA-Z0-9]+/[a-zA-Z0-9]+)/?")
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
const { slug } = params
let dateParts = params.date.split(/[\/-]/).map((p) => parseInt(p, 10))
if (dateParts.length > 3) {
throw error(404, 'Post not found (bad date)')
}
// let start = new Date(dateParts[0] || 1, dateParts[1] || 0, dateParts[2] || 0);
// // @ts-ignore
// let end = new Date(...dateParts);
// console.log(dateParts)
// get post with metadata
const page = pages
.filter((post) => slug === post.slug)
.filter((post) => {
let date = new Date(post.date)
return (
(!dateParts[0] || date.getFullYear() == dateParts[0]) &&
(!dateParts[1] || date.getMonth()+1 == dateParts[1]) &&
(!dateParts[2] || date.getDate() == dateParts[2])
)
})[0]
if (!page) {
throw error(404, 'Post not found')
}
return {
page
}
}
@@ -0,0 +1,27 @@
<script lang="ts">
// https://github.com/mattjennings/sveltekit-blog-template/blob/main/src/routes/post/%5Bslug%5D/%2Bpage.svelte
import SvelteSeo from "svelte-seo";
export let data;
import { SITE_URL } from "$lib/metadata";
// let GhReleasesDownload: Promise<any>;
// if (data.ghReleaseData) {
// GhReleasesDownload = import("$lib/GhReleasesDownload.svelte").then((m) => m.default)
// }
console.log(data)
</script>
<SvelteSeo
title={data.post.title}
description={data.post.description}
canonical={SITE_URL + "/blog/" + data.post.canonical}
/>
<h1>{data.post.title}</h1>
<!-- {#await GhReleasesDownload}
{:then component}
<svelte:component this={component} releaseData={data.ghReleaseData} />
{/await} -->
<svelte:component this={data.component} />
@@ -0,0 +1,22 @@
// import { pages } from './projects'
import { error } from '@sveltejs/kit'
/** @type {import('./$types').PageServerLoad} */
export async function load({ data, params }) {
// if (!post) {
// throw error(404, 'Post not found')
// }
// load the markdown file based on slug
console.log(data)
const component =
// await import(data.page.filepath)
await import("Notes/Blogs/" + data.page.filepath.name + ".md")
// console.log(data.page.filepath)
return {
post: data.page,
component: component.default
}
}
+106
View File
@@ -0,0 +1,106 @@
import { browser } from '$app/environment'
// import { format } from 'date-fns'
import slugify from 'slugify';
import { parse, format, relative } from "node:path";
// we require some server-side APIs to parse all metadata
if (browser) {
throw new Error(`posts can only be imported server-side`)
}
// import { browser } from '$app/environment'
// import { format } from 'date-fns'
// import { parse } from 'node-html-parser'
// import readingTime from 'reading-time/lib/reading-time.js'
// // we require some server-side APIs to parse all metadata
// if (browser) {
// throw new Error(`posts can only be imported server-side`)
// }
// // Get all posts and add metadata
// export const posts = Object.entries(import.meta.glob('/posts/**/*.md', { eager: true }))
// .map(([filepath, post]) => {
// const html = parse(post.default.render().html)
// const preview = post.metadata.preview ? parse(post.metadata.preview) : html.querySelector('p')
// return {
// ...post.metadata,
// // generate the slug from the file path
// slug: filepath
// .replace(/(\/index)?\.md/, '')
// .split('/')
// .pop(),
// // whether or not this file is `my-post.md` or `my-post/index.md`
// // (needed to do correct dynamic import in posts/[slug].svelte)
// isIndexFile: filepath.endsWith('/index.md'),
// // format date as yyyy-MM-dd
// date: post.metadata.date
// ? format(
// // offset by timezone so that the date is correct
// addTimezoneOffset(new Date(post.metadata.date)),
// 'yyyy-MM-dd'
// )
// : undefined,
// preview: {
// html: preview.toString(),
// // text-only preview (i.e no html elements), used for SEO
// text: preview.structuredText ?? preview.toString()
// },
// // get estimated reading time for the post
// readingTime: readingTime(html.structuredText).text
// }
// })
// // sort by date
// .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
// // add references to the next/previous post
// .map((post, index, allPosts) => ({
// ...post,
// next: allPosts[index - 1],
// previous: allPosts[index + 1]
// }))
const dateRegex = /^((?<year>\d{4})-(?<month>[0][1-9]|1[0-2])-(?<day>[0][1-9]|[1-2]\d|3[01]))\s*/
export const pages = Object.entries(import.meta.glob('/node_modules/Notes/Blogs/*.md', { eager: true }))
.map(([filepath, post]) => {
let path = parse(filepath);
let title = path.name.replace(dateRegex, "")
// @ts-ignore
// let {year, month, day}: { year: string, month: string, day: string } = path.name.match(dateRegex)?.groups;
// console.log(year, month, day)
let date = path.name.match(dateRegex)[1];
let datePath = date.replaceAll("-", "/")
let slug = slugify(title, { lower: true })
return {
title,
date,
canonical: datePath + "/" + slug,
// @ts-ignore
...post.metadata,
slug,
// filepath: relative(import.meta.dirname, filepath)
filepath: path
}
})
// sort by date
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
// Get all posts and add metadata
// // add references to the next/previous post
// .map((post, index, allPosts) => ({
// ...post,
// next: allPosts[index - 1],
// previous: allPosts[index + 1]
// }))
// function addTimezoneOffset(date) {
// const offsetInMilliseconds = new Date().getTimezoneOffset() * 60 * 1000
// return new Date(new Date(date).getTime() + offsetInMilliseconds)
// }
@@ -6,8 +6,8 @@ import type { RequestHandler } from '@sveltejs/kit';
import slugify from 'slugify';
import { parse, format } from "node:path";
const pages = Object.entries(import.meta.glob('/node_modules/Notes/Projects/*.md', { eager: true }))
import { pages as blogPosts } from "../blog/posts"
const projects = Object.entries(import.meta.glob('/node_modules/Notes/Projects/*.md', { eager: true }))
.map(([filepath, post]) => {
return parse(filepath)
})
@@ -27,7 +27,8 @@ export const GET: RequestHandler = async ({ params }) => {
origin: SITE_URL,
page: params.page,
paramValues: {
'/projects/[slug]': pages
'/projects/[slug]': projects,
'/blog/[...date]/[slug]': blogPosts.map((post) => post.canonical)
}
});
};