BlogEngine: Remove last references to the pre-built blog posts
This commit is contained in:
parent
a8fc9a2691
commit
8d2a27e4d8
17 changed files with 272 additions and 66 deletions
|
|
@ -18,7 +18,7 @@ System fonts, I had argued to me, are designed and considered specifically for t
|
|||
|
||||
Ever since they introduced that tiny bit of doubt into my mind, I couldn’t help thinking that maybe I should just be using system fonts. Long story short, I rewrote my entire personal site (https://thomaswilson.xyz) from [Nuxt](https://nuxtjs.org) (a Vue.JS application framework) into [Gatsby](https://www.gatsbyjs.org) (a React.JS static site generator). It took me like to evenings and I’m happy with my decision.
|
||||
|
||||
Look, there were a couple of other factors in this. Like, I had just taken the jump to go freelance so I needed to move my increasing portfolio over to my professional site. I wanted to move some of my older pieces of writing from Medium onto pages on this site. I was creating a beautiful list of my [favourite albums of 2019](/albums-2019). There was a lot going on, and I was finding a lot of friction in the process of writing blog posts for this site.
|
||||
Look, there were a couple of other factors in this. Like, I had just taken the jump to go freelance so I needed to move my increasing portfolio over to my professional site. I wanted to move some of my older pieces of writing from Medium onto pages on this site. I was creating a beautiful list of my favourite albums of 2019 (note from 2023 Wilson: This page of 2019's hottest records didn't make the move between sites). There was a lot going on, and I was finding a lot of friction in the process of writing blog posts for this site.
|
||||
|
||||
## Why React, not Vue ?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
|
||||
title: "Re-publishing my writing on my experiences with food and eating disorders"
|
||||
author: "Thomas Wilson"
|
||||
draft: false
|
||||
date: 2020-02-20
|
||||
slug: "introducing-eating-anthology"
|
||||
tags:
|
||||
- eating-disorder
|
||||
---
|
||||
|
||||
**tl;dr** - I have moved the writing I have created on my experiences with eating disorders to this site (and off of Medium.). You can find them [here](/eating-anthology).
|
||||
|
||||
For at least the past five years, my mental health has been affected through and alongside a weird relationship with food, exercise, and eating. I find writing helps me clarify my thoughts and my situation to me. While a lot of this writing is intentionally private, there are a couple of piece I want to be publicly available.
|
||||
|
||||
Back when I published my first piece on this subject, I put it on medium.com - however that site has recently favoured the advertiser over the reader. Back in 2016 it was very much the opposite, and while I understand they need to make money from people reading their writing - I don't, and so I can afford to favour the reader experience. When I've written something so personal and meaningful, I wanted people to read it in the same way.
|
||||
|
||||
So I have collated my writing into a small anthology, which you can find on my personal site, [here](/eating-anthology) or each piece individually at:
|
||||
|
||||
- 2016: [My Eating Disorder Was Dangerous Because It Was Powerful](/eating-anthology/2016)
|
||||
- 2017: [Loss and my ED](/eating-anthology/2017)
|
||||
- 2020: [My eating disorder will always remind me of the person I was, and am](/eating-anthology/2020)
|
||||
|
||||
I've left the initial pieces as untouched - with only some minor styling changes to bring some of the long lyrics I mention into life.
|
||||
|
|
@ -28,10 +28,10 @@ But still, the problem isn't knowing what three things to do before breakfast, i
|
|||
|
||||
We all want that. And Clear dedicates a whole book to giving us the tools to get there. I want to surface the parts of Atomic Habits that resonated with me:
|
||||
|
||||
1. [The rules for forging and breaking a habit.](#1-the-rules)
|
||||
2. [Call out your automatic actions / lazy behaviours.](#2-call-out-your-automatic-or-lazy-behaviours)
|
||||
3. [Decide what kind of person you want to be.](#3-decide-what-kind-of-person-you-want-to-be)
|
||||
4. [Monkey brain makes decisions but is easy to trick.](#4-monkey-brain-is-tricky)
|
||||
1. The rules for forging and breaking a habit.
|
||||
2. Call out your automatic actions / lazy behaviours.
|
||||
3. Decide what kind of person you want to be.
|
||||
4. Monkey brain makes decisions but is easy to trick.
|
||||
|
||||
## 1. The Rules
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Still, that feels cool.
|
|||
|
||||
## Sewing chat
|
||||
|
||||
I've started making my first *real* shirt (after three muslin toiles). The folk over at [freesewing](www.freesewing.org) have done a good job at making a (completely free and open source) tool to help me, a very novice tailor, draft and re-draft a pattern. I love the internet.
|
||||
I've started making my first *real* shirt (after three muslin toiles). The folk over at [freesewing](https://www.freesewing.org) have done a good job at making a (completely free and open source) tool to help me, a very novice tailor, draft and re-draft a pattern. I love the internet.
|
||||
|
||||
I picked up some certified sustainable fabrics from [Offset Warehouse](https://www.offsetwarehouse.com/) which turned out to be a little more summery than I was expecting. Perhaps not November-December style dress.
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ I could have made a toile to test the alteration, but it would have been my four
|
|||
|
||||
So on to trousers I go. I'll be mocking up the [Wardrobe By Me mens Chinos](https://wardrobebyme.com/products/chino-pants-sewing-pattern) soon. This is the first trouser pattern I found that went down to a 28" waist (even down to a 26!).
|
||||
|
||||
I toyed around with a Thread Theory pattern over summer, but they only go down to 30" waist. I loved my recent adventures in drafting and altering (thanks, once more [freesewing](www.freesewing.org)) but I want something prefabbed to tweak, not something rough that needs shaping.
|
||||
I toyed around with a Thread Theory pattern over summer, but they only go down to 30" waist. I loved my recent adventures in drafting and altering (thanks, once more [freesewing](https://www.freesewing.org)) but I want something prefabbed to tweak, not something rough that needs shaping.
|
||||
|
||||
Finding clothes that fit has always been hard for me. Ready to wear formal menswear is designed for larger (upwards, but also outwards) bodies. The skills to make, and then change, sewing patterns is a lot. It's been a year and a half since I picked up my first sewing machine and I'm still finding new things to trouble-shoot. They're smaller things, and I think I'm getting better at fixing them, but it's a lot of energy.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { describe, it, beforeEach, expect } from 'vitest';
|
||||
import { describe, it, beforeEach, beforeAll, expect } from 'vitest';
|
||||
import { BlogController } from './BlogController.js';
|
||||
|
||||
describe(`BlogController`, () => {
|
||||
describe(`Getting all blog posts and book reviews`, () => {
|
||||
let controller: BlogController;
|
||||
|
||||
beforeEach(async () => {
|
||||
controller = await BlogController.singleton();
|
||||
});
|
||||
|
||||
describe(`Getting all blog posts and book reviews`, () => {
|
||||
it(`should load blogs from the content folder`, async () => {
|
||||
// GIVEN
|
||||
const blogPosts = await controller.getAllBlogPosts();
|
||||
|
|
@ -24,4 +24,51 @@ describe(`BlogController`, () => {
|
|||
expect(aKnownBookReview).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe(`Finding a blog post or book review by slug`, () => {
|
||||
describe(`Finding a blog post`, () => {
|
||||
// GIVEN
|
||||
const slugForRealBlogPost = '2023-02-03-vibe-check-10';
|
||||
const slugForFakeBlogPost = 'some-made-up-blog-post';
|
||||
|
||||
it(`should return null if there's no blog post with the slug`, async () => {
|
||||
// WHEN
|
||||
const blogPost = await controller.getBlogOrBookReviewBySlug(slugForFakeBlogPost);
|
||||
|
||||
// THEN
|
||||
expect(blogPost).toBeNull();
|
||||
});
|
||||
|
||||
it(`should return the blog post if it exists`, async () => {
|
||||
// WHEN
|
||||
const blogPost = await controller.getBlogOrBookReviewBySlug(slugForRealBlogPost);
|
||||
|
||||
// THEN
|
||||
expect(blogPost).not.toBeNull();
|
||||
expect(blogPost.title).toBe('Vibe Check #10');
|
||||
});
|
||||
});
|
||||
|
||||
describe(`Finding a book review`, () => {
|
||||
const realSlug = 'after';
|
||||
const fakeSlug = 'some-made-up-book-review';
|
||||
|
||||
it(`should return null if there's no book review with the slug`, async () => {
|
||||
// WHEN
|
||||
const bookReview = await controller.getBlogOrBookReviewBySlug(fakeSlug);
|
||||
|
||||
// THEN
|
||||
expect(bookReview).toBeNull();
|
||||
});
|
||||
|
||||
it(`should return the book review if it exists`, async () => {
|
||||
// WHEN
|
||||
const bookReview = await controller.getBlogOrBookReviewBySlug(realSlug);
|
||||
|
||||
// THEN
|
||||
expect(bookReview).not.toBeNull();
|
||||
expect(bookReview.title).toBe('After');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { BlogPost } from './BlogPost.js';
|
||||
import type { BookReview } from './BookReview.js';
|
||||
import { MarkdownRepository } from './markdown-repository.js';
|
||||
|
||||
const blogPostMetaGlobImport = import.meta.glob('../../content/blog/*.md', { as: 'raw' });
|
||||
|
|
@ -22,6 +24,7 @@ interface BookReviewListItem {
|
|||
score: number;
|
||||
finished: string;
|
||||
date: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export class BlogController {
|
||||
|
|
@ -37,22 +40,21 @@ export class BlogController {
|
|||
|
||||
async getAllBlogPosts(): Promise<Array<BlogPostListItem | BookReviewListItem>> {
|
||||
const blogPosts = await this.markdownRepository.blogPosts;
|
||||
|
||||
const bookReviews = await this.markdownRepository.bookReviews;
|
||||
await blogPosts.buildAllBlogPosts();
|
||||
|
||||
const blogPostListItems: BlogPostListItem[] = blogPosts.blogPosts.map((blogPost) => {
|
||||
return {
|
||||
title: blogPost.title,
|
||||
author: blogPost.author,
|
||||
book_review: false,
|
||||
content: blogPost.html,
|
||||
date: blogPost.date.toISOString(),
|
||||
preview: blogPost.excerpt,
|
||||
slug: blogPost.slug,
|
||||
};
|
||||
return this.blogPostToBlogPostListItem(blogPost);
|
||||
});
|
||||
|
||||
const bookReviewListItems: BookReviewListItem[] = bookReviews.bookReviews.map((bookReview) => {
|
||||
return this.bookReviewToBookReviewListItem(bookReview);
|
||||
});
|
||||
|
||||
return [...blogPostListItems, ...bookReviewListItems].sort((a, b) => (a.date > b.date ? -1 : 1));
|
||||
}
|
||||
|
||||
private bookReviewToBookReviewListItem(bookReview: BookReview, includeHtml = false): BookReviewListItem {
|
||||
return {
|
||||
book_review: true,
|
||||
title: bookReview.title,
|
||||
|
|
@ -62,9 +64,33 @@ export class BlogController {
|
|||
image: bookReview.image,
|
||||
score: bookReview.score,
|
||||
slug: bookReview.slug,
|
||||
content: includeHtml ? bookReview.html : '',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return [...blogPostListItems, ...bookReviewListItems].sort((a, b) => (a.date > b.date ? -1 : 1));
|
||||
private blogPostToBlogPostListItem(blogPost: BlogPost, includeHtml = false): BlogPostListItem {
|
||||
return {
|
||||
title: blogPost.title,
|
||||
author: blogPost.author,
|
||||
book_review: false,
|
||||
content: includeHtml ? blogPost.html : '',
|
||||
date: blogPost.date.toISOString(),
|
||||
preview: blogPost.excerpt,
|
||||
slug: blogPost.slug,
|
||||
};
|
||||
}
|
||||
|
||||
async getBlogOrBookReviewBySlug(slug: string): Promise<BookReviewListItem | BlogPostListItem | null> {
|
||||
const blogPost = await this.markdownRepository.getBlogPostBySlug(slug);
|
||||
if (blogPost) {
|
||||
return this.blogPostToBlogPostListItem(blogPost, true);
|
||||
}
|
||||
|
||||
const bookReview = await this.markdownRepository.getBookReviewBySlug(slug);
|
||||
if (bookReview) {
|
||||
return this.bookReviewToBookReviewListItem(bookReview, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,21 @@ import { describe, it, expect } from 'vitest';
|
|||
import { BookReview } from './BookReview.js';
|
||||
import { aBookReview } from './test-builders/book-review-builder.js';
|
||||
|
||||
const exampleBookReview = `---
|
||||
title: "After"
|
||||
author: "Dr Bruce Greyson"
|
||||
score: 3.5
|
||||
image: "after"
|
||||
slug: "after"
|
||||
book_review: true
|
||||
date: 2021-05-05
|
||||
finished: 2021-04-20
|
||||
draft: false
|
||||
---
|
||||
|
||||
This [link](https://www.example.com) a book review written in *markdown*.
|
||||
`;
|
||||
|
||||
describe(`BookReview`, () => {
|
||||
it(`should construct`, () => {
|
||||
// GIVEN
|
||||
|
|
@ -15,6 +30,7 @@ describe(`BookReview`, () => {
|
|||
date: new Date('2021-05-05'),
|
||||
finished: new Date('2021-04-20'),
|
||||
draft: false,
|
||||
markdownContent: exampleBookReview,
|
||||
});
|
||||
|
||||
// WHEN
|
||||
|
|
@ -26,9 +42,31 @@ describe(`BookReview`, () => {
|
|||
.withSlug('after')
|
||||
.withDate(new Date('2021-05-05'))
|
||||
.withFinished(new Date('2021-04-20'))
|
||||
.withMarkdownContent(exampleBookReview)
|
||||
.build();
|
||||
|
||||
// THEN
|
||||
expect(bookReview).toEqual(expectedBookReview);
|
||||
});
|
||||
|
||||
it(`should build the HTML`, async () => {
|
||||
// GIVEN
|
||||
const bookReview = aBookReview().withMarkdownContent(exampleBookReview).build();
|
||||
|
||||
// WHEN
|
||||
await bookReview.build();
|
||||
|
||||
// THEN
|
||||
expect(bookReview.html).toEqual(
|
||||
'<p>This <a href="https://www.example.com">link</a> a book review written in <em>markdown</em>.</p>'
|
||||
);
|
||||
});
|
||||
|
||||
it(`should not have the HTML built by default`, () => {
|
||||
// GIVEN
|
||||
const bookReview = aBookReview().withMarkdownContent(exampleBookReview).build();
|
||||
|
||||
// WHEN/THEN
|
||||
expect(bookReview.html).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
import type { Processor } from 'unified';
|
||||
import { unified } from 'unified';
|
||||
import markdown from 'remark-parse';
|
||||
import markdownFrontmatter from 'remark-frontmatter';
|
||||
import remarkStringify from 'remark-stringify';
|
||||
import remarkRehype from 'remark-rehype';
|
||||
import rehypeStringify from 'rehype-stringify';
|
||||
|
||||
interface BookReviewProps {
|
||||
title: string;
|
||||
author: string;
|
||||
|
|
@ -7,6 +15,7 @@ interface BookReviewProps {
|
|||
date: Date;
|
||||
finished: Date;
|
||||
draft: boolean;
|
||||
markdownContent: string;
|
||||
}
|
||||
|
||||
export class BookReview {
|
||||
|
|
@ -17,6 +26,8 @@ export class BookReview {
|
|||
readonly slug: string;
|
||||
readonly date: Date;
|
||||
readonly finished: Date;
|
||||
private readonly markdownContent: string;
|
||||
private _html: string | null = null;
|
||||
|
||||
constructor(props: BookReviewProps) {
|
||||
this.title = props.title;
|
||||
|
|
@ -26,5 +37,33 @@ export class BookReview {
|
|||
this.slug = props.slug;
|
||||
this.date = props.date;
|
||||
this.finished = props.finished;
|
||||
this.markdownContent = props.markdownContent;
|
||||
}
|
||||
|
||||
private htmlProcessorFactory(): Processor {
|
||||
return unified() //
|
||||
.use(markdown)
|
||||
.use(markdownFrontmatter)
|
||||
.use(remarkStringify)
|
||||
.use(remarkRehype)
|
||||
.use(rehypeStringify);
|
||||
}
|
||||
|
||||
async build(): Promise<void> {
|
||||
await this.getHtml();
|
||||
}
|
||||
|
||||
async getHtml(): Promise<string> {
|
||||
if (this._html === null) {
|
||||
const processor = this.htmlProcessorFactory();
|
||||
const value = await processor.process(this.markdownContent);
|
||||
this._html = value.toString();
|
||||
}
|
||||
|
||||
return this._html;
|
||||
}
|
||||
|
||||
get html(): string | null {
|
||||
return this._html;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,4 +14,18 @@ describe(`BookReviewSet`, () => {
|
|||
// THEN
|
||||
expect(bookReviewSet.bookReviews).toStrictEqual([bookReview]);
|
||||
});
|
||||
|
||||
it(`should build all the HTML contents`, async () => {
|
||||
// GIVEN
|
||||
const bookReview = aBookReview().withTitle(`The title`).withMarkdownContent('test').build();
|
||||
const anotherBookReview = aBookReview().withTitle(`Another title`).withMarkdownContent('test').build();
|
||||
const bookReviewSet = new BookReviewSet([bookReview, anotherBookReview]);
|
||||
|
||||
// WHEN
|
||||
await bookReviewSet.buildAllBookReviews();
|
||||
|
||||
// THEN
|
||||
expect(bookReview.html).not.toBeNull();
|
||||
expect(anotherBookReview.html).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BookReview } from './BookReview.js';
|
||||
import type { BookReview } from './BookReview.js';
|
||||
|
||||
export class BookReviewSet {
|
||||
private _bookReviews: BookReview[] = [];
|
||||
|
|
@ -7,6 +7,11 @@ export class BookReviewSet {
|
|||
this._bookReviews = bookReviews;
|
||||
}
|
||||
|
||||
async buildAllBookReviews(): Promise<void> {
|
||||
const bookReviewPromises = this._bookReviews.map((bookReview) => bookReview.build());
|
||||
await Promise.all(bookReviewPromises);
|
||||
}
|
||||
|
||||
get bookReviews(): BookReview[] {
|
||||
return this._bookReviews;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import { MarkdownRepository } from './markdown-repository.js';
|
||||
|
||||
import { MarkdownFile } from './MarkdownFile.js';
|
||||
|
|
@ -25,13 +25,13 @@ describe(`Blog MarkdownRepository`, () => {
|
|||
// GIVEN
|
||||
const repository = await MarkdownRepository.fromViteGlobImport(blogPostImport, bookReviewImport);
|
||||
|
||||
const expectedBlogPost = aBlogPost()
|
||||
const expectedBlogPost = await aBlogPost()
|
||||
.withAuthor('Thomas Wilson')
|
||||
.withDate(new Date('2023-02-01T08:00:00Z'))
|
||||
.withSlug('2023-02-01-test')
|
||||
.withTitle('Test Blog Post')
|
||||
.withMarkdownContent(testMarkdownContent)
|
||||
.build();
|
||||
.constructAndThenBuild();
|
||||
|
||||
// WHEN
|
||||
const blogPost = repository.blogPosts.getBlogPostWithTitle('Test Blog Post');
|
||||
|
|
@ -40,4 +40,29 @@ describe(`Blog MarkdownRepository`, () => {
|
|||
expect(repository).toBeDefined();
|
||||
expect(blogPost).toStrictEqual(expectedBlogPost);
|
||||
});
|
||||
|
||||
it(`should automatically build all the blog posts and book reviews`, async () => {
|
||||
// GIVEN
|
||||
const repository = await MarkdownRepository.fromViteGlobImport(blogPostImport, bookReviewImport);
|
||||
|
||||
// WHEN/THEN
|
||||
expect(repository.blogPosts.blogPosts[0].html).not.toBeNull();
|
||||
expect(repository.bookReviews.bookReviews[0].html).not.toBeNull();
|
||||
});
|
||||
|
||||
describe(`Finding by Slug`, () => {
|
||||
let repository: MarkdownRepository;
|
||||
|
||||
beforeAll(async () => {
|
||||
repository = await MarkdownRepository.fromViteGlobImport(blogPostImport, bookReviewImport);
|
||||
});
|
||||
|
||||
it(`should return null if there's neither a blog post nor a book review with the slug`, async () => {
|
||||
// WHEN
|
||||
const markdownFile = repository.getBlogPostBySlug('non-existent-slug');
|
||||
|
||||
// THEN
|
||||
expect(markdownFile).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ export class MarkdownRepository {
|
|||
finished: markdownFile.frontmatter.finished,
|
||||
image: markdownFile.frontmatter.image,
|
||||
score: markdownFile.frontmatter.score,
|
||||
markdownContent: markdownFile.content,
|
||||
});
|
||||
|
||||
bookReviews = [...bookReviews, bookReview];
|
||||
|
|
@ -94,6 +95,21 @@ export class MarkdownRepository {
|
|||
}
|
||||
}
|
||||
|
||||
return new MarkdownRepository(blogPosts, bookReviews);
|
||||
const repository = new MarkdownRepository(blogPosts, bookReviews);
|
||||
await repository.buildAll();
|
||||
return repository;
|
||||
}
|
||||
|
||||
private async buildAll() {
|
||||
await Promise.all([this.blogPosts.buildAllBlogPosts(), this.bookReviews.buildAllBookReviews()]);
|
||||
return;
|
||||
}
|
||||
|
||||
getBlogPostBySlug(slug: string): BlogPost | null {
|
||||
return this.blogPosts.blogPosts.find((blogPost) => blogPost.slug === slug) ?? null;
|
||||
}
|
||||
|
||||
getBookReviewBySlug(slug: string): BookReview | null {
|
||||
return this.bookReviews.bookReviews.find((bookReview) => bookReview.slug === slug) ?? null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class BookReviewBuilder {
|
|||
private image = 'default image';
|
||||
private score = 0;
|
||||
private slug = 'default slug';
|
||||
private markdownContent = 'default markdown content';
|
||||
|
||||
withTitle(title: string): BookReviewBuilder {
|
||||
this.title = title;
|
||||
|
|
@ -50,6 +51,11 @@ class BookReviewBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
withMarkdownContent(markdownContent: string): BookReviewBuilder {
|
||||
this.markdownContent = markdownContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): BookReview {
|
||||
return new BookReview({
|
||||
title: this.title,
|
||||
|
|
@ -60,6 +66,7 @@ class BookReviewBuilder {
|
|||
image: this.image,
|
||||
score: this.score,
|
||||
slug: this.slug,
|
||||
markdownContent: this.markdownContent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { json, type LoadEvent, error } from '@sveltejs/kit';
|
||||
import { fetchBlogPostBySlug } from '$lib';
|
||||
import { BlogController } from '../../../../lib/blog/BlogController.js';
|
||||
|
||||
export const GET = async ({ params }: LoadEvent) => {
|
||||
// const controller = await BlogController.singleton();
|
||||
const controller = await BlogController.singleton();
|
||||
const { slug } = params;
|
||||
|
||||
const post = await fetchBlogPostBySlug(slug);
|
||||
const post = await controller.getBlogOrBookReviewBySlug(slug);
|
||||
|
||||
if (!post) {
|
||||
throw error(404, `Could not find blog post with slug '${slug}'`);
|
||||
|
|
|
|||
6
src/routes/blog/[slug]/+page.server.ts
Normal file
6
src/routes/blog/[slug]/+page.server.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Nearly four years ago I started writing blog posts in markdown.
|
||||
// Now I've got a bunch of ragtag files, so pre-rendering them
|
||||
// requires some Markdown parsing that I've not done yet.
|
||||
// I'd love to get tis to be `true`
|
||||
// 2023-02-06 Wilson
|
||||
export const prerender = false;
|
||||
8
src/routes/book-reviews/[slug]/+page.server.ts
Normal file
8
src/routes/book-reviews/[slug]/+page.server.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import type { LoadEvent } from '@sveltejs/kit';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ params }: LoadEvent) {
|
||||
const { slug } = params;
|
||||
// redirect to 404 if post not found
|
||||
throw redirect(307, `/blog/${slug}`);
|
||||
}
|
||||
Loading…
Reference in a new issue