-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
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
feat(v2): add ability to set custom heading id #4222
Changes from 1 commit
5b6a4dc
0e33167
fc13227
c7a6a8e
3f75cf3
d8493df
07f0d03
bb3eb06
d0a09af
216696d
0d575de
c3ee525
870628f
c69a45f
728d262
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,14 @@ | |
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
/* Based on remark-slug (https://github.com/remarkjs/remark-slug) */ | ||
/* Based on remark-slug (https://github.com/remarkjs/remark-slug) and gatsby-remark-autolink-headers (https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-autolink-headers) */ | ||
|
||
const visit = require('unist-util-visit'); | ||
const toString = require('mdast-util-to-string'); | ||
const slugs = require('github-slugger')(); | ||
|
||
const customHeadingIdRegex = /^(.*?)\s*\{#([\w-]+)\}$/; | ||
|
||
function slug() { | ||
const transformer = (ast) => { | ||
slugs.reset(); | ||
|
@@ -26,11 +28,29 @@ function slug() { | |
const headingTextNodes = headingNode.children.filter( | ||
({type}) => !['html', 'jsx'].includes(type), | ||
); | ||
const normalizedHeadingNode = | ||
const heading = toString( | ||
headingTextNodes.length > 0 | ||
? {children: headingTextNodes} | ||
: headingNode; | ||
id = slugs.slug(toString(normalizedHeadingNode)); | ||
: headingNode, | ||
); | ||
|
||
// Support explicit heading IDs | ||
const customHeadingIdMatches = customHeadingIdRegex.exec(heading); | ||
|
||
if (customHeadingIdMatches) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious if this feature will have to be configurable? How much of an impact on performance would it have if it were always enabled by default? Or does it make sense to create a separate plugin for this feature? For now I have combined it into the existing slug plugin because they essentially do the same thing (create headings id). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine to enable by default and put in mdx loader. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just thought that having new plugin might be better for this. Since this feature is mainly needed when using the i18n. Moreover, I think to add a cli command to automatically create explicit ids for existing headers (we have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having a way to "lock" the ids at a given time looks like a nice idea. Not sure how to build that easily, how would the plugin know where to look for markdown files, as the paths are provided as options to the docs, blog, page plugin? The only thing I'm thinking of is having this cli in core, and getting all the relevant paths from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's actually useful in all cases. If you want to link to some heading, we'd rather ensure that link does not break when the heading is updated. Fixing heading ids you want to link to should rather be the norm, and we could even add a warning someday to tell the user that he's linking to an anchor that has not been fixed? Currently it's too easy to have broken anchor links in a Docusaurus site. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I lost sight of that it is currently not possible to add new Remark plugin from the custom Docusaurus plugin. Maybe we should create new hook for this purpose? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to add new command to write heading ids to the docusaurus core (eg. |
||
id = customHeadingIdMatches[2]; | ||
|
||
// Remove the custom ID part from the text node | ||
if (headingNode.children.length > 1) { | ||
headingNode.children.pop(); | ||
} else { | ||
const lastNode = | ||
headingNode.children[headingNode.children.length - 1]; | ||
lastNode.value = customHeadingIdMatches[1] || heading; | ||
} | ||
} else { | ||
id = slugs.slug(heading); | ||
} | ||
} | ||
|
||
data.id = id; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,10 @@ The headers are well-spaced so that the hierarchy is clear. | |
- that you want your users to remember | ||
- and you may nest them | ||
- multiple times | ||
|
||
### Custom id headers {#custom-id} | ||
|
||
With `{#custom-id}` syntax you can set your own header id. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth creating a page dedicated to anchor links in markdown features category? I'd also document this in the i18n section but I can do it in another PR? |
||
``` | ||
|
||
This will render in the browser as follows: | ||
|
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.
What about extracting
customHeadingIdRegex.exec(heading);
in a separate functionconst {heading,id} = extractHeadingId(rawHeading)
and adding tests?That would simplify the testing of Regepx edge cases.
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.
Hmm, but we have separate test that with different cases, so it seems to me redundant extract new function (and its test), because in many ways it will duplicate this test.