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

Add the ability to generate only a ToC (rather than inject it)

joostdecock opened this issue · comments

Initial checklist

Problem

Injecting the ToC in the markdown content is nice but it makes the ToC baked-in to the content itself.
I have a use-case/layout where I'd like the ToC to be separated from the main content (think sidebar with the ToC).

I could certainly try to extract it after the fact, but I prefer to be able to generate the MDX in one pass and then run another pass for just the ToC and pass both as individual props.

So, I would like to see an option that makes this plugin return only the ToC.

Solution

I have implemented this in this commit in my fork. I would create a pull request for it, but the contributor guidelines say to first create an issue to discuss. So let's discuss :)

This does the following:

  • Add a tocOnly option
  • If the options.tocOnly option is set and no options.heading is set, pass false as options.heading to mdast-util-toc (so that all headings are put in a ToC and it is inserted
  • If options.tocOnly is set, set node.children to only the ToC and return

But I feel looking at the changes in the commit above is going to be more clear than my trying to explain it here.

Alternatives

Best-case scenario, this could be handled without doing two passes over the MDX. For example by putting the ToC in a different type of node that we can easily pull out and/or keep out of the generated HTML. (Essentially the way frontmatter works, where it adds a YAML node.)

Alas, this is an area where I'm a total noob so I'd appreciate your expert advice on the best way to implement this.
Because even if you decide against including it, I'd still like to have it (and thus might just maintain a fork for it).

Thank you, and keep up the good work.
joost

Hi, this is exactly why unified has low level utilities and plugins that wrap those utilities: to compose functionality but leave the building blocks to do something else. I don't see the benefit of adding such as option, if you just want the tree, you should use mdast-util-toc. You could then for instance turn it into hast with remark-rehype and then into HTML with rehype-stringify.

Sure, write it yourself is a perfectly valid answer.
But this is a common use-case and people are doing all sorts of hacks to make it happen. So I feel it would be a good addition to the ecosystem, even if it's not part of this particular plugin.

If you don't see the benefit of adding the option, can you see the benefit of having a plugin that generates a ToC (and only a ToC)?
Because in that case, I'd still be interested to hear an expert opinion on how to do both in one pass.

commented

write it yourself does not mean it’s not a good addition to the ecosystem. I believe murderlon is saying that we can’t maintain everything that anyone ever would need. Rather, we went to great lengths to make everything a lego block so that everyone can make whatever they want.

This plugin (remark-toc) deals with markdown. Markdown does not have imports and exports. There is no way it could export stuff. I don’t believe replacing everything with a ToC fits with what unified is: a chain of multiple transforms. As you mention with two-passes it’s also slow.

You are asking about MDX. MDX in version 2 has an extremely powerful AST and its own plugins. The docs on Extending MDX include a list of MDX-specific plugins. remark-mdx-frontmatter and rehype-mdx-title are two examples that are exactly what you want when combined with mdast-util-toc.
It would be extremely useful, as you say, for someone to make a proper solution and share it with the world, e.g., remark-mdx-toc.

Thanks @wooorm for the nudge in the right direction. I might take a stab at it.

commented

Cool! Let me know if you need help!

commented

For anyone looking further, I found remark-mdx-toc for mdx