This Article IsCreated at don't-know-whenLast Modified at 2023-04-14Referenced as ia.www.b06

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.182.0/_util/asserts.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()
}