Markdown's syntax is easy to learn, and even though the syntax is forgiving, linting can help you avoid unexpected issues.
markdownlint and its CLI tool markdownlint-cli
is the most common tool used for linting Markdown files. As of writing, markdownlint validates Markdown files against a list of 53 rules . markdownlint-cli
can be installed and used locally, but it's also easy to integrate into CI/CD pipelines.
See "Common Markdown Mistakes" for a list of the most common Markdown syntax mistakes I see people make.
Common Markdown Mistakes
Nov 9, 2020 · 4 min read
Markdown provides simple syntax for writing structure documents, but most written markdown would not pass a linter check. Here's a list of 11 common syntax mistakes and how to fix them.
Usage
markdownlint-cli
has instructions for how to install via npm
and Homebrew , but I'll focus on running it via Docker for OS portability. You can run markdownlint-cli
in a container to lint Markdown files in your current directory like this:
docker run --volume "$PWD:/workdir" \
ghcr.io/igorshubovych/markdownlint-cli:latest \
"**/*.md"
If you want to disable certain markdownlint rules, you can do so like this:
docker run --volume "$PWD:/workdir" \
ghcr.io/igorshubovych/markdownlint-cli:latest \
--disable MD013 MD033 MD041 -- \
"**/*.md"
Note the required --
which terminates the list of space-separated rule names.
Example output
Let's define a markdown file sample.md
with the contents:
#Sample
This is a sample Markdown file.
It has a number of formatting issues.
### Sub-heading
Here's the example output from markdownlint-cli
:
$ docker run --volume "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md"
sample.md:1:1 MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#Sample"]
sample.md:1 MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#Sample"]
sample.md:4 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2]
sample.md:7:1 MD019/no-multiple-space-atx Multiple spaces after hash on atx style heading [Context: "### Sub-heading"]
Adding to GitHub Actions
Linters become a lot more powerful when you add them to your CI pipelines. A linter without enforcement will surely be ignored over time.
Here's a sample of how you could add markdownlint-cli
to a GitHub Actions .github/workflows/test.yml
(feel free to change the filename):
name: Project CI
on:
pull_request:
types:
- opened
- synchronize # HEAD has changed, e.g. a push happened
- reopened
jobs:
markdown-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker run --volume "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md"
Then you can add a branch protection rule to prevent pull request merges without certain passing jobs.