Adding a linter to 11ty

Adding a linter to 11ty

Lint is a much maligned and feared tool in the world of the programmer. From the early days in C and C++, where the original lint tools would drown you in notifications and complaints about your code so it was difficult to see the bugs for the trees, up to JsLint and the more modern crop, programmers have been wary of this tool.

Despite the amount of overwhelm they can create in the unwary, they are very useful in seeing things we cannot easily see from our close entwinement with our code day to day. Linters are good at seeing the details but also being able to survey the whole codebase and spot problems we may never see until they come back as defects.

In 11ty, adding a linter is fairly easy and I added one recently, not to overwhelm but to catch a couple of specific issues that I wanted to make sure didn't make it to the final site.

Like most things in 11ty, it starts with the elevent config:


eleventyConfig.addLinter('page-content', require('./\_11ty/linters/page-content'));

The name of the linter doesn't matter too much, the code (which follows) is the important bit but the other line I added to the config very much does.


When the linter runs, it outputs whatever it reports in line with the files as they are written to disk. 11ty defaults to being quite chatty in the terminal and lint errors do not stop the build process so it can be difficult to spot them during a normal build unless you go scrolling back through the output looking for them.

Setting quiet mode on 11ty means that a lot of this noise is removed so that any lint errors become immemdiately obvious when they occur.


With the config out of the way, let's move on to the lint code itself. The 11ty linter docs are brief but to the point and I found them good enough to get me started on my linter. My main concern I was trying to eliminate with the linter was badly formed markdown tables. More than other markdown constructs, if the syntax isn't just right with a table, the result can just be some dashes and pipes and a load of text spread out across the page rather than the nice neat table you were expecting.


The arguments to each linting function are the page content, the path of the source document and the path of the generated output.

module.exports = function(content, inputPath, outputPath) {

if (!outputPath.endsWith(".html")) return;

let count = 0;

let tableMarkup = /\| ---/gi;

if (content.match(tableMarkup)) {
console.error(`\t${count} - Incorrectly formatted table (${inputPath})`);

For me, I only wanted to process finished html pages so had to include a guard for that. Then a short regex, tested using my go-to tool regular expressions 101. What I particularly like about regex101 is that it explains all the parts of the regular expression you give it, so it can make it easier to review a cryptic regex that you have long since forgotten.

If we find any matches in the content string we report an error with a count of how many we find and which source file this is (again no build failure here) and move on to the next file in line.