Table of Contents plugin reworked using the Deno stack to mimic the same plugin published here. Which I need for my Lume site.
Important
ROBOT TRANSPARENCY NOTICE: A robot (DeepSeek V4 Pro) was used to generate Unit tests, the bot didn't write those, it suggested 15-line patches that were curated by me. This same methodology was used to generate the type compatibilty. All "AI" code was hand-reviewed and corrected.
TL;DR Don't. I made this for my own website because the Lume TOC was beyond unusable for me and I'd rather maintain a fork to keep doing things my way.
Also tests are Deno runtime dependent and the build system doesn't use deno
tasks but maak instead. You'll need either nix or guix for that.
Install it via JSR with:
deno install jsr:@funcproglinux/markdown-it-toc-revivedDon't expect many updates through there as they have relied on FlareSlop to enforce proprietary web malware:
Usage in Markdown:
[[toc]]
## First
### NestedPlaceholders: ${toc}, [toc], [[toc]], [[_toc_]] (case-insensitive),
alone on a line.
Lume 3.3.1 retyped markdown.plugins from unknown[] to
(MarkdownItPlugin | [MarkdownItPlugin, unknown[]])[], which breaks the
[plugin, {options}] tuple in two separate ways:
-
MarkdownItPlugindefaults to...params: unknown[], so no plugin with typed options is assignable (unknownis not assignable toAnchorOptions | undefined). Parameterizing the generic doesn't help either. -
The type says the second tuple element is a params array, but the runtime is
engine.use(...plugin), i.e. that element is passed as a single argument. Obey the type and write[mdAnchor, [opts]]and your options are silently dropped.
This plugin is a factory, so it drops straight into plugins. For third-party
plugins with options, use Lume's addMarkdownItPlugin hook instead of a tuple:
import lume from "lume/mod.ts";
import mdAnchor from "markdown-it-anchor";
import { toc } from "@funcproglinux/markdown-it-toc-revived";
const site = lume({ src: "./src", dest: "./output" }, {
markdown: {
options: { html: true },
plugins: [
toc({
level: [2, 3, 4, 5, 6],
listType: "ul",
containerClass: "table-of-contents",
listClass: "nested-list",
}),
],
},
});
site.hooks.addMarkdownItPlugin(mdAnchor, {
permalink: mdAnchor.permalink.linkInsideHeader({
placement: "before",
symbol: "§",
class: "text-decoration-none",
}),
});
export default site;markdown-it-attrs and markdown-it-deflist are already installed by Lume by
default, so don't pass them again unless you set useDefaultPlugins: false.
Order doesn't really matter as the tree gets built at render time, so whatever
ids markdown-it-anchor set are already there and it just uses those.
JSDocs are your friend.
-
The tree is rebuilt per placeholder. Which should be more than enough for sites with a single ToC. I've yet to see a legitimate use case for more than one, but needless to say it's obvious what would happen given the condition I just mentioned.
-
It only reads
textandcode_inline. Alt text and rawhtml_inlinein a heading will be dropped. -
Reads the text after core rules, so
typographeralready mangled your quotes. -
Without
markdown-it-anchor, duplicate headings get-1,-2suffixes computed here; nothing emits matchingids, so those links go nowhere. Use an anchor plugin or write your own. -
Inline options for
$<toc{...}>are removed on purpose. Don't expect those. -
Lume runs
engine.disable("code"), so a placeholder indented by 4 spaces is not a code block there: the rule bails on the indent guard and the line falls through toparagraph, rendering[[toc]]literally instead of<pre>.
deno check && deno lint && deno testEven though similar logic is shared, mostly for exposed API that will be used in a Lume site this is written from scratch. If you still want to use it, that's AGPLv3.0 or later for y'all.
This is an independent implementation based on the API exposed by
markdown-it-toc-done-right under the MIT License, © Fabio Zendhi Nagao.
