@microflash/rehype-slugify
v2.0.0
Published
Rehype plugin to add id attributes to headings
Maintainers
Readme
rehype-slugify
rehype plugin to add ids to headings using a slugger of your choice
Status: Feature Complete
No new features are planned. Updates will primarily focus on bug fixes, security patches, and performance optimizations.
Contents
What's this?
This package is a unified (rehype) plugin to add ids to headings. It looks for headings (<h1> through <h6>) that don't yet have ids and adds id attributes to them based on the text they contain. You'll have to provide an implementation of the algorithm that does that, such as github-slugger, @sindresorhus/slugify, and so on.
When should I use this?
This plugin is useful when you have relatively long documents and you want to be able to link to particular sections.
A different plugin, rehype-autolink-headings, adds links to these headings back to themselves, which is useful as it lets users more easily link to particular sections.
This plugin allows you to use your own slug algorithm to handle usecases that rehype-slug doesn't allow, such as, support for additional languages, custom replacements, and so on.
Install
This package is ESM only. In Node.js (version 16.0+), install with npm:
npm install @microflash/rehype-slugifyIn Deno, with esm.sh:
import rehypeSlugify from 'https://esm.sh/@microflash/rehype-slugify'In browsers, with esm.sh:
<script type="module">
import rehypeSlugify from 'https://esm.sh/@microflash/rehype-slugify?bundle'
</script>Use
Say we have the following file example.html:
<h1 id="some-id">Lorem ipsum</h1>
<h2>Dolor sit amet 😪</h2>
<h3>consectetur & adipisicing</h3>
<h4>elit</h4>
<h5>elit</h5>And our module example.js looks as follows:
import { readFileSync } from 'node:fs'
import { unified } from 'unified'
import rehypeParse from 'rehype-parse'
import rehypeStringify from 'rehype-stringify'
import rehypeSlugify from '@microflash/rehype-slugify'
import GithubSlugger from 'github-slugger'
const slugger = new GithubSlugger()
const options = {
slugify: (v) => slugger.slug(v),
reset: () => slugger.reset(),
}
async function main() {
const html = String(
unified()
.use(rehypeParse, { fragment: true })
.use(rehypeSlugify, options)
.use(rehypeStringify)
.processSync(readFileSync('example.html'))
)
console.log(html)
}
main()Running that with node example.js yields:
<h1 id="some-id">Lorem ipsum</h1>
<h2 id="dolor-sit-amet-">Dolor sit amet 😪</h2>
<h3 id="consectetur--adipisicing">consectetur & adipisicing</h3>
<h4 id="elit">elit</h4>
<h5 id="elit-1">elit</h5>Note that <h1> keeps its existing id. The plugin never overwrites ids that are already present. Duplicate headings are disambiguated by the slugger (by appending a numeric index in this example), not the plugin.
API
The default export is rehypeSlugify.
Options
slugify(text)((text: string) => string, required): receives the text content of a heading and returns its slug. The plugin does nothing if this option is not a function.reset()(() => void, optional): called once before each document is visited, giving the slugger a chance to clear internal state such as a seen-slugs counter. Omit it if your slugger is stateless.
Examples
Example: integrate with @sindresorhus/slugify
Say we have the following file example.html:
<h1 id="some-id">Lorem ipsum</h1>
<h2>Dolor sit amet 😪</h2>
<h3>consectetur & adipisicing</h3>
<h4>elit</h4>
<h5>sed@do</h5>And our module example.js looks as follows:
import { readFileSync } from 'node:fs'
import { unified } from 'unified'
import rehypeParse from 'rehype-parse'
import rehypeStringify from 'rehype-stringify'
import rehypeSlugify from '@microflash/rehype-slugify'
import { slugifyWithCounter } from '@sindresorhus/slugify'
const slugify = slugifyWithCounter()
const options = {
slugify: (v) => slugify(v, {
customReplacements: [
['@', 'at']
]
}),
reset: () => slugify.reset(),
}
async function main() {
const html = String(
unified()
.use(rehypeParse, { fragment: true })
.use(rehypeSlugify, options)
.use(rehypeStringify)
.processSync(readFileSync('example.html'))
)
console.log(html)
}
main()Running that with node example.js yields:
<h1 id="some-id">Lorem ipsum</h1>
<h2 id="dolor-sit-amet">Dolor sit amet 😪</h2>
<h3 id="consectetur-and-adipisicing">consectetur & adipisicing</h3>
<h4 id="elit">elit</h4>
<h5 id="sedatdo">sed@do</h5>Security
Use of @microflash/rehype-slugify can open you up to a cross-site scripting (XSS) attack as it sets id attributes on headings, which causes what is known as "DOM clobbering". Please use rehype-sanitize and see its Example: headings (DOM clobbering) for information on how to properly solve it.
Related
rehype-slug: plugin to generate slugs usinggithub-sluggerrehype-autolink-headings: add links from headings back to themselves
