Add the ability to generate only a ToC (rather than inject it)
joostdecock opened this issue · comments
Initial checklist
- I read the support docs
- I read the contributing guide
- I agree to follow the code of conduct
- I searched issues and couldn’t find anything (or linked relevant results below)
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 nooptions.heading
is set, passfalse
asoptions.heading
to mdast-util-toc (so that all headings are put in a ToC and it is inserted - If
options.tocOnly
is set, setnode.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.
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.
Cool! Let me know if you need help!
For anyone looking further, I found remark-mdx-toc for mdx