-
Notifications
You must be signed in to change notification settings - Fork 15
fix(toc): prevent misnamed mdx components from breaking TOC #1242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(toc): prevent misnamed mdx components from breaking TOC #1242
Conversation
Discovered through [this ticket](https://linear.app/readme-io/issue/CX-2543/tabapay-toc-does-not-respect-headers-nesting) that the TOC generated by the MDX renderer would become broken with miscalculated nesting depths. This happened when a jsx element was embedded inside the MDX body and the custom component name did not match the exported jsx elements. For example: image here I'm not entirely sure whether this is an edge-case we should be guarding against from our custom component editor. Like, adding validation to ensure that the component name matches what's being exported from it. My gut tells me it'd be really hard to enforce that. So, the fix here is to simply take this edge-case into account and add protections for the cases where this happens. When a component name doesn't match the jsx element name, it previously was making its way into the `plugin/toc` flow. But b/c it has no `tagName`, the `getDepth()` calculation would break and return a `NaN` instead of a valid integer. This would break the nesting logic in `toctoHast()` and cause all headers to render at the first level. Fixed this by: 1. filtering away these mismatched components that make it through 1. additionally updating the `getDepth()` function to be more fail-proof and handle the case where `tagName` is undefined CX-2543
| // 👇🏼 this is what we're guarding against | ||
| CompDoesNotMatchExportedModule: compModule, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the edge-case that was causing the malformed TOCs in some projects.
when they have custom components that exports a JSX element that differs from the saved file name, this is what happens. the components hash keys are something different than the exported elements.
| <ul> | ||
| <li><a href="#subheading">SubHeading</a></li> | ||
| </ul> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when everything works properly, the subheading should be indented one level like this
| if (node.type === 'mdxJsxFlowElement') { | ||
| return components[node.name] || []; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the exported JSX elements are the nodes (i.e. node.name) and the components is a hash of custom component files. previously, it was matching node.name in components which would be false when the filename didn't match the exported module. otherwise, it'd return the mdxJsxFlowElement node, which would then make its way into the getDepth() function above on L74.
now, if the node type is an mdx jsx element, we'll return an empty array instead, which essentially filters it out completely. i think this is the desired result, but i put in a patch in the getDepth() function just in case something other than a heading element makes it in
kevinports
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
## Version 11.7.6 ### 🛠 Fixes & Updates * **deps:** bump actions/checkout from 5 to 6 ([#1241](#1241)) ([2c36df2](2c36df2)), closes [actions/checkout#2248](actions/checkout#2248) [actions/checkout#2286](actions/checkout#2286) [actions/checkout#2298](actions/checkout#2298) [actions/checkout#2311](actions/checkout#2311) [actions/checkout#2301](actions/checkout#2301) [actions/checkout#2286](actions/checkout#2286) [actions/checkout#2248](actions/checkout#2248) [actions/checkout#2301](actions/checkout#2301) [actions/checkout#2226](actions/checkout#2226) [actions/checkout#2305](actions/checkout#2305) [actions/checkout#1971](actions/checkout#1971) [actions/checkout#1977](actions/checkout#1977) [actions/checkout#2043](actions/checkout#2043) [actions/checkout#2044](actions/checkout#2044) [actions/checkout#2194](actions/checkout#2194) [actions/checkout#2224](actions/checkout#2224) [actions/checkout#2236](actions/checkout#2236) [actions/checkout#1941](actions/checkout#1941) [actions/checkout#1946](actions/checkout#1946) [actions/checkout#1924](actions/checkout#1924) [actions/checkout#1180](actions/checkout#1180) [actions/checkout#1777](actions/checkout#1777) [actions/checkout#1872](actions/checkout#1872) [actions/checkout#1739](actions/checkout#1739) [actions/checkout#1697](actions/checkout#1697) [actions/checkout#1774](actions/checkout#1774) [actions/checkout#1776](actions/checkout#1776) [actions/checkout#1732](actions/checkout#1732) [actions/checkout#1703](actions/checkout#1703) [actions/checkout#1694](actions/checkout#1694) [actions/checkout#1696](actions/checkout#1696) [actions/checkout#1695](actions/checkout#1695) [#2311](https://github.com/readmeio/markdown/issues/2311) [#2298](https://github.com/readmeio/markdown/issues/2298) [#2286](https://github.com/readmeio/markdown/issues/2286) [#2248](https://github.com/readmeio/markdown/issues/2248) * **toc:** prevent misnamed mdx components from breaking TOC ([#1242](#1242)) ([fcb5f7d](fcb5f7d)) <!--SKIP CI-->
This PR was released!🚀 Changes included in v11.7.6 |

🧰 Changes
Discovered through this ticket
that the TOC generated by the MDX renderer would become broken with
miscalculated nesting depths and exposing
h3s or other heading depths that shouldn't be included.This happened when a jsx element was embedded inside the MDX body and
the custom component name did not match the exported jsx elements.
For example:

I'm not entirely sure whether this is an edge-case we should be guarding
against from inside our custom component editor. Like, adding validation to
ensure that the component name matches what's being exported from it. My
gut tells me it'd be really hard to enforce that.
So, the fix (for now) is to simply take this edge-case into account and add
protections for the cases where this happens.
When a component name doesn't match the jsx element name, it previously
was making its way into the
plugin/tocflow. But b/c it has notagName, thegetDepth()calculation would break and return aNaNinstead of a valid integer. This would break the nesting logic in
toctoHast()and cause all headers to render at the first level.Fixed this by:
getDepth()function to be more fail-proofand handle the case where
tagNameis undefined🧬 QA & Testing
Tests should pass