Blog post: Markdownlint
Tags: markdown, markdownlint, introduction
Published: 2019-02-25 21:11:41

---
title: Blog post: Markdownlint
published: true
description: an introduction to Markdownlint
tags: markdown, markdownlint, introduction
---
As a follow-up to my post from the 11th. "[Giving a talk at CopenhagenJS](https://dev.to/jonasbn/blog-post-giving-a-talk-at-copenhagenjs--ll7)" I got my presentation accepted. So at the CopenhagenJS February meeting I gave a presentation of **Markdownlint**. The slides are available at [SlideShare](https://www.slideshare.net/jonasbn/markdownlint), but I thought I would do a small write-up on the topic of the presentation here.

First some _basic_ background history on **Markdownlint** currently comes in two implementations, the first one in **Ruby** inspired to the implementation in **Node**. The **Node** library in addition has a NPM package with a command line interface (`markdownlint`), where the **Ruby** package comes with the command `mdl`.

The **Node** based implementation is the one I use on a daily basis so my emphasis will be on this one. I am primarily working in **Visual Studio Code** now and therefor work with the plugin available there and the command line tool `markdownlint` both based on the **Node** implementation. Plugins for both **Sublime Text** and **Atom** exist.

Now let's look at some details. The linters both specify a set of rules, when applied can be raised when your **Markdown** is not adhering to the best-practice suggested. All the rules are identified by a key: `MDXXX`, where the number identifies a unique part, but instead of your having to work with and remember all these _cryptic keys_, you can also use human readable aliases.

For my presentation I had prepared a small [GitHub repository](https://github.com/jonasbn/markdownlint-presentation) with all the references used and some examples for pasting and copying, since slides are not always the best reference material for later.

So if we lift [the example from the mentioned repository](https://github.com/jonasbn/markdownlint-presentation#markdownlint-configuration), which is a configuration file for `markdownlint` (**Node**).

```json
{
    "default": true
}
```

This is the most basic setup for `markdownlint` it basically enables all the rules. Now lets get back to the rules.

An example, the rule `MD013` also has the alias `line-length`, which is easier to work with in for example a configuration file. So if we want to disable the line length rule for our _linting_, we would write as follows:

```json
{
    "default": true,
    "line-length": false
}
```

This is also [the configuration used in the example GitHub repository](https://github.com/jonasbn/markdownlint-presentation/blob/master/.markdownlint.json) as it looks at the time of writing :-)

The examples given above can be written to a file named: `.markdownlint.json`, this would mean that the command line tool `markdownlint` would pick up on this local configuration if available.

So if we enable `MD013` (`line-length`) again, we would get the following output:

```bash
$ markdownlint README.md
README.md: 7: MD013/line-length Line length [Expected: 80; Actual: 129]
README.md: 13: MD013/line-length Line length [Expected: 80; Actual: 104]
README.md: 14: MD013/line-length Line length [Expected: 80; Actual: 130]
README.md: 15: MD013/line-length Line length [Expected: 80; Actual: 167]
README.md: 17: MD013/line-length Line length [Expected: 80; Actual: 139]
README.md: 18: MD013/line-length Line length [Expected: 80; Actual: 127]
```

And just to make a swift comparison, `mdl` would report the same.

```bash
> mdl README.md
README.md:7: MD013 Line length
README.md:13: MD013 Line length
README.md:14: MD013 Line length
README.md:15: MD013 Line length
README.md:17: MD013 Line length
README.md:18: MD013 Line length

A detailed description of the rules is available at https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md
```

The command line is marvelous, but if you use **Visual Studio Code** like me you are in for a treat. The plugin for **Code** is based on the same implementation and hence your projects can use the `.markdownlint.json` configuration in your editor.

This requires [the Markdownlint plugin](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint). The following screenshot demonstrates the report on the same violations of the rule `MD013`

![Visual Studio Code with Markdownlint](https://raw.githubusercontent.com/jonasbn/markdownlint-presentation/master/images/vscode_with_markdownlint.png)

And to add even more _sugar on top_ check out how the configuration handling reacts.

![Visual Studio Code with Markdownlint JSON configuration file](https://raw.githubusercontent.com/jonasbn/markdownlint-presentation/master/images/vscode_with_markdownlint.json.png)

The plugin comes with builtin handling of the configuration file, so if you _hover_ over a rule, whether it is specified as `MDXXX` or the related _alias_, but will be visible. This is quite handy since it lets you write human readable configuration files, but still with the nifty key to the rule listing available for the respective Markdown linters.

But everything is not _hunky-dory_. The two implementations are _close_, this mean that you can use the two tools somewhat interchangeably, but they are not fully compliant and compatible.

The **Ruby** implementation implements 38 rules from `MD001` to `MD041` and a the rule `MD046` Whereas the **Node** implements 41 rules from `MD001` to `MD045`.

Meaning that the problematic rules are:

- `MD042`, only available in the **Node** implementation
- `MD043`, only available in the **Node** implementation
- `MD044`, only available in the **Node** implementation
- `MD045`, only available in the **Node** implementation
- `MD046`, only available in the **Ruby** implementation

You can see my two gists for [Ruby](https://gist.github.com/jonasbn/c2ecef9bfc53d456b512e26f9e28dde6) and [Node](https://gist.github.com/jonasbn/bf2f4dbc86eccb6f446d10f729aab214). This might of course change, but that is the state of things at the time of writing, do consult the original listing in the official documentation: [Ruby](https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md) and [Node](https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md).

If you are aware of the differences and your local **Markdownlint** configuration does not rely on any of the mentioned _holes_ you can get by with the both tools, but is easier to pick the one of the two which fits your situation the best. Since using both would require to rely on a single configuration so maintaining your rules become a additional work in your project.

Some rules can have additional configuration parameters in addition to enabling and disabling, I have not compared all of these possible configurations between the two implementations for the rule listings for the two implementations, so there might  hiding dragons in this area as well.

In addition to the command line and the editor integrations, you can add **Markdownlint** to your CI/CD setup, you might not be the only contributor to your **Markdown** products and you cannot assume other are have a similar process or the same tools.

This is a very basic configuration for **Travis CI** using **Markdownlint**

```yaml
language: node_js
node_js:
- '6'
install:
- npm install -g markdownlint-cli
script:
- markdownlint *.md
```

This works for **Node** based projects and you can possibly tweak the example for projects based on other languages.

The following is an example for an XML founded project:

```yaml
language: node_js
node_js:
- '6'

before_install:
  - sudo apt-get install -y libxml2-utils

install:
   - npm install -g markdownlint-cli

jobs:
  include:
    - stage: "Documentation test"
      name: "Markdown test"
      script: markdownlint README.md
    - stage: "Product test"
      name: "XML/XSD test"
      script: xmllint --schema epp.xsd xml/*.xml
```

Here the product is XML and is therefor _linted_ using another command line tool (`xmllint`) and the documentation is tested using `markdownlint`. Both examples have been lifted from my [GitHub repository](https://github.com/jonasbn/markdownlint-presentation). This renders this beautiful representation.

![Travis CI staged builds](https://raw.githubusercontent.com/jonasbn/markdownlint-presentation/master/images/travis-ci-markdownlint.png)

Just for the reference here is a untested example for what a **Travis** configuration for a **Markdownlint** setup using `mdl`  and in **Ruby** could look like.

```yaml
language: ruby
install:
- gem install mdl
script:
- mdl *.md
```

For the presentation I also talked about what a linter is and the rationale for using linters in general, I do not want to touch on this topic in this blog post, perhaps in a future post. Instead I have dug more into the details around the **Markdownlint** implementations. In the presentation I also mentioned **CommonMark** I decided not touch on this topic in this post since it would simply run too long, this might also be a topic for a post in the future.

Preparing the presentation and writing this post has identified several areas in which one could put further effort (projects, contributions, PRs).

- Implement `MD042` in the **Ruby** implementation
- Implement `MD043` in the **Ruby** implementation
- Implement `MD044` in the **Ruby** implementation
- Implement `MD045` in the **Ruby** implementation
- Implement `MD046` in the **Node** implementation

- Experiment with more advanced **Travis** configurations for more variations of projects and repositories like **Rust**, **Perl** whatever might come up

- Extend the capabilities of the **Node** and **Ruby** implementations so configuration files can be shared and then easily maintained

- Finally the grand finale - contact the authors of the two projects an suggest consolidating a standard for Markdownlint and rules, so the different implementations can be compared based against a baseline outlined as a standard. This is very much inspired by the work made for **CommonMark** and not completely undoable because of the awesome authors behind the existing implementations and the fact that the **Node** implementation is inspired and based on the **Ruby** implementation.

I might propagate a lot of the information from this blog post to the mentioned [GitHub repository](https://github.com/jonasbn/markdownlint-presentation), do however checkout the repository for a list of references used for this post and the initial presentation and possible future findings, tips and example will go there.

Happy **Markdown** writing and linting...