Skip to content

Commit

Permalink
Cleanup blog engine:
Browse files Browse the repository at this point in the history
 - Posts into typescript
 - Post type into non-global
 - Remove repeated date parsing code
 - Remove repeated sorting code
 - Proper 404 errors on bad urls
  • Loading branch information
scosman committed Feb 29, 2024
1 parent fef6d70 commit 66c3fdb
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 86 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,13 @@ After the steps above, you’ll have a working version like the demo page. Howev
- Content
- Add actual content for marketing homepage
- Add actual content for your blog (or delete the blog)
- Update all fields in `src/routes/(marketing)/blog/posts.json`, and replace the post pages under `src/routes/(marketing)/blog/posts` to align to the urls from posts.json.
- Update all fields in `src/routes/(marketing)/blog/posts.ts`, and replace the post pages under `src/routes/(marketing)/blog/posts` to align to the urls from `posts.ts`.
- Alternatively remove the blog by removing the src/routes/(marketing)/blog directory, and remove any links to the blog in the header and footer. You can always bring it back later.
- Add any pages you want on top of our boiler plate (about, terms of service, etc). Be sure to add links to them in the header, mobile menu header, and footer as appropriate (`src/routes/(marketing)/+layout.svelte`).
- Note: if you add any dynamic content to the main marketing page, pricing page or blog, be sure to set `prerender = false` in the appropriate `+page.ts` file. These are currently pre-rendered and served as static assets for performance reasons, but that will break if you add server side rendering requirements.
- Update SEO content
- Update title and meta description tags for every public page. We include generic ones using your site name (PUBLIC_SITE_NAME), but the more specific these are the better.
- This done automatically for blog posts from `posts.json` metadata
- This done automatically for blog posts from `posts.ts` metadata
- Style
- Create a new DaisyUI Theme matching your brand or use one of the built in themes from DaisyUI (see `tailwind.config.js`)
- Update the marketing page layout `src/routes/(marketing)/+layout.svelte`: customize design, delete unwanted pages from header and footer
Expand Down
8 changes: 0 additions & 8 deletions src/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ declare global {
website?: string
email?: string
}

type Post = {
link: string
date: string // date is a string 'YYYY-MM-DD'
title: string
description: string
parsedDate?: Date // Optional because it's added dynamically
}
}

export {}
20 changes: 6 additions & 14 deletions src/routes/(marketing)/blog/(posts)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script lang="ts">
import { page } from "$app/stores"
import { postList } from "./../posts.json"
import { error } from "@sveltejs/kit"
import { sortedBlogPosts, type BlogPost } from "./../posts"
let currentPost: Post | null = null
for (const post of postList) {
let currentPost: BlogPost | null = null
for (const post of sortedBlogPosts) {
if (
$page.url.pathname == post.link ||
$page.url.pathname == post.link + "/"
Expand All @@ -12,17 +13,8 @@
continue
}
}
if (currentPost != null) {
let dateParts = currentPost.date.split("-")
currentPost.parsedDate = new Date(
parseInt(dateParts[0]),
parseInt(dateParts[1]) - 1,
parseInt(dateParts[2]),
) // Note: months are 0-based
} else {
console.log(
"WARNING: rendering blog post, which is not listed in posts.json",
)
if (!currentPost) {
throw error(404, "Blog post not found")
}
</script>

Expand Down
20 changes: 2 additions & 18 deletions src/routes/(marketing)/blog/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
<script lang="ts">
import { postList, blogInfo } from "./posts.json"
const typedPostList: Post[] = postList as Post[]
for (const post of typedPostList) {
let dateParts = post.date.split("-")
post.parsedDate = new Date(
parseInt(dateParts[0]),
parseInt(dateParts[1]) - 1,
parseInt(dateParts[2]),
) // Note: months are 0-based
}
let sortedPosts = typedPostList.sort((a, b) => {
const dateA = a.parsedDate ? a.parsedDate.getTime() : 0
const dateB = b.parsedDate ? b.parsedDate.getTime() : 0
return dateB - dateA
})
import { sortedBlogPosts, blogInfo } from "./posts"
</script>

<svelte:head>
Expand All @@ -42,7 +26,7 @@
</div>
<div class="text-lg text-center">A demo blog with sample content.</div>

{#each sortedPosts as post}
{#each sortedBlogPosts as post}
<a href={post.link}>
<div class="card my-6 bg-white shadow-xl flex-row overflow-hidden">
<div class="flex-none w-6 md:w-32 bg-secondary"></div>
Expand Down
26 changes: 0 additions & 26 deletions src/routes/(marketing)/blog/posts.json

This file was deleted.

52 changes: 52 additions & 0 deletions src/routes/(marketing)/blog/posts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export const blogInfo = {
name: "SaaS Starter Blog",
description: "A sample blog",
}

export type BlogPost = {
link: string
date: string // date is a string 'YYYY-MM-DD'
title: string
description: string
parsedDate?: Date // Optional because it's added dynamically
}

// Update this list with the actual blog post list
// Create a page in the "(posts)" directory for each entry
const blogPosts: BlogPost[] = [
{
title: "Example Blog Post 2",
description: "Even more example content!",
link: "/blog/awesome_post",
date: "2022-9-23",
},
{
title: "How to build a SaaS Webpage in 12 easy steps",
description: "A getting started guide for saas websites.",
link: "/blog/how_to_build_saas_page",
date: "2022-12-26",
},
{
title: "Example Blog Post",
description: "A sample blog post, showing our blog engine",
link: "/blog/example_blog_post",
date: "2023-03-13",
},
]

// Parse post dates from strings to Date objects
for (const post of blogPosts) {
if (!post.parsedDate) {
const dateParts = post.date.split("-")
post.parsedDate = new Date(
parseInt(dateParts[0]),
parseInt(dateParts[1]) - 1,
parseInt(dateParts[2]),
) // Note: months are 0-based
}
}

export const sortedBlogPosts = blogPosts.sort(
(a: BlogPost, b: BlogPost) =>
(b.parsedDate?.getTime() ?? 0) - (a.parsedDate?.getTime() ?? 0),
)
20 changes: 2 additions & 18 deletions src/routes/(marketing)/blog/rss.xml/+server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { postList, blogInfo } from "../posts.json"

const typedPostList: Post[] = postList as Post[]
import { sortedBlogPosts, blogInfo } from "../posts"

export const prerender = true

Expand All @@ -25,21 +23,7 @@ export function GET({ url }) {
<link>${url.origin}/blog</link>
<description>${blogInfo.description}</description>
<atom:link href="${url.origin}/blog/rss.xml" rel="self" type="application/rss+xml" />`
for (const post of typedPostList) {
const dateParts = post.date.split("-")
post.parsedDate = new Date(
parseInt(dateParts[0]),
parseInt(dateParts[1]) - 1,
parseInt(dateParts[2]),
) // Note: months are 0-based
}
const sortedPosts = typedPostList.sort((a, b) => {
const parsedDateA = a.parsedDate || new Date(0)
const parsedDateB = b.parsedDate || new Date(0)
return parsedDateB.getTime() - parsedDateA.getTime()
})

for (const post of sortedPosts) {
for (const post of sortedBlogPosts) {
body += `
<item>
<title>${encodeXML(post.title)}</title>
Expand Down

0 comments on commit 66c3fdb

Please sign in to comment.