Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanThatOneKid committed Apr 24, 2024
1 parent a92843c commit b4b4ddf
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 0 deletions.
1 change: 1 addition & 0 deletions blog/hello-world.md
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
---

Expand Down
5 changes: 5 additions & 0 deletions codegen/html.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { LandingPage } from "#/components/landing_page/mod.ts";
import { BlogPage, posts } from "#/components/blog_page/mod.ts";

export function generateHTML() {
const landingPageHTML = <LandingPage /> as string;
Deno.writeTextFileSync("generated/index.html", landingPageHTML);

const blogPageHTML = <BlogPage posts={posts} /> as string;
Deno.mkdirSync("generated/blog", { recursive: true });
Deno.writeTextFileSync("generated/blog/index.html", blogPageHTML);

// TODO: Generate blog page, post pages, and label filtered pages.
}

Expand Down
18 changes: 18 additions & 0 deletions components/blog_page/blog_hero_section.tsx
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>
);
}
41 changes: 41 additions & 0 deletions components/blog_page/blog_page.tsx
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>
);
}
24 changes: 24 additions & 0 deletions components/blog_page/blog_topics.tsx
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>;
}
56 changes: 56 additions & 0 deletions components/blog_page/data.ts
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);
}
37 changes: 37 additions & 0 deletions components/blog_page/markdown.ts
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: "🧪",
});
2 changes: 2 additions & 0 deletions components/blog_page/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./blog_page.tsx";
export * from "./data.ts";
41 changes: 41 additions & 0 deletions components/blog_page/posts.ts
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(" ", "-");
}
4 changes: 4 additions & 0 deletions components/page_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export function PageNav() {
<A class="page-nav-header page-link-visible-on-hover" href="/">
FartLabs
</A>

<A class="page-nav-button" href="/blog">
Blog
</A>
</NAV>
);
}
4 changes: 4 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
"#/": "./",
"@fartlabs/htx": "jsr:@fartlabs/htx@^0.0.3",
"@fartlabs/jsonx": "jsr:@fartlabs/jsonx@^0.0.10",
"@std/front-matter": "jsr:@std/front-matter@^0.223.0",
"@std/fs": "jsr:@std/fs@^0.223.0",
"@std/http": "jsr:@std/http@^0.223.0",
"alea": "npm:alea@^1.0.1",
"highlight.js": "npm:highlight.js@^11.9.0",
"markdown-it": "npm:markdown-it@^14.1.0",
"markdown-it-anchor": "npm:markdown-it-anchor@^8.6.7",
"simplex-noise": "npm:simplex-noise@^4.0.1"
},
"tasks": {
Expand Down

0 comments on commit b4b4ddf

Please sign in to comment.