Buliding Static Site with Lume and Deno
Recently, I’ve ported this website to use Lume.
Here’s my _config.ts
setup.
import lume from "lume/mod.ts"
import code_highlight from "lume/plugins/code_highlight.ts"
import filterPages from "lume/plugins/filter_pages.ts"
import inline from "lume/plugins/inline.ts"
import jsx_preact from "lume/plugins/jsx_preact.ts"
import katex from "lume/plugins/katex.ts"
import mdx from "lume/plugins/mdx.ts"
import minifyHTML from "lume/plugins/minify_html.ts";
import nav from "lume/plugins/nav.ts"
import postcss from "lume/plugins/postcss.ts"
import relative_urls from "lume/plugins/relative_urls.ts"
import remark from "lume/plugins/remark.ts"
import resolve_urls from "lume/plugins/resolve_urls.ts"
import sitemap from "lume/plugins/sitemap.ts"
import source_maps from "lume/plugins/source_maps.ts"
import windi_css from "lume/plugins/windi_css.ts"
const site = lume()
site.use(remark())
site.use(code_highlight())
site.use(jsx_preact())
site.use(katex())
site.use(mdx())
site.use(nav())
site.use(postcss())
site.use(relative_urls())
site.use(resolve_urls())
site.use(sitemap())
site.use(source_maps())
site.use(windi_css({ preflight: false }))
site.use(minifyHTML({extensions: [".html", ".css"]}))
site.use(inline())
site.ignore("readme.md")
site.copy([".jpg", ".gif", ".png", ".webp", ".svg"])
site.use(
filterPages({
fn: (page) => !page.data.draft,
})
)
export default site
Navigation Menu
The nav plugin is useful for generating TOC (like the one at /blog/).
Atom Feed
You can generate arbitrary content by returning a String
from a .tsx
file.
Here’s my file generating atom feed: all.atom.xml.tsx
post.data.children
is only populated after the blog posts are rendered, thus renderOrder = 1
(lume doc).
import { assert } from "https://deno.land/std@0.224.0/assert/assert.ts"
import type { Nav } from "lume/plugins/nav.ts"
import { unified } from "npm:unified"
import rehypeParse from "npm:rehype-parse"
import rehypeStringify from "npm:rehype-stringify"
async function toXML(htmlContent: string): string {
const file = await unified()
.use(rehypeParse, { fragment: true })
.use(rehypeStringify, { closeSelfClosing: true })
.process(htmlContent)
return String(file)
}
export const url = "./all.atom.xml"
export const renderOrder = 1
export default async ({ nav }: { nav: Nav }) => {
let newest_mtime = "0000-00-00"
const posts = nav.menu("/blog")!.children!
posts.forEach((post) => {
assert(post.data.mtime, `${post.slug} has no mtime`)
if (post.data.mtime && post.data.mtime > newest_mtime) {
newest_mtime = post.data.mtime
}
})
const entries = await Promise.all(
posts.map(
async (post) => `
<entry>
<title>${post.data.title}</title>
<link href="/blog/${post.slug}/"/>
<id>${post.slug}</id>
${post.data.mtime ? `<updated>${post.data.mtime}</updated>` : ""}
<content>${await toXML(post.data.children)}</content>
</entry>`
)
)
return `
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Locria's info dump</title>
<link href="https://www.1a-insec.net/"/>
<updated>${newest_mtime}</updated>
<author>
<name>Locria Cyber</name>
</author>
<id>urn:uuid:629b78bb-c1f9-4868-ab5b-3ff5c013575a</id>
${entries.join("\n")}
</feed>
`.trim()
}