Josh Goldberg
Sketches of a surveillance camera

Configuring Markdownlint Alongside Prettier

Jan 25, 20235 minute read

Using the common markdown linter and disabling any rules that would intersect with dedicated formatters such as Prettier.

As a maintainer of a popular linting tool, typescript-eslint, I often get asked how I configure linting for my projects. My JavaScript code gets ESLint; my TypeScript code gets typescript-eslint added on top, and my Markdown code gets Markdownlint.

For all three of those tools (and others like them), keep in mind that linters are not good formatters. Linters are great at catching bugs, enforcing stylistic decisions such as naming, and detecting violations of best practices - but they’re inherently terrible at formatting your code. Whenever you enable a linter, you should make sure it isn’t also trying to format your code.

Markdownlint

I generally use markdownlint with markdownlint-cli for linting my Markdown code. They’re fast and straightforward to configure. You can get started with them by installing markdownlint-cli to your project’s devDependencies:

npm i -D markdownlint-cli

…and then adding a script to run it on all your .md files:

{
	"scripts": {
		"lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\""
	}
}

Markdownlint and Prettier

Until recently, Markdownlint didn’t have a built-in way to disable all formatting lint rules the way ESLint does with eslint-config-prettier. Previously, whenever I configured Markdownlint, I’d add at least the following line in my .markdownlint.json to make it not try to apply formatting logic:

// .markdownlint.json
{
	"line-length": false
}

Unfortunately, depending on how you configure Prettier (or any other auto-formatter), there are over a dozen Markdownlint rules that might conflict with formatting!

// .markdownlint.json
{
	"blanks-around-fences": false,
	"blanks-around-headings": false,
	"blanks-around-lists": false,
	"code-fence-style": false,
	"emphasis-style": false,
	"heading-start-left": false,
	"hr-style": false,
	"line-length": false,
	"list-indent": false,
	"list-marker-space": false,
	"no-blanks-blockquote": false,
	"no-hard-tabs": false,
	"no-missing-space-atx": false,
	"no-missing-space-closed-atx": false,
	"no-multiple-blanks": false,
	"no-multiple-space-atx": false,
	"no-multiple-space-blockquote": false,
	"no-multiple-space-closed-atx": false,
	"no-trailing-spaces": false,
	"ol-prefix": false,
	"strong-style": false,
	"ul-indent": false
}

style/prettier.json To The Rescue

We shouldn’t be expected to add any or all of those configurations to every project that uses both Markdownlint and a dedicated formatter. That’s why I added a style/prettier.json extension option to Markdownlint. Now, instead of adding all those lines to your .markdownlint.json, you can include a single extends directive:

// .markdownlint.json
{
	"extends": "markdownlint/style/prettier"
}

Note that because that file is provided in the markdownlint package, you’ll want to explicitly have markdownlint installed, even though it’s already a dependency of markdownlint-cli:

npm i markdownlint@latest markdownlint-cli@latest -D

You can read more about configuring Markdownlint alongside Prettier in Markdownlint’s Prettier documentation.

Aside: Sentences Per Line

By the way, I wrote my own sentences-per-line Markdownlint rule. It enforces that each sentence is on its own line (or, in other words, that no line contains more than one sentence). I like keeping to one sentence per line because it enforces simpler Git diffs and shorter, more readable lines.

npm i -D sentences-per-line
markdownlint --rules sentences-per-line

Putting It All Together

You can see a repository that uses Markdownlint with formatting rules disabled over at JoshuaKGoldberg/template-typescript-node-package. Its package.json lint:md script runs Markdownlint with sentences-per-line on all Markdown files (including those in the .github/ directory, since dotfile folders aren’t linted by default):

{
	"scripts": {
		"lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\" --rules sentences-per-line"
	}
}

Its .markdownlint.json extends from markdownlint/style/prettier and disables a couple more rules I tend to prefer off in my repositories:

{
	"extends": "markdownlint/style/prettier",
	"first-line-h1": false,
	"no-inline-html": false
}

Anyway, thanks for reading. I hope you found some of this useful! 🧹