Refactor site generation into a modular content pipeline
Deploy website / deploy (push) Successful in 24s
Deploy website / deploy (push) Successful in 24s
This commit is contained in:
1 parent
0f69028d4a
commit
146d516f24
20 files changed
+826
-427
No files matched your search
+71
@@ -0,0 +1,71 @@
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { readdir, readFile, stat } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
export const stripSidenotes = (text) => {
|
||||
let result = ''
|
||||
for (let index = 0; index < text.length;) {
|
||||
if (text[index] === '^' && text[index + 1] === '[') {
|
||||
const end = text.indexOf(']', index + 2)
|
||||
if (end >= 0) {
|
||||
index = end + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
result += text[index++]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const dateFor = async (file) => {
|
||||
try {
|
||||
const gitDate = execFileSync(
|
||||
'git',
|
||||
['log', '-1', '--date=format-local:%Y-%m-%dT%H:%M:%SZ', '--format=%ad', '--', file],
|
||||
{ env: { ...process.env, TZ: 'UTC' }, stdio: ['ignore', 'pipe', 'ignore'] },
|
||||
).toString().trim()
|
||||
if (gitDate) return gitDate
|
||||
} catch {}
|
||||
try {
|
||||
return (await stat(file)).mtime.toISOString()
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export const readPages = async (dir, { includeDrafts = false } = {}) => {
|
||||
let entries
|
||||
try {
|
||||
entries = await readdir(dir, { withFileTypes: true })
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') return []
|
||||
throw error
|
||||
}
|
||||
const markdownEntries = entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith('.md') && entry.name !== '.md'
|
||||
&& (includeDrafts || !entry.name.startsWith('.')))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
const pages = await Promise.all(markdownEntries.map(async (entry) => {
|
||||
const file = join(dir, entry.name)
|
||||
const source = await readFile(file, 'utf8')
|
||||
const titleLine = source.split(/\r?\n/).find((line) => line.startsWith('# '))
|
||||
if (!titleLine) return undefined
|
||||
const draft = entry.name.startsWith('.')
|
||||
const name = (draft ? entry.name.slice(1) : entry.name).slice(0, -3)
|
||||
if (!name) return undefined
|
||||
return {
|
||||
name,
|
||||
title: stripSidenotes(titleLine.slice(2).trim()),
|
||||
file,
|
||||
source,
|
||||
date: await dateFor(file),
|
||||
draft,
|
||||
}
|
||||
}))
|
||||
const byName = new Map()
|
||||
for (const page of pages.filter(Boolean)) {
|
||||
const existing = byName.get(page.name)
|
||||
if (!existing || (existing.draft && !page.draft)) byName.set(page.name, page)
|
||||
}
|
||||
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name))
|
||||
}
|
||||
Reference in new issue
Block a user