Linting & Fixing React with Vim, ALE, XO, and Prettier
tl;dr
Pick up the vim setup here, and the package.json here
Linting.
Ah... yes... that thing you need to add to a project, that you just don't want to add. But, there is this little voice in your head telling you to do this because... Just because.
It's like testing. I've always slacked to add either tests or linting because they felt like a lot of useless work.
Things changed when, a while ago I was coding in python. There are plugins like black and iSort that do lots of useful code cleaning routines. For example, isort sorts imports in a smart way. Black formats the code for me so that I no longer worry if this python line has wrong indentation.
This reminds me of this quote:
βArouse in the other person an eager want.β -- Dale Carnegie
In other words, I did not start using linters because it is best practice. Instead, I used a linter because it made my life as a programmer easier.
In other words, linters solved a major pain point.
Back to React. I already have Ale installed in vim, and it worked wonders with both black
and isort
. However, it did a horrible job with javascript. It always thought that all my files were broken.
That was because Prettier wanted to format things in a way that xo did not understand. I had installed both, but did not have time to make them talk to each other! This forced me to reload my vimrc
everytime I opened a new file, in order to disable the linting. Horrible UX!
So, today, I decided to invest time to fix this problem once and for all.
package.json rules
package.json
rules, rule!
It used to be that we needed to add .eslintrc
and .babelrc
and webpack.config.js
.nodemonignore
at the root of each project. Like... for real. This was one of the reasons why I ignored linting: It felt like a lot of work to maintain. Or, was I just finding lazy excuses?
But, I know that some of you out there use that excuse. C'monnn don't hide
Let's move on.
Apparently, package.json
can contain rules that would otherwise go in these files. Just look at this beauty:
// package.json
{
"name": "package-name"
"devDependencies":{
...
}
"nodemonConfig": {
...
},
"babel": {
...
},
"xo": {
"prettier": true,
...
},
"husky": {
...
}
}
Fantastic!
vim config
We have the configuration in package.json
, but how do we make vim
understand it? In other words, how will Ale read this config?
First things first. Ale provides an interface for both linters
and a fixers
.
let b:ale_fixers = {'python': ['black', 'isort'], 'javascript': ['xo']}
let b:ale_linters = {'python': ['pyflakes'], 'javascript': ['xo']}
Here, I am telling Ale to fix js with xo
and python with black
and isort
. Then, I'm telling it to lint python with pyflakes
, and javascript with xo
.
But, if I switched the js fixer to Prettier
, things no longer worked properly because xo
does not quite like what Prettier does. I could add xo
settings inside .vimrc
:
let g:ale_javascript_xo_options = "--plug=react --prettier"
But this is a bad idea because not everybody uses vim!
Now, it turns out, xo
can be told to use Prettier as a fixer, AND, this can be doen inside the package.json
:
// package.json
{
"xo": {
"prettier": true,
}
}
But, that's not enough. xo
uses eslint under the hood, and eslint does not understand react by default. It will throw errors at otherwise correct jsx statements:
import Header from './Header' // no-unused-vars
For this reason, we need to update the xo
configuration, and add a few dev dependencies.
"devDependencies": {
+ "eslint-config-xo": "^0.26.0",
+ "eslint-config-xo-react": "^0.19.0",
},
"xo": {
"prettier": true,
+ "extends": "xo-react",
}
By doing this, vim will finally fix js with Prettier
, and lint it with xo
.
But, the rules aren't quite enough.
But, the default rules did not always match my taste. For example, I did not agree with how it wanted to me to use kebab-case for my file names. Fortunately, this was easy to turn off:
"xo": {
"prettier": true,
"extends": "xo-react",
+ "rules": {
+ "unicorn/filename-case": 0,
+ }
}
And yup, that's the exact syntax used for eslint, and that's without needing a special eslint section, or .eslintrc
. Sweet!
Enforcing linting
Ok, so I have linting in place, but I had the hunch that, if I did not enforce it to happen, i might slack. So, I recalled that some folks would enforce linting as a git pre-hook.
I looked around and came across husky which does just that, and guess what? It can be configured inside package.json
!π
Here's how things look like:
// package.json
{
"scripts":{
+ "test": "xo --fix"
},
+ "husky": {
+ "hooks": {
+ "pre-commit": "npm test"
+ }
+ }
}
This way, before every commit, npm test
is run, and for now, this script will run xo.
I do not mind enforcing linting before every commit (Meaning that a commit won't be allowed to happen if linting fails). However, this can be enforced only before a commit. In which case, replace the script to:
// package.json
{
"husky": {
"hooks": {
- "pre-commit": "npm test"
+ "pre-push": "npm test"
}
}
}
Et voila! Now, regardless of what IDE you have, as long as Prettier
and xo
are installed globally, every developper on the team will be forced to lint.
ting here...