remarkjs / remark-toc

plugin to generate a table of contents (TOC)

Home Page:https://remark.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Leading content replaced by TOC if I don't have a heading before it

dereckmezquita opened this issue · comments

Initial checklist

Affected packages and versions

^10.1.0

Link to runnable example

No response

Steps to reproduce

If I have my markdown with a TOC insertion point and content directly below it without a header then any content until the next header is removed and replaced with the TOC:

---
title: "Hierarchy in art"
published: true
---

## Table of contents

Here is some content that will get removed.

## test

Some more content.

However, if I do this then my content doesn't get removed:

---
title: "Hierarchy in art"
published: true
---

## Table of contents

## some header any header here even an empty one protects content

Here is some content that will get removed.

## test

Some more content.

Is there a way I can avoid losing that content there? I have my h1 generated from the yaml in my markdown and only want to insert the TOC for any lower level headers h2 etc.

I don't necessarily need my leading content to have a header as this is my de facto introduction etc.

You can consult my source code here; note this is a work in progress: https://github.com/dereckmezquita/derecksnotes.com/tree/v3-nextjs-refactor/client-next

Expected behavior

If I place a TOC header and no following headers then the content should not be removed instead a TOC should be inserted.

Actual behavior

If there is not header after a TOC insertion point and there is content directly after, then any content before the next header gets removed.

Runtime

Node v16

Package manager

npm 8

OS

macOS

Build and bundle tools

Next.js

Duplicate of #18

Hi! This was closed. Team: If this was fixed, please add phase/solved. Otherwise, please add one of the no/* labels.

Hi! Thanks for taking the time to contribute!

Because we treat issues as our backlog, we close duplicates to focus our work and not have to touch the same chunk of code for the same reason multiple times. This is also why we may mark something as duplicate that isn’t an exact duplicate but is closely related.

Thanks,
— bb

I posted this a while ago because I was not happy with remarkToc; I was not happy with the rigidity - unfortunately, we can't have more options.

I created my own remark plugin using the underlying dependency mdast-util-toc; sharing for others to use:

import { toc, Options as TocOptions } from 'mdast-util-toc';
import { Root } from 'mdast';
import { Node } from 'mdast-util-toc/lib'
import { Plugin } from 'unified';

interface RemarkTocCollapseOptions extends TocOptions {
    heading?: string;
}

/**
 * Plugin to generate a Table of Contents (TOC); does not remove leading paragraphs without headers.
 */
const remarkToc: Plugin<[(RemarkTocOptions | undefined)?], Root> = (options = {}) => {
    return (node: Node) => {
        const result = toc(
            node as Root,
            Object.assign({}, options, {
                heading: options.heading || 'toc|table[ -]of[ -]contents?',
                tight: true,
                maxDepth: 10
            })
        );

        if (
            result.endIndex === null ||
            result.index === null ||
            result.index === -1 ||
            !result.map
        ) {
            return;
        }

        // I don't want remarkToc to remove leading paragraphs with no headers
        if ('children' in node) {
            node.children = [
                ...node.children.slice(0, result.index),
 
                result.map,
               
                ...node.children.slice(result.index)
            ];
        }
    };
};

export default remarkToc;

That algorithm unfortunately doesn’t support repeated calls: it would keep on adding tables of contents repeatedly.