Lint Markdown internal links
Lint Markdown internal links
Use Remark. It’s the only linter I’m aware of that can check relative links across files.
I assume you already have node and npm installed. The versions I have currently are:
$ npm --version
7.19.1
$ node --version
v18.14.2
Use the following components:
- remark inspects and changes markdown via plugins.
- remarklint collects linting plugins for remark.
- remark-validate-links checks that markdown links and images point to existing local files and headings in a Git repo.
Basic Linting
A quickstart from the remarklint documentation.
Set up:
cd "$(mktemp --dir)"
npm install remark-cli remark-preset-lint-consistent remark-preset-lint-recommended
mkdir doc
cat > doc/example.md <<"EOF"
1) Hello, _Jupiter_ and *Neptune*!
EOF
npm exec -- remark doc/ --use remark-preset-lint-consistent --use remark-preset-lint-recommended
Output:
doc/example.md
1:1-1:35 warning Marker style should be `.` ordered-list-marker-style remark-lint
1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint
1:25-1:34 warning Emphasis should use `_` as a marker emphasis-marker remark-lint
⚠ 3 warnings
Structural integrity checking
Or relative link validation. A quick start from the remark-validate-links documentation.
npm install remark-validate-links
cat > doc/example-link.md <<"EOF"
# Alpha
Links are checked:
This [exists](#alpha).
This [one does not](#does-not).
# Bravo
Headings in `readme.md` are [checked](readme.md#no-such-heading).
And [missing files are reported](missing-example.js).
Definitions are also checked:
[alpha]: #alpha
[charlie]: #charlie
References w/o definitions are not checked: [delta]
EOF
# remark-validate-links fails unless it runs in a git repo with an origin remote.
# The README shows how avoid that by setting `options.repository`. I don't know
# how to set that via npm CLI.
git init .
git add remote origin localhost
npm exec -- remark doc/ --use remark-preset-lint-consistent --use remark-preset-lint-recommended --use remark-validate-links
Output:
doc/example-link.md
6:6-6:31 warning Link to unknown heading: `does-not` missing-heading remark-validate-links
10:29-10:65 warning Link to unknown file: `readme.md` missing-file remark-validate-links
10:29-10:65 warning Link to unknown heading in `readme.md`: `no-such-heading` missing-heading-in-file remark-validate-links
11:5-11:53 warning Link to unknown file: `missing-example.js` missing-file remark-validate-links
15:1-15:16 warning Found unused definition no-unused-definitions remark-lint
16:1-16:20 warning Found unused definition no-unused-definitions remark-lint
16:1-16:20 warning Link to unknown heading: `charlie` missing-heading remark-validate-links
18:45-18:52 warning Found reference to undefined definition no-undefined-references remark-lint
doc/example.md
1:1-1:35 warning Marker style should be `.` ordered-list-marker-style remark-lint
1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint
1:25-1:34 warning Emphasis should use `_` as a marker emphasis-marker remark-lint
⚠ 11 warnings
From here, it’s an exercise for the reader to integrate this into pre-commit and other development workflows.
Update 2023-05-26
YOu need to npm init
the directory otherwise the packages are actually installed in the home folder. And then not usable as exec!
If you follow the remark quickstart after npm init
, you get this error: “Cannot use import statement outside a module.”
https://stackoverflow.com/questions/58384179/syntaxerror-cannot-use-import-statement-outside-a-module
It happens because remark is an “ESM only” package.
Someone tries to explain what that means to CommonJS users:
Pure ESM package https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c