-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.mjs
More file actions
142 lines (133 loc) · 4.17 KB
/
build.mjs
File metadata and controls
142 lines (133 loc) · 4.17 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { rmSync, mkdirSync, copyFileSync, cpSync, writeFileSync, readFileSync, existsSync, readdirSync, statSync } from "fs";
import { join } from "path";
// Auto-discover talks: any directory with a slides.html file
const talks = readdirSync(".")
.filter((entry) => {
if (entry.startsWith(".") || entry === "public" || entry === "node_modules") return false;
if (!statSync(entry).isDirectory()) return false;
return existsSync(join(entry, "slides.html"));
})
.map((dir) => {
// Load metadata from talk.json if it exists, otherwise derive from dir name
const metaPath = join(dir, "talk.json");
if (existsSync(metaPath)) {
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
return { dir, title: meta.title, event: meta.event, date: meta.date };
}
// Derive from directory name (e.g. "2026-02-mcpconf-london" → "mcpconf london")
const name = dir.replace(/^\d{4}-\d{2}-/, "").replace(/-/g, " ");
return { dir, title: name, event: "", date: dir.slice(0, 7) };
})
.sort((a, b) => b.dir.localeCompare(a.dir)); // newest first
const publicDir = "public";
// Clean and recreate public/
rmSync(publicDir, { recursive: true, force: true });
mkdirSync(publicDir, { recursive: true });
// Copy each talk's directory contents → public/<talk-dir>/
for (const talk of talks) {
const dest = join(publicDir, talk.dir);
mkdirSync(dest, { recursive: true });
// Copy all files from the talk directory
const entries = readdirSync(talk.dir);
for (const entry of entries) {
const srcPath = join(talk.dir, entry);
const destPath = join(dest, entry);
if (statSync(srcPath).isDirectory()) {
cpSync(srcPath, destPath, { recursive: true });
} else {
copyFileSync(srcPath, destPath);
}
}
// Rename slides.html to index.html for serving
const slidesPath = join(dest, "slides.html");
const indexPath = join(dest, "index.html");
if (existsSync(slidesPath) && !existsSync(indexPath)) {
copyFileSync(slidesPath, indexPath);
}
console.log(`Copied ${talk.dir}/ → ${dest}/ (${entries.length} files)`);
}
// Generate landing page
const talkListHtml = talks
.map(
(t) => `
<a href="/${t.dir}/" class="talk">
<span class="talk-title">${t.title}</span>
${t.event ? `<span class="talk-meta">${t.event} · ${t.date}</span>` : `<span class="talk-meta">${t.date}</span>`}
</a>`
)
.join("\n");
const indexHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>StackOne Talks</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #0a0a0a;
color: #e0e0e0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 4rem 1.5rem;
}
h1 {
font-size: 2rem;
font-weight: 700;
color: #fff;
margin-bottom: 0.5rem;
}
h1 span { color: #05C168; }
.subtitle {
color: #888;
margin-bottom: 3rem;
font-size: 1.1rem;
}
.talks {
width: 100%;
max-width: 640px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.talk {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 1.25rem 1.5rem;
background: #161616;
border: 1px solid #262626;
border-radius: 12px;
text-decoration: none;
color: inherit;
transition: border-color 0.15s, background 0.15s;
}
.talk:hover {
border-color: #05C168;
background: #1a1a1a;
}
.talk-title {
font-size: 1.1rem;
font-weight: 600;
color: #fff;
}
.talk-meta {
font-size: 0.9rem;
color: #888;
}
</style>
</head>
<body>
<h1>Stack<span>One</span> Talks</h1>
<p class="subtitle">Conference presentations and slides</p>
<div class="talks">
${talkListHtml}
</div>
</body>
</html>`;
writeFileSync(join(publicDir, "index.html"), indexHtml);
console.log(`Generated landing page at ${join(publicDir, "index.html")}`);
console.log(`Build complete: ${talks.length} talk(s)`);