Check out a free preview of the full Enterprise UI Development: Testing & Code Quality course:
The "Writing Custom ESLint Rules" Lesson is part of the full, Enterprise UI Development: Testing & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:

Steve creates custom ESLint rules with AST Explorer. The custom rules can be added to a project to help developers adhere to custom coding standards.

Get Unlimited Access Now

Transcript from the "Writing Custom ESLint Rules" Lesson

[00:00:00]
>> Most people have a passing familiarity with ESlint and have dealt with it or have it, whatever. In this case, we're dealing with large code bases, you probably already have it installed. What I wanna do is just kind of like plant a little bit of a flag on two things, which is one, what would it look like to create your own ESLint rule?

[00:00:24] And it's gonna be a very high level of what are some of the things you could do if you chose to. And then we'll do one more little thing where we'll look at, failing builds are great except that they're not, and so can you catch this stuff on pre-commit?

[00:00:37] And then that's kind of how we'll pull this into the station with two more little areas of exploration that I think are gonna be wildly helpful for you, but kind of things that are kind of deeper dives later on. And so ESLint, for those of you don't know how it works, it breaks your code into an abstract syntax tree, kind of what we saw with the code coverage.

[00:01:02] Breaks into a giant tree kind of a DOM that you can traverse up and down and look at things and labels on that's a function expression, that's an argument, that's a the variable declaration, so on and so forth. And a great way to get a sense of this is you can go to astexplorer.net, and you can say, last time I was doing this, let's jack up the font a little bit.

[00:01:28] Where I wrote some code, and it will show you the tree that is built from that, so you have a program. In this case I have three expression statements, we get more fun highlighting as I hover over stuff. Because again, if you look, this is how even the code coverage stuff works.

[00:01:47] It's expression statement we're calling something, that makes sense. It's got a callee, it has an argument, I hid location data. You can see its keeping track of where the start was cuz it looks there are new lines, it's just a string of text with new line characters, right?

[00:02:04] As it's parsing through and making sense of stuff, it can see all that stuff. I don't necessarily care, I care about the meaning of my program, right, we can minify stuff, it's how all this stuff works. What is super powerful here, I realized that it has local storage, so some of the surprise is ruined.

[00:02:27] But if I wanted to change this console error, it'll cross my code base, auto fix ' to a log error. I can literally write my own ESLint rules and either yell at you, not preferred, or kinda go through and get a sense of the structure of your code and can auto-fix.

[00:02:47] So let's say, and this is a little naive, there's import statements you'd have to deal with, right, and stuff like that, but the concept is there. Which is, I want to change console errors, I want to fetch the request from API. I will actually do that once, you can watch me live code instead of me pointing at a bunch of stuff.

[00:03:10] So let's start this over a little bit. But one of the great things is you can take whatever the questionable code is. So in this case, we wanna change fetch to request from API. So, cool, if I hover over that, you can see me move around, and I'm different things like string literal.

[00:03:30] So looks like this is an identifier, right, with the name fetch, great. So let's go ahead and let's just delete this, and we can say identifier. Cuz as it goes to the triggers just you setting up a bunch of rules for what you want to happen, right? In this case, the ESLint rule was not fired because nothing triggered it, that makes total sense.

[00:04:00] What am I choose to do here the actual node is passive, not Node.js, node as in node tree, DOM node, that kind of node. Where I have it, I can ask things about it, so I could say literally I know some things about it, right? I can see that it's got some arguments in TypeScript, you can do string, you can do a certain amount of template literals.

[00:04:29] But you could theoretically be like, hey that's, so you can do also, you can basically do anything you want with static analysis to your code using this, right? So, once you have the test, once you know the plan of action of what your particular code base needs, you can say all right, go there.

[00:04:45] If I find an identifier, which is basically a function name or something like that, you can do something if node.name, this is fetch in this case, right? Then we can start by just saying, we'll take this context here. We'll say context.report. And in this case, we'll say, The message is, Whatever, right, making it up.

[00:05:29] And so we can do that, cool in pass in the node. What is my issue here? Put a comma in there.
>> Misspelt identifier on line nine.
>> That will be my issue. This is why, to Mark's question of why I like TypeScript is because you are my TypeScript right now in this editor and I clearly.

[00:06:01] If it looks I can code, you take TypeScript away from me, and it's unclear if I can.
>> [LAUGH]
>> So cool, you can say at this point it is just yelling at us. It's like, hey, prefer requestFromApi, yeah, over fetch, and it's kinda cool that it will yell at you with the line and the character that that shows up on, right?

[00:06:26] And so you could theoretically, now you don't have to be pedantic during code reviews. You can set up the rules in this case, right? You can just pull it in as ESLint rule, you don't have to build infrastructure for this. If you have npm run lint or whatever, and you have the GitHub action we talked about earlier, right?

[00:06:43] You basically just inherit this rule and it will now break the build if they use fetch.
>> Wait, how does this get into your checks in your code?
>> Yeah, so if you have ESLint installed you can just add it as an extra rule.
>> Add what? What do you take?

[00:06:59]
>> Effectively this code down here that we're writing, right, this is how a rule is structured, right? Then you get in your .eslintrc, you can also pull in additional home written rules. So now this will yell at you, I think you can do, yeah, here you can see in the metadata, I can do problem or suggestion or something like that.

[00:07:23] And it depends on what one will break the build, one, GitHub has annotations now, one will just automatically, yay, you should consider doing this other thing, right and be a lint warning and-
>> Is there a way where it'll just do it for you?
>> I love that segue, is there a way where you can have it auto fix it for me?

[00:07:48] So you see that one of the metadata that I do have set is whether or not this is fixable, which I ask myself about myself all the time. And there are some nuances to this, but let's do the world's simplest case, which is cool. Let's say, all of our little weird helper methods are effectively API compliant with the very simplest use case here, right?

[00:08:14] And so cool, report that we had a problem absolutely, then what I would like you to do is let's apply a fix in this case. So we have one more method, we have the message, what node you put in your own location data too, but this is good enough for us.

[00:08:31] We'll say fix, right? And then you have this, fix it, sometimes APIs are weird. So we've got this fixer, and what it does is returns an array, which is all the weird changes you need to make to this thing. And to be clear, fixer, yeah, I'm just I'm glancing down at the methods right now.

[00:08:53] It's insert text after, insert text after a range, insert text before, remove from the code completely, so you could have it just remove the console logs if you want it, right? And you can go up and down the tree, you can look at its parent, you can do all sorts of stuff.

[00:09:11] You can replace some of the text, that seems useful if we wanna just replace all fetches with requests from API, right? You can do all sorts of things, you can replace a text range. You can do all sorts of interesting stuff, remove it completely, break the build. Let's do this array, it's just all these changes you wanna apply, so we'll do fixer.replaceText, in this case.

[00:09:35] On this particular node, right, cuz you might go up and down the DOM tree and find other stuff as well. If you've ever used jQuery, you secretly know how to do this better than it looks to you right now. And let's say we'll replace that with requests from API

[00:09:51]
>> Is this Node the top left window?
>> Which one?
>> Node, is that referring to the top left?
>> Node, is what is currently highlighted here, right? So each kind of token, I'm just taking one word, do you understand with another one, right? The parser goes through, and it's analyzing your code, so call expression is a node on the tree.

[00:10:14] It has some a children which are identifier and then the arguments, nodes on that tree further down. Think about a DOM node and that tree, that's effectively what is turning your code into. And so if you look at the output now, is that it didn't even fire an issue, it just auto replaced.

[00:10:36] So now, if you had texts of the word fetch in it, that wouldn't get replaced, it has to be a fetch identifier which is a function call, right? And it will go and only in those explicitly meaningful cases, will it go and swap it out?
>> Do you think there's a point though that it gets-

[00:10:59]
>> Yes.
>> Too much in the sense that here you're actually changing function because a functional reason versus source order or punctuation or whatever. I just feel like here, you'd almost want the engineers to really get used to writing requests from API versus-
>> Yes.
>> It'll just fix it for me.

[00:11:18]
>> Yeah, to be clear, this was the easiest example to write. Would I have this set to auto-fix? No.
>> Cool.
>> Right, absolutely not, would I hypothetically want to run this one once, right? Look at the diff, eyeball it and then maybe have it do a suggestion or warning after that, probably right?

[00:11:44]
>> So-
>> Cuz if I get this wrong, I have now taken somebody else's well-meaning code and shipped a bug to production, yeah.
>> If you wanted to, for example, just replace console logs, would you just fix it out, replace text, console log, and then just an empty string, or how does that look?

[00:12:04]
>> If I just wanna say it again, I was listening and thinking.
>> The one example you mentioned was, you could use it to just remove all console logs.
>> Yeah, there was remove. I can remove the node completely. Let's see, I don't know with the name, I think it will take the entire thing out.

[00:12:20] Let's find out together. So I want to take all the fetches out, I don't know, it's easy to do. So remove, I have to do a range on that one, remove node or token I just got passed in. Okay, yeah see I gotta get a little more nuanced and traverse the tree a little bit better on this one because that's a problem, but the nice part is the arguments is the next one down there, right?

[00:12:48] There was the literal, you can see as highlighted on the upper. I know you probably can't see. For those, paying attention at home, that is yellow. [LAUGH] Y'all can't see and everyone at home can see, I can see that as I'm over here, well, back when it worked.

[00:13:10] Yeah, you can see all right, you can see the yellow, I guess I moved the mouse while I was talking, then I couldn't see it. But yeah, you can kinda see and get a sense of it, so yeah. Yes, what you can live code, this kind of stuff is kind of squarely and stuff along those lines.