markdown/no-missing-link-fragments
Disallow link fragments that do not reference valid headings
[usage](#usage) jumps to the "Usage" heading in the same document. When the heading is renamed, the link stays and silently stops working.
A heading's anchor is computed the way GitHub does it: lowercased, punctuation removed, spaces turned into -, and -1, -2 added to repeats. An id or name attribute in HTML, a heading written in HTML and {#custom-id} at the end of a heading count too. #top and line references like #L10-L20 are always allowed.
Configure#
Off by default. It needs an ESLint config that says which files are in the language:
eslint.config.js
import markdown from "@eslint/markdown";
export default [
{
files: ["**/*.md"],
plugins: { markdown },
language: "markdown/commonmark",
rules: { "markdown/no-missing-link-fragments": "error" },
},
];It also works with markdown/gfm.
Examples#
example.md1 problem
# Getting started
See [usage](#usage)Link fragment #usage does not reference a heading or anchor in this document..example.mdNo problems
# Getting started
See [the start](#getting-started), [the API](#api), [the FAQ](#questions) or [the top](#top).
## Reference {#api}
<a id="questions"></a>Options#
| Option | Default | What it does |
|---|---|---|
ignoreCase | true | Ignore case, so #Usage matches the anchor usage. |
allowPattern | "" | A regular expression. Fragments that match it are not reported. |
example.mdwith
{ "ignoreCase": false }1 problem# Usage
See [usage](#Usage)Link fragment #Usage does not reference a heading or anchor in this document..example.mdwith
{ "allowPattern": "^tab-" }No problemsSee [the second tab](#tab-2).