mirror of
https://gitlab.com/MisterBiggs/brain-quartz.git
synced 2025-07-26 16:21:26 +00:00
.github
docs
quartz
cli
components
i18n
plugins
emitters
404.tsx
aliases.ts
assets.ts
cname.ts
componentResources.ts
contentIndex.tsx
contentPage.tsx
favicon.ts
folderPage.tsx
helpers.ts
index.ts
ogImage.tsx
static.ts
tagPage.tsx
filters
transformers
index.ts
types.ts
vfile.ts
processors
static
styles
util
bootstrap-cli.mjs
bootstrap-worker.mjs
build.ts
cfg.ts
worker.ts
.gitattributes
.gitignore
.gitlab-ci.yml
.node-version
.npmrc
.prettierignore
.prettierrc
CODE_OF_CONDUCT.md
Dockerfile
LICENSE.txt
README.md
bun.lock
globals.d.ts
index.d.ts
package-lock.json
package.json
quartz.config.ts
quartz.layout.ts
tsconfig.json
* checkpoint * incremental all the things * properly splice changes array * smol doc update * update docs * make fancy logger dumb in ci
175 lines
5.4 KiB
TypeScript
175 lines
5.4 KiB
TypeScript
import { Root } from "hast"
|
|
import { GlobalConfiguration } from "../../cfg"
|
|
import { getDate } from "../../components/Date"
|
|
import { escapeHTML } from "../../util/escape"
|
|
import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../../util/path"
|
|
import { QuartzEmitterPlugin } from "../types"
|
|
import { toHtml } from "hast-util-to-html"
|
|
import { write } from "./helpers"
|
|
import { i18n } from "../../i18n"
|
|
|
|
export type ContentIndexMap = Map<FullSlug, ContentDetails>
|
|
export type ContentDetails = {
|
|
slug: FullSlug
|
|
filePath: FilePath
|
|
title: string
|
|
links: SimpleSlug[]
|
|
tags: string[]
|
|
content: string
|
|
richContent?: string
|
|
date?: Date
|
|
description?: string
|
|
}
|
|
|
|
interface Options {
|
|
enableSiteMap: boolean
|
|
enableRSS: boolean
|
|
rssLimit?: number
|
|
rssFullHtml: boolean
|
|
rssSlug: string
|
|
includeEmptyFiles: boolean
|
|
}
|
|
|
|
const defaultOptions: Options = {
|
|
enableSiteMap: true,
|
|
enableRSS: true,
|
|
rssLimit: 10,
|
|
rssFullHtml: false,
|
|
rssSlug: "index",
|
|
includeEmptyFiles: true,
|
|
}
|
|
|
|
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndexMap): string {
|
|
const base = cfg.baseUrl ?? ""
|
|
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
|
|
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
|
${content.date && `<lastmod>${content.date.toISOString()}</lastmod>`}
|
|
</url>`
|
|
const urls = Array.from(idx)
|
|
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
|
|
.join("")
|
|
return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`
|
|
}
|
|
|
|
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndexMap, limit?: number): string {
|
|
const base = cfg.baseUrl ?? ""
|
|
|
|
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item>
|
|
<title>${escapeHTML(content.title)}</title>
|
|
<link>https://${joinSegments(base, encodeURI(slug))}</link>
|
|
<guid>https://${joinSegments(base, encodeURI(slug))}</guid>
|
|
<description>${content.richContent ?? content.description}</description>
|
|
<pubDate>${content.date?.toUTCString()}</pubDate>
|
|
</item>`
|
|
|
|
const items = Array.from(idx)
|
|
.sort(([_, f1], [__, f2]) => {
|
|
if (f1.date && f2.date) {
|
|
return f2.date.getTime() - f1.date.getTime()
|
|
} else if (f1.date && !f2.date) {
|
|
return -1
|
|
} else if (!f1.date && f2.date) {
|
|
return 1
|
|
}
|
|
|
|
return f1.title.localeCompare(f2.title)
|
|
})
|
|
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
|
|
.slice(0, limit ?? idx.size)
|
|
.join("")
|
|
|
|
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<rss version="2.0">
|
|
<channel>
|
|
<title>${escapeHTML(cfg.pageTitle)}</title>
|
|
<link>https://${base}</link>
|
|
<description>${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({ count: limit }) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
|
|
cfg.pageTitle,
|
|
)}</description>
|
|
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
|
${items}
|
|
</channel>
|
|
</rss>`
|
|
}
|
|
|
|
export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
|
opts = { ...defaultOptions, ...opts }
|
|
return {
|
|
name: "ContentIndex",
|
|
async *emit(ctx, content) {
|
|
const cfg = ctx.cfg.configuration
|
|
const linkIndex: ContentIndexMap = new Map()
|
|
for (const [tree, file] of content) {
|
|
const slug = file.data.slug!
|
|
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()
|
|
if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
|
|
linkIndex.set(slug, {
|
|
slug,
|
|
filePath: file.data.relativePath!,
|
|
title: file.data.frontmatter?.title!,
|
|
links: file.data.links ?? [],
|
|
tags: file.data.frontmatter?.tags ?? [],
|
|
content: file.data.text ?? "",
|
|
richContent: opts?.rssFullHtml
|
|
? escapeHTML(toHtml(tree as Root, { allowDangerousHtml: true }))
|
|
: undefined,
|
|
date: date,
|
|
description: file.data.description ?? "",
|
|
})
|
|
}
|
|
}
|
|
|
|
if (opts?.enableSiteMap) {
|
|
yield write({
|
|
ctx,
|
|
content: generateSiteMap(cfg, linkIndex),
|
|
slug: "sitemap" as FullSlug,
|
|
ext: ".xml",
|
|
})
|
|
}
|
|
|
|
if (opts?.enableRSS) {
|
|
yield write({
|
|
ctx,
|
|
content: generateRSSFeed(cfg, linkIndex, opts.rssLimit),
|
|
slug: (opts?.rssSlug ?? "index") as FullSlug,
|
|
ext: ".xml",
|
|
})
|
|
}
|
|
|
|
const fp = joinSegments("static", "contentIndex") as FullSlug
|
|
const simplifiedIndex = Object.fromEntries(
|
|
Array.from(linkIndex).map(([slug, content]) => {
|
|
// remove description and from content index as nothing downstream
|
|
// actually uses it. we only keep it in the index as we need it
|
|
// for the RSS feed
|
|
delete content.description
|
|
delete content.date
|
|
return [slug, content]
|
|
}),
|
|
)
|
|
|
|
yield write({
|
|
ctx,
|
|
content: JSON.stringify(simplifiedIndex),
|
|
slug: fp,
|
|
ext: ".json",
|
|
})
|
|
},
|
|
externalResources: (ctx) => {
|
|
if (opts?.enableRSS) {
|
|
return {
|
|
additionalHead: [
|
|
<link
|
|
rel="alternate"
|
|
type="application/rss+xml"
|
|
title="RSS Feed"
|
|
href={`https://${ctx.cfg.configuration.baseUrl}/index.xml`}
|
|
/>,
|
|
],
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|