npm.io
1.1.10 • Published 21h ago

rehype-custom-toc

Licence
MIT
Version
1.1.10
Deps
8
Size
15 kB
Vulns
0
Weekly
0

rehype-custom-toc

rehype plugin to generate a customizable table of contents. If you are using Astro, you can use the convenient wrapper astro-custom-toc.

Installation

npm install rehype-custom-toc

Example usage

The table of contents will be inserted at the location of the <!-- toc --> comment or at the beginning of the file if no comment is found.

When parsing HTML with rehype, the <!-- toc --> comment is preserved as-is, but when converting from Markdown you need to convert Markdown comments to HTML comments (as shown below with remark-comment) so rehype-custom-toc can detect the TOC marker.

import remarkComment from "remark-comment";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeCustomToc from "rehype-custom-toc";
import rehypeStringify from "rehype-stringify";
import { unified } from "unified";

/**
 * A comment node generated by the `remark-comment` plugin.
 */
interface Comment extends Node {
    type: "comment";
    value: "";
    commentValue: string;
}

const processor = unified()
    .use(remarkParse)
    .use(remarkComment, {
        ast: true
    })
    .use(remarkRehype, {
        handlers: {
            /**
             * Convert a mdast comment node to a hast comment node.
             * @param _ State
             * @param node mdast comment node
             * @returns hast comment node
             */
            comment: (_, node: Comment) => ({
                type: "comment",
                value: node.commentValue
            })
        }
    })
    .use(rehypeCustomToc)
    .use(rehypeStringify);

const markdown = `
# Title

This is a sample markdown paragraph.

<!-- toc -->

## Section 1

### Subsection 1.1
`;

processor.process(markdown).then((result) => {
    console.log(result.toString());
});

The above code will output the following (formatted for readability):

<h1 id="title">Title</h1>
<p>This is a sample markdown paragraph.</p>
<aside class="toc">
    <h2>Contents</h2>
    <nav>
        <ul>
            <li><a href="#title">Title</a></li>
            <ul>
                <li><a href="#section-1">Section 1</a></li>
                <ul>
                    <li><a href="#subsection-11">Subsection 1.1</a></li>
                </ul>
            </ul>
        </ul>
    </nav>
</aside>
<h2 id="section-1">Section 1</h2>
<h3 id="subsection-11">Subsection 1.1</h3>
Add IDs to headings without a TOC

You can import rehypeSlugger from rehype-custom-toc and use it as a standalone plugin to add slugs to headings without generating a table of contents. This is useful if you want to add IDs to headings but don't want a TOC.

import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import { rehypeSlugger } from "rehype-custom-toc";
import rehypeStringify from "rehype-stringify";
import { unified } from "unified";

const processor = unified().use(remarkParse).use(remarkRehype).use(rehypeSlugger).use(rehypeStringify);

const markdown = `
# Title

This is a sample markdown paragraph.

## Section 1

### Subsection 1.1
`;

processor.process(markdown).then((result) => {
    console.log(result.toString());
});

The above code will output the following (formatted for readability):

<h1 id="title">Title</h1>
<p>This is a sample markdown paragraph.</p>
<h2 id="section-1">Section 1</h2>
<h3 id="subsection-11">Subsection 1.1</h3>

Options

template

A function that takes the generated HTML and returns the final HTML. This can be used to wrap the generated HTML in a custom template.

Default:

const defaultTemplate = (html) => {
    return `
<aside class="toc">
    <h2>Contents</h2>
    <nav>
        ${html}
    </nav>
</aside>`.trim();
};
maxDepth

The maximum depth of headings to include in the table of contents.

Default: 3

ordered

Whether to use an ordered list (<ol>) or an unordered list (<ul>).

Default: false

Keywords