summaryrefslogtreecommitdiff
path: root/src/utils/generateToc.ts
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-07-03 10:56:21 +0300
committerDawid Rycerz <dawid@rycerz.xyz>2025-07-03 10:56:21 +0300
commit456cf011b36de91c9936994b1fa45703adcd309b (patch)
tree8e60daf998f731ac50d100fa490eaecae1168042 /src/utils/generateToc.ts
Initial fork of chrismwilliams/astro-theme-cactus theme
Diffstat (limited to 'src/utils/generateToc.ts')
-rw-r--r--src/utils/generateToc.ts37
1 files changed, 37 insertions, 0 deletions
diff --git a/src/utils/generateToc.ts b/src/utils/generateToc.ts
new file mode 100644
index 0000000..f63f0bf
--- /dev/null
+++ b/src/utils/generateToc.ts
@@ -0,0 +1,37 @@
+// Heavy inspiration from starlight: https://github.com/withastro/starlight/blob/main/packages/starlight/utils/generateToC.ts
+import type { MarkdownHeading } from "astro";
+
+export interface TocItem extends MarkdownHeading {
+ children: TocItem[];
+}
+
+interface TocOpts {
+ maxHeadingLevel?: number | undefined;
+ minHeadingLevel?: number | undefined;
+}
+
+/** Inject a ToC entry as deep in the tree as its `depth` property requires. */
+function injectChild(items: TocItem[], item: TocItem): void {
+ const lastItem = items.at(-1);
+ if (!lastItem || lastItem.depth >= item.depth) {
+ items.push(item);
+ } else {
+ injectChild(lastItem.children, item);
+ return;
+ }
+}
+
+export function generateToc(
+ headings: ReadonlyArray<MarkdownHeading>,
+ { maxHeadingLevel = 4, minHeadingLevel = 2 }: TocOpts = {},
+) {
+ // by default this ignores/filters out h1 and h5 heading(s)
+ const bodyHeadings = headings.filter(
+ ({ depth }) => depth >= minHeadingLevel && depth <= maxHeadingLevel,
+ );
+ const toc: Array<TocItem> = [];
+
+ for (const heading of bodyHeadings) injectChild(toc, { ...heading, children: [] });
+
+ return toc;
+}