Using stylelint in a lit-element project
stylelint + lit-element = good times
As some of you know, lately I’ve been very much involved in a few linting projects as well as spending plenty of time contributing to the linters themselves.
One thing I’ve been missing so far in my own projects is the ability to lint my CSS so I can easily enforce best practices and, more importantly, detect potential problems in my rules/syntax.
This has been possible in projects which use CSS as-is for some time, using the excellent open source stylelint.
However, it has been a bit of a struggle when using a project like lit-element due to the fact that most or all stylesheets are contained within template literals (for several reasons I won’t go into here).
A PR or two recently got merged, though, so it is no longer such a struggle!
A lit element
Here’s an example element which uses lit, so you get an idea of how a stylesheet might look in this case:
class FooElement extends LitElement {
static get styles() {
return css`
:host { display: block; }
`;
}
render() {
return html`<h1>I'm some content!</h1>`;
}
}
The problem & solution
As mentioned before, the problem here is that the styles exist inside a template literal rather than an actual stylesheet (i.e. a css file).
Due to this, stylelint can’t lint it out of the box as it has no idea how to extract the CSS.
Here is where the very useful stylelint-processor-styled-components processor comes in handy and can be gently pushed to into supporting our needs.
This handy little processor allows stylelint to extract CSS from styled-components which look a bit like this:
const MyTitle = styled.h1`
color: hotpink;
`;
// This is rendered with hotpink color
<MyTitle>Some Title</MyTitle>
Looks familiar, right? If only we could tell the processor to look for
css
templates rather than styled
templates, right??
We’re in luck! This processor allows you to define which module and which import name to extract CSS from with a bit of config:
{
"importName": "css",
"moduleName": "lit-element",
"strict": true
}
Each of these has the following meaning:
importName
specifies which function to parse template strings formoduleName
specifies which module the function we specified must be imported from (to avoid parsing non-litcss
functions)strict
specifies that we only want to parse this specific import from the module we specified
The strict
flag is a recent addition from me. I added it because
the processor’s default behaviour is to extract CSS from all lit-element
imports, which means it will try (and fail…) to lint html
contents too.
This flag pretty much enables a little stricter parsing so only the named
import is parsed.
Setup
So this’ll be fairly straight forward!
Install
$ npm i -D stylelint
stylelint-processor-styled-components
stylelint-config-recommended
stylelint-config-styled-components
Configure
Create a .stylelintrc.json
:
{
"processors": [
[
"stylelint-processor-styled-components",
{
"moduleName": "lit-element",
"importName": "css",
"strict": true
}
]
],
"extends": [
"stylelint-config-recommended",
"stylelint-config-styled-components"
]
}
Simple. We enable the processor, then we tell it to only parse CSS
from lit-element’s css
template function and we extend the provided
configs to do some bits of leg work for us.
Run it!
$ npx stylelint "src/**/*.js"
Add it to your package.json
as a script, too, to make things a little
easier:
{
"scripts": {
"lint:css": "stylelint \"src/**/*.js\""
}
}
Wrap up
I was stuck for quite some time having literally no clue how on earth I lint some “pseudo-CSS” from inside some template expression. A few people seemed to be in the same situation, so I hope this helped you out and gave a good idea of the direction to head in.
Don’t forget to try out a code formatter too, such as prettier! You should totally make use of something like this instead of relying on stylistic lint rules.
I had such crazily customised lint rules until I came to the revelation that I just need to format code automatically instead of relying on mere humans. Now our development process is much smoother, highly recommended.
Anyhow, enjoy becoming as much of a pain as I am with my error-level max line length and JSDoc rules :x