-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstatic-site-generator.js
More file actions
110 lines (99 loc) · 3.64 KB
/
static-site-generator.js
File metadata and controls
110 lines (99 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Minimal static site generator using DocForge API
// Converts a folder of .md files to .html pages
// Usage: mkdir content && echo "# Hello" > content/index.md && node static-site-generator.js
import { readdir, readFile, writeFile, mkdir } from 'fs/promises';
import { join, parse } from 'path';
const INPUT_DIR = './content';
const OUTPUT_DIR = './dist';
const SITE_TITLE = 'My Site';
async function convertMarkdown(markdown) {
const res = await fetch('https://docforge-api.vercel.app/api/md-to-html', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ markdown })
});
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}
function wrapInLayout(html, title) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title} — ${SITE_TITLE}</title>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
max-width: 720px;
margin: 0 auto;
padding: 2rem 1rem;
color: #1a1a1a;
line-height: 1.7;
}
h1 { font-size: 2rem; margin-bottom: 0.5rem; letter-spacing: -0.5px; }
h2 { font-size: 1.4rem; margin: 1.5rem 0 0.5rem; }
h3 { font-size: 1.1rem; margin: 1.2rem 0 0.4rem; }
p { margin-bottom: 1rem; }
ul, ol { margin: 0.5rem 0 1rem 1.5rem; }
pre {
background: #f5f5f5;
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
margin: 1rem 0;
font-size: 0.9rem;
}
code { font-family: 'SF Mono', Consolas, monospace; font-size: 0.9em; }
p code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; }
blockquote {
border-left: 3px solid #ddd;
padding-left: 1rem;
color: #666;
margin: 1rem 0;
}
a { color: #2563eb; }
hr { border: none; border-top: 1px solid #eee; margin: 2rem 0; }
nav { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid #eee; }
nav a { margin-right: 1rem; text-decoration: none; color: #666; }
nav a:hover { color: #1a1a1a; }
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #eee; color: #999; font-size: 0.85rem; }
</style>
</head>
<body>
<nav>
<a href="/">Home</a>
</nav>
${html}
<footer>
<p>Built with DocForge API</p>
</footer>
</body>
</html>`;
}
async function build() {
await mkdir(OUTPUT_DIR, { recursive: true });
let files;
try {
files = (await readdir(INPUT_DIR)).filter(f => f.endsWith('.md'));
} catch {
console.log(`No "${INPUT_DIR}" directory found. Creating sample content...`);
await mkdir(INPUT_DIR, { recursive: true });
await writeFile(join(INPUT_DIR, 'index.md'), '# Welcome\n\nThis is your static site. Edit files in `content/` and rebuild.\n');
await writeFile(join(INPUT_DIR, 'about.md'), '# About\n\nThis site was generated from Markdown using the [DocForge API](https://docforge-api.vercel.app).\n');
files = ['index.md', 'about.md'];
}
console.log(`Building ${files.length} pages...`);
for (const file of files) {
const markdown = await readFile(join(INPUT_DIR, file), 'utf-8');
const { html, meta } = await convertMarkdown(markdown);
const title = meta.headings[0] || parse(file).name;
const page = wrapInLayout(html, title);
const outName = parse(file).name + '.html';
await writeFile(join(OUTPUT_DIR, outName), page);
console.log(` ${file} → ${outName} (${meta.wordCount} words)`);
}
console.log(`\nDone! Open ${OUTPUT_DIR}/index.html in your browser.`);
}
build();