Linting styles in JavaScript with stylelint
December 14th, 2016
I recently co-created a styling library for React called styled-components
, which let’s you write actual CSS in your JavaScript. The issue is that you write this CSS inside strings and without selectors:
const Button = styled.button`
background: tomato;
border-radius: 3px;
color: white;
font-size: 1em;
@media screen and (min-width: 750px) {
font-size: 1.1em;
}
`;
stylelint
is a linter for CSS, meaning it helps you make sure you’re writing good code. It’s like eslint
, except for CSS instead of JS. stylelint
has a huge amount of rules and a vibrant ecosystem, which means it’s the best choice for CSS linting right now.
Leveraging the power and ecosystem of stylelint
to allow developers to lint their CSS in styled-components
seems like a great thing to do! How can we make that happen?
First try
The first idea was to leverage the power of eslint
, the JavaScript linter, to parse the JavaScript and then run stylelint
from there on the extracted CSS string. A lot of people already use and rely on eslint
, so providing an eslint rule that does this would make it easy to adopt and use.
Sadly, that doesn’t work. After building a first prototype I noticed that for some unknown reason the stylelint results weren’t coming back to the CLI. I was executing stylelint
on the right code, but the user couldn’t see the result! I asked around and somebody in the eslint
Gitter chatroom explained to me why:
eslint
rules have to be sync, but stylelint
has to be run async.
Ugh, super annoying! I sent out a frustrated tweet on Twitter, to which thankfully @stylelint replied quickly:
@mxstbr @andreysitnik How about writing a stylelint processor that finds these strings and passes them to stylelint?
— stylelint (@stylelint) October 19, 2016
Huh, processors? Never heard of those…
Processors
As it turns out, stylelint
has support for custom “processors”. Processors allow developers to add support for filetypes which stylelint
normally doesn’t understand. They get the file contents, and have to return a CSS string which stylelint
can lint.
One of the most common use cases would be linting CSS inside HTML (in <style>
tags) or Markdown. (in code blocks) There are processors for that, which basically take the HTML/Markdown file you pass to stylelint
, parse it, take the CSS code from the <style>
tags or fenced code blocks, and pass it to stylelint
.
That sounds exactly like what we need! How do I write one?!
Writing a processor
The main file of your processor has to export a function which returns an object with two keys, code
and result
:
// index.js
module.exports = function (options) {
return {
code: code,
result: result,
};
}
Both values of these properties have to be functions:
code
is a function that gets passed the contents of the file the user wants to lint, and should return the extracted CSS code forstylelint
to lint.result
is a function that gets passed the linting result as an object, which you can optionally mutate to adjust things like source maps or error messages.
// index.js
module.exports = function (options) {
return {
code: function (input, filename) {
return extractCSS(input);
},
result: function (stylelintResult, filename) {
return fixSourceMap(stylelintResult);
},
};
}
In the code
step, my styled-components
processor parses the incoming JavaScript files with babylon
. (the parser Babel uses under the hood) Then it gets the content of all the tagged template literals that are styled-components
related and fixes the resulting CSS to be “real” CSS with selectors etc. before passing it to stylelint
.
In the result
step I then replace each mention of “brace” in all error messages with “backtick”, since you generally don’t have braces when writing styled-components
, and adjust the source map to fit the JavaScript file again.
This is what that looks like now that it’s done 🎉
While it works, as you can imagine there’s quite a few edge cases and I definitely haven’t fixed all of them yet. Feel free to take a look at the source and if you use styled-components
please try it out and let me know how it went!
To see what a really well tested, commented and perfectly working processor looks like take a look at the excellent stylelint-processor-markdown
by @davidtheclark for some inspiration!
Liked this post? Sign up for the weekly newsletter!
Be the first to know when a new article is posted and get an inside scoop on the most interesting news and the freshest links of the week. (I hate spam just as much as you do, so no spam, ever. Promise.)
Tweet this article