-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a92843c
commit b4b4ddf
Showing
11 changed files
with
233 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
--- | ||
title: Hello World! | ||
author: EthanThatOneKid | ||
topics: ["deno"] | ||
date: 2024-04-23 | ||
--- | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { A, H2 } from "@fartlabs/htx"; | ||
import { PageSection } from "#/components/page_section.tsx"; | ||
import { BlogTopics } from "./blog_topics.tsx"; | ||
import { topics } from "./data.ts"; | ||
|
||
export function BlogHeroSection() { | ||
return ( | ||
<PageSection class="blog-hero"> | ||
<H2 id="blog" class="page-heading"> | ||
<A class="page-link-visible-on-hover" href="#blog"> | ||
Blog | ||
</A> | ||
</H2> | ||
|
||
<BlogTopics topics={topics} /> | ||
</PageSection> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { A, DIV, H2, H3, P } from "@fartlabs/htx"; | ||
import { PageLayout } from "#/components/page_layout.tsx"; | ||
import { PageSection } from "#/components/page_section.tsx"; | ||
import type { Post } from "./posts.ts"; | ||
import { BlogHeroSection } from "./blog_hero_section.tsx"; | ||
import { BlogTopics } from "./blog_topics.tsx"; | ||
|
||
export interface BlogPageProps { | ||
posts: Post[]; | ||
} | ||
|
||
export function BlogPage(props: BlogPageProps) { | ||
return ( | ||
<PageLayout | ||
title="Blog | FartLabs, where imagination becomes software" | ||
description="Software out the wazoo! We specialize in imagination-driven development." | ||
> | ||
<BlogHeroSection /> | ||
|
||
<PageSection> | ||
{props.posts | ||
.map((post) => <PostPreview post={post} />) | ||
.join("\n")} | ||
</PageSection> | ||
</PageLayout> | ||
); | ||
} | ||
|
||
function PostPreview(props: { post: Post }) { | ||
return ( | ||
<DIV class="post-preview"> | ||
<H3 class="page-heading-2"> | ||
<A class="page-link-visible-on-hover" href={`/${props.post.id}`}> | ||
{props.post.attrs.title} | ||
</A> | ||
</H3> | ||
<P>{props.post.attrs.description}</P> | ||
<BlogTopics topics={props.post.attrs.topics} /> | ||
</DIV> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { A, DIV } from "@fartlabs/htx"; | ||
import { toTopicID } from "./posts.ts"; | ||
|
||
export interface BlogTopicsProps { | ||
topics: string[]; | ||
} | ||
|
||
export function BlogTopics(props: BlogTopicsProps) { | ||
return ( | ||
<DIV class="topics"> | ||
{props.topics | ||
.map((topic) => <BlogTopic topic={topic} />) | ||
.join("")} | ||
</DIV> | ||
); | ||
} | ||
|
||
export interface BlogTopicProps { | ||
topic: string; | ||
} | ||
|
||
export function BlogTopic(props: BlogTopicProps) { | ||
return <A href={`/blog/${toTopicID(props.topic)}`}>{props.topic}</A>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { test } from "@std/front-matter"; | ||
import { extract } from "@std/front-matter/any"; | ||
import { expandGlobSync } from "@std/fs"; | ||
import type { Post, PostAttrs } from "./posts.ts"; | ||
import { renderHTML } from "./markdown.ts"; | ||
|
||
export const posts = readPosts(); | ||
export const topics = topicsOf(posts); | ||
|
||
/** | ||
* readPosts reads all the blog posts. | ||
*/ | ||
function readPosts(): Post[] { | ||
const posts: Post[] = []; | ||
const it = expandGlobSync("blog/*.md"); | ||
for (const file of it) { | ||
const md = Deno.readTextFileSync(file.path); | ||
if (!test(md)) { | ||
throw new Error(`invalid front matter in ${file.path}`); | ||
} | ||
|
||
const extracted = extract<PostAttrs>(md); | ||
const post: Post = { | ||
id: file.name, | ||
attrs: extracted.attrs, | ||
html: renderHTML(extracted.body), | ||
}; | ||
posts.push(post); | ||
} | ||
|
||
return posts | ||
.toSorted((a, b) => a.attrs.date.localeCompare(b.attrs.date)); | ||
} | ||
|
||
/** | ||
* constTopics counts the topics. | ||
*/ | ||
function countTopics(posts: Post[]): Map<string, number> { | ||
const topics = new Map<string, number>(); | ||
for (const post of posts) { | ||
for (const topic of post.attrs.topics) { | ||
topics.set(topic, (topics.get(topic) ?? 0) + 1); | ||
} | ||
} | ||
|
||
return topics; | ||
} | ||
|
||
/** | ||
* topicsOf returns the topics of the blog posts sorted by frequency. | ||
*/ | ||
function topicsOf(posts: Post[]): string[] { | ||
return Array.from(countTopics(posts).entries()) | ||
.toSorted(([, a], [, b]) => b - a) | ||
.map(([topic]) => topic); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import MarkdownIt from "markdown-it"; | ||
import anchorPlugin from "markdown-it-anchor"; | ||
import hljs from "highlight.js"; | ||
|
||
/** | ||
* renderHTML renders HTML from markdown content. | ||
*/ | ||
export function renderHTML(md: string): string { | ||
return renderer.render(md); | ||
} | ||
|
||
/** | ||
* renderer is the markdown renderer used for rendering markdown content. | ||
* | ||
* @see | ||
* https://github.com/markdown-it/markdown-it/blob/0fe7ccb4b7f30236fb05f623be6924961d296d3d/README.md?plain=1#L154 | ||
*/ | ||
const renderer: MarkdownIt = new MarkdownIt({ | ||
html: true, | ||
linkify: true, | ||
typographer: true, | ||
highlight(content: string, language?: string) { | ||
const html = language && hljs.getLanguage(language) | ||
? hljs.highlight( | ||
content, | ||
{ language, ignoreIllegals: true }, | ||
).value | ||
: renderer.utils.escapeHtml(content); | ||
return `<pre data-lang="${language}"><code class="hljs">${html}</code></pre>`; | ||
}, | ||
}); | ||
|
||
renderer.use(anchorPlugin, { | ||
permalink: true, | ||
permalinkBefore: true, | ||
permalinkSymbol: "🧪", | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./blog_page.tsx"; | ||
export * from "./data.ts"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* Post represents a blog post. | ||
*/ | ||
export interface Post { | ||
id: string; | ||
attrs: PostAttrs; | ||
html: string; | ||
} | ||
|
||
/** | ||
* PostAttrs represents a blog post's attributes. | ||
*/ | ||
export interface PostAttrs { | ||
title: string; | ||
description: string; | ||
authors: PostAuthor[]; | ||
topics: string[]; | ||
date: string; | ||
} | ||
|
||
/** | ||
* PostAuthor represents the author of a post. | ||
*/ | ||
export interface PostAuthor { | ||
name: string; | ||
username: string; | ||
} | ||
|
||
/** | ||
* toTopicIDs converts topic strings to topic IDs. | ||
*/ | ||
export function toTopicIDs(topics: string[]) { | ||
return topics.map((topic) => toTopicID(topic)); | ||
} | ||
|
||
/** | ||
* toTopicID converts a topic string to a topic ID. | ||
*/ | ||
export function toTopicID(topic: string) { | ||
return topic.toLowerCase().replace(" ", "-"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters