Skip to content

Commit de9aa8a

Browse files
committed
Initial blog setup
1 parent 2d34256 commit de9aa8a

8 files changed

Lines changed: 211 additions & 0 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Deploy Minimal Blog
2+
3+
on:
4+
push:
5+
branches: ["master", "main"]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repo
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Bun
25+
uses: oven-sh/setup-bun@v2
26+
with:
27+
bun-version: latest
28+
29+
- name: Install dependencies
30+
run: bun install
31+
32+
- name: Build site
33+
run: bun run build
34+
35+
- name: Upload Pages artifact
36+
uses: actions/upload-pages-artifact@v3
37+
with:
38+
path: ./dist
39+
40+
deploy:
41+
environment:
42+
name: github-pages
43+
url: ${{ steps.deployment.outputs.page_url }}
44+
runs-on: ubuntu-latest
45+
needs: build
46+
steps:
47+
- name: Deploy to GitHub Pages
48+
id: deployment
49+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
bun.lockb
4+
bun.lock
5+
.DS_Store

content/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: My First Post
3+
---
4+
# Hello World

package.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "vilhelm",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"build": "tsx src/build.ts"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/visj/visj.github.io.git"
12+
},
13+
"keywords": [],
14+
"author": "",
15+
"license": "ISC",
16+
"type": "commonjs",
17+
"bugs": {
18+
"url": "https://github.com/visj/visj.github.io/issues"
19+
},
20+
"homepage": "https://github.com/visj/visj.github.io#readme",
21+
"dependencies": {
22+
"@shikijs/markdown-exit": "^4.0.2",
23+
"gray-matter": "^4.0.3",
24+
"markdown-exit": "^1.0.0-beta.9",
25+
"shiki": "^4.0.2"
26+
},
27+
"devDependencies": {
28+
"@types/node": "^25.5.0",
29+
"tsx": "^4.21.0",
30+
"typescript": "^5.9.3"
31+
}
32+
}

src/build.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import fs from 'fs/promises';
2+
import path from 'path';
3+
import { createMarkdownExit } from 'markdown-exit';
4+
import Shiki from '@shikijs/markdown-exit';
5+
import matter from 'gray-matter';
6+
7+
const CONTENT_DIR = './content';
8+
const DIST_DIR = './dist';
9+
const TEMPLATE_PATH = './src/template.html';
10+
11+
async function build() {
12+
// 1. Initialize markdown-exit using the factory helper
13+
const md = createMarkdownExit();
14+
15+
// 2. Add Shiki plugin for syntax highlighting
16+
// This will automatically handle tokenizing code blocks asynchronously
17+
md.use(Shiki({
18+
themes: {
19+
light: 'github-light',
20+
dark: 'github-dark',
21+
}
22+
}));
23+
24+
// 3. Prepare directories and load the HTML template
25+
await fs.mkdir(DIST_DIR, { recursive: true });
26+
const template = await fs.readFile(TEMPLATE_PATH, 'utf-8');
27+
28+
// Copy over the global CSS file
29+
await fs.copyFile('./src/style.css', path.join(DIST_DIR, 'style.css'));
30+
31+
// 4. Recursive function to read all markdown files and mirror the folder structure
32+
async function processDirectory(dir: string) {
33+
const entries = await fs.readdir(dir, { withFileTypes: true });
34+
35+
for (const entry of entries) {
36+
const fullPath = path.join(dir, entry.name);
37+
38+
// Get the path relative to the content folder (e.g., "dev/my-post.md")
39+
const relativePath = path.relative(CONTENT_DIR, fullPath);
40+
41+
if (entry.isDirectory()) {
42+
// If it's a folder, create the equivalent folder in /dist
43+
const destDir = path.join(DIST_DIR, relativePath);
44+
await fs.mkdir(destDir, { recursive: true });
45+
await processDirectory(fullPath);
46+
} else if (entry.name.endsWith('.md')) {
47+
// If it's a markdown file, figure out the destination HTML path
48+
const destPath = path.join(DIST_DIR, relativePath).replace(/\.md$/, '.html');
49+
const fileContent = await fs.readFile(fullPath, 'utf-8');
50+
51+
// Parse YAML frontmatter (like title) and the raw markdown body
52+
const { data, content } = matter(fileContent);
53+
const title = data.title || 'My Blog';
54+
55+
// Render markdown asynchronously (required for the Shiki plugin to work)
56+
const htmlContent = await md.renderAsync(content);
57+
58+
// Calculate correct relative path for the CSS file based on folder depth
59+
const depth = relativePath.split(path.sep).length - 1;
60+
const cssPath = depth === 0 ? './style.css' : '../'.repeat(depth) + 'style.css';
61+
62+
// Inject everything into the template shell
63+
const finalHtml = template
64+
.replace('{{TITLE}}', title)
65+
.replace('{{CSS_PATH}}', cssPath)
66+
.replace('{{CONTENT}}', htmlContent);
67+
68+
// Write the compiled HTML file
69+
await fs.writeFile(destPath, finalHtml);
70+
console.log(`Built: ${destPath}`);
71+
}
72+
}
73+
}
74+
75+
// Kick off the build process
76+
await processDirectory(CONTENT_DIR);
77+
console.log('✅ Build complete!');
78+
}
79+
80+
build().catch(console.error);

src/style.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
body {
2+
font-family: system-ui, -apple-system, sans-serif;
3+
line-height: 1.6;
4+
max-width: 800px;
5+
margin: 0 auto;
6+
padding: 2rem;
7+
color: #333;
8+
background: #fafafa;
9+
}
10+
11+
/* Ensure code blocks have padding and scroll horizontally */
12+
pre.shiki {
13+
padding: 1rem;
14+
border-radius: 6px;
15+
overflow-x: auto;
16+
}

src/template.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>{{TITLE}}</title>
7+
<link rel="stylesheet" href="{{CSS_PATH}}">
8+
</head>
9+
<body>
10+
<nav>
11+
<a href="/">Home</a>
12+
</nav>
13+
<main>
14+
{{CONTENT}}
15+
</main>
16+
</body>
17+
</html>

tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"moduleResolution": "Node",
5+
"esModuleInterop": true,
6+
"strict": true
7+
}
8+
}

0 commit comments

Comments
 (0)