Skip to content

Commit 1d0719b

Browse files
authored
feat: load the sidebar data and titles/excerpts from the blog post markdown document and its frontmatter (#2981)
1 parent 1868ad2 commit 1d0719b

12 files changed

Lines changed: 174 additions & 125 deletions

website/.vitepress/blog.data.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { createContentLoader } from 'vitepress';
2+
3+
export interface Post {
4+
title: string;
5+
url: string;
6+
date: {
7+
time: number;
8+
string: string;
9+
};
10+
author: string;
11+
tags: string[];
12+
excerpt?: string;
13+
}
14+
15+
declare const data: Post[];
16+
export { data };
17+
18+
// extractExcerpt renders the whole document first so that links which have
19+
// `[ref]: url` style definitions outside of the excerpt still resolve. The
20+
// rendered HTML is then cut at the `<!-- more -->` marker leaving just the
21+
// excerpt text.
22+
function extractExcerpt(html: string): string | undefined {
23+
const markerIndex = html.indexOf('<!-- more -->');
24+
if (markerIndex === -1) return undefined;
25+
return html
26+
.slice(0, markerIndex)
27+
.replace(/<h1[^>]*>[\s\S]*?<\/h1>/, '')
28+
.replace(/<AuthorCard\b[^>]*\/?>/gi, '')
29+
.trim();
30+
}
31+
32+
export default createContentLoader('blog/*.md', {
33+
render: true,
34+
transform(raw) {
35+
return raw
36+
.filter(({ url }) => url !== '/blog/')
37+
.map(({ frontmatter, html, url }) => {
38+
const date = new Date(frontmatter.date);
39+
return {
40+
title: frontmatter.title,
41+
url,
42+
date: {
43+
time: date.getTime(),
44+
string: date.toISOString().slice(0, 10)
45+
},
46+
author: frontmatter.author,
47+
tags: frontmatter.tags ?? [],
48+
excerpt: html ? extractExcerpt(html) : undefined
49+
};
50+
})
51+
.sort((a, b) => b.date.time - a.date.time);
52+
}
53+
});

website/.vitepress/components/BlogPost.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<div class="post-text">
2121
<AuthorCard :author="author" />
2222

23-
<p class="post-description">{{ description }}</p>
23+
<div class="post-description" v-html="description"></div>
2424

2525
<div class="post-footer">
2626
<div class="post-tags" v-if="tags?.length">

website/.vitepress/config.ts

Lines changed: 36 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { defineConfig, HeadConfig } from 'vitepress';
22
import githubLinksPlugin from './plugins/github-links';
3-
import { readFileSync } from 'fs';
3+
import { readdirSync, readFileSync } from 'fs';
44
import { resolve } from 'path';
5+
import matter from 'gray-matter';
56
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
67
import {
78
groupIconMdPlugin,
@@ -19,6 +20,39 @@ const version = readFileSync(
1920
'utf8'
2021
).trim();
2122

23+
// Builds the "/blog/" sidebar from each blog post's frontmatter.
24+
function buildBlogSidebar() {
25+
const blogDir = resolve(__dirname, '../src/blog');
26+
const posts = readdirSync(blogDir)
27+
.filter((file) => file.endsWith('.md') && file !== 'index.md')
28+
.map((file) => {
29+
const { data: frontmatter } = matter(
30+
readFileSync(resolve(blogDir, file), 'utf8')
31+
);
32+
return {
33+
slug: file.replace(/\.md$/, ''),
34+
title: frontmatter.sidebarTitle ?? frontmatter.title,
35+
date: new Date(frontmatter.date)
36+
};
37+
})
38+
.sort((a, b) => b.date.getTime() - a.date.getTime());
39+
40+
const byYear = new Map<number, { text: string; link: string }[]>();
41+
for (const post of posts) {
42+
const year = post.date.getFullYear();
43+
if (!byYear.has(year)) byYear.set(year, []);
44+
byYear.get(year)!.push({ text: post.title, link: `/blog/${post.slug}` });
45+
}
46+
47+
return [...byYear.entries()]
48+
.sort((a, b) => b[0] - a[0])
49+
.map(([year, items]) => ({
50+
text: String(year),
51+
collapsed: false,
52+
items
53+
}));
54+
}
55+
2256
const urlVersion =
2357
process.env.NODE_ENV === 'development'
2458
? {
@@ -311,56 +345,7 @@ export default defineConfig({
311345
],
312346

313347
sidebar: {
314-
'/blog/': [
315-
{
316-
text: '2026',
317-
collapsed: false,
318-
items: [
319-
{
320-
text: 'GitHub SOSF',
321-
link: '/blog/github-secure-open-source-program'
322-
},
323-
{
324-
text: 'Using `go tool task`',
325-
link: '/blog/go-tool-task'
326-
},
327-
{
328-
text: 'Conditionals Statements',
329-
link: '/blog/if-and-variable-prompt'
330-
}
331-
]
332-
},
333-
{
334-
text: '2025',
335-
collapsed: false,
336-
items: [
337-
{
338-
text: 'Built-in Core Utilities',
339-
link: '/blog/windows-core-utils'
340-
}
341-
]
342-
},
343-
{
344-
text: '2024',
345-
collapsed: false,
346-
items: [
347-
{
348-
text: 'Any Variables',
349-
link: '/blog/any-variables'
350-
}
351-
]
352-
},
353-
{
354-
text: '2023',
355-
collapsed: false,
356-
items: [
357-
{
358-
text: 'Introducing Experiments',
359-
link: '/blog/task-in-2023'
360-
}
361-
]
362-
}
363-
],
348+
'/blog/': buildBlogSidebar(),
364349
'/': [
365350
{
366351
text: 'Installation',

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"devDependencies": {
1616
"@types/markdown-it": "^14.1.2",
1717
"@types/node": "^24.1.0",
18+
"gray-matter": "^4.0.3",
1819
"netlify-cli": "^27.0.0",
1920
"prettier": "^3.6.2",
2021
"vitepress": "^1.6.3",

website/pnpm-lock.yaml

Lines changed: 42 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/src/blog/any-variables.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
---
22
title: Any Variables
3+
description:
4+
Task now supports most variable types, including booleans, integers, floats
5+
and arrays!
36
author: pd93
47
date: 2024-05-09
8+
tags: ['experiments', 'variables']
59
outline: deep
610
editLink: false
711
---
@@ -17,6 +21,8 @@ simple problems. Starting from [v3.37.0][v3.37.0], this is no longer the case!
1721
Task now supports most variable types, including **booleans**, **integers**,
1822
**floats** and **arrays**!
1923

24+
<!-- more -->
25+
2026
## What's the big deal?
2127

2228
These changes allow you to use variables in a much more natural way and opens up

0 commit comments

Comments
 (0)