import { GlobalConfiguration } from "../../cfg" import { CanonicalSlug, ClientSlug } from "../../path" import { QuartzEmitterPlugin } from "../types" import path from "path" export type ContentIndex = Map export type ContentDetails = { title: string, links: CanonicalSlug[], tags: string[], content: string, date?: Date, description?: string, } interface Options { enableSiteMap: boolean enableRSS: boolean includeEmptyFiles: boolean } const defaultOptions: Options = { enableSiteMap: true, enableRSS: true, includeEmptyFiles: false, } function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string { const base = cfg.baseUrl ?? "" const createURLEntry = (slug: CanonicalSlug, content: ContentDetails): string => ` https://${base}/${slug} ${content.date?.toISOString()} ` const urls = Array.from(idx).map(([slug, content]) => createURLEntry(slug, content)).join("") return `${urls}` } function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex): string { const base = cfg.baseUrl ?? "" const root = `https://${base}` as ClientSlug const createURLEntry = (slug: CanonicalSlug, content: ContentDetails): string => ` ${content.title} ${root}/${slug} ${root}/${slug} ${content.description} ${content.date?.toUTCString()} ` const items = Array.from(idx).map(([slug, content]) => createURLEntry(slug, content)).join("") return ` ${cfg.pageTitle} ${root} Recent content on ${cfg.pageTitle} Quartz -- quartz.jzhao.xyz ${items} ` } export const ContentIndex: QuartzEmitterPlugin> = (opts) => { opts = { ...defaultOptions, ...opts } return { name: "ContentIndex", async emit(_contentDir, cfg, content, _resources, emit) { const emitted: string[] = [] const linkIndex: ContentIndex = new Map() for (const [_tree, file] of content) { const slug = file.data.slug! const date = file.data.dates?.modified ?? new Date() if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) { linkIndex.set(slug, { title: file.data.frontmatter?.title!, links: file.data.links ?? [], tags: file.data.frontmatter?.tags ?? [], content: file.data.text ?? "", date: date, description: file.data.description ?? "" }) } } if (opts?.enableSiteMap) { await emit({ content: generateSiteMap(cfg, linkIndex), slug: "sitemap", ext: ".xml" }) emitted.push("sitemap.xml") } if (opts?.enableRSS) { await emit({ content: generateRSSFeed(cfg, linkIndex), slug: "index", ext: ".xml" }) emitted.push("index.xml") } const fp = path.join("static", "contentIndex") 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] }) ) await emit({ content: JSON.stringify(simplifiedIndex), slug: fp, ext: ".json", }) emitted.push(`${fp}.json`) return emitted }, getQuartzComponents: () => [], } }