Code Transformation and Linting with ASTs Challenge 2: Solution
Transcript from the "Challenge 2: Solution" Lesson
>> Kent C. Dodds: Okay, great. Let's go ahead and solve this. In my solution, I actually stick with identifier and I verify the parents to do the full on solution. And what you probably have in your solution, whatever you ended up using is a lot of dot this, dot this, dot this, dot this.
[00:00:17] Comparing is this thing equal to this other thing. So I'm gonna show you a little utility that I wrote and that we'll be using for the rest of the workshop and I'm just gonna copy it to save us a little bit of time. But it is this function called looks like.
[00:00:34] So what this does is it's like a deep equal, basically. It's a pretty simple function, but in a second, when I use it, you'll see that it actually simplifies our stuff quite a bit. And I think that's something like this exists 1 million times on NPM. But it's in my plug in so I can use it in AST explorer.
[00:01:01] So I'm gonna copy all this over to AST explorer, I'm gonna bring over a couple of the use cases or all of the user, a quick thing. I'll ust pull all these over and just change this out a little bit. And so I should have this done before we even start up.
[00:01:36] Good lesson for next time. Okay, so these are the invalid and these are valid. And we are getting a bunch of warnings, but we're getting warnings on the valid ones in addition to the invalid ones. So we want to make our solution a little bit more robust. So what we're going to do is let's take a look at the valid ones and see how those differ from the invalid ones.
[00:02:10] So, in both cases, we have an identifier that has the name of console. But the difference here is in the parent, the parent has an object and that object is something totally different. Actually, let's move over to the invalid case and see the things that are common among the invalid and then we can make sure that we're not hitting our valid cases.
[00:02:39] That would be a lot better. So the console has a parent that's a MemberExpression, and that MemberExpression is using a property or has a property that's an identifier that's one of our disallowed functions for console. So I'm gonna go ahead and make disallowedMethods and we'll do "log', and 'info', 'warn', and there are a bunch more and we can add those pretty easily later on.
[00:03:12] And so instead fo doing this, we're going to do something with this looks like function that I have. So I will say isConsoleCall = looksLike. And we're going to pass the node and determine whether the node looks like some other object. And then we'll use that in here instead.
[00:03:39] Actually, let's first say that the name is console, and now these two things are functionally equivalent, what we had before and what we have now. And so these this identifiers and older looking at has a name console and then each one of these nodes here will console like this so you can see this.
[00:04:01] Cuz you don't really see that in the AST Explorer. But each one of these has a parent, and that parent has a type of member expression, and that MemberExpression also has a parent that's of type call expression. And then that member expression also has a property that has a type of identifier and a name of console.
[00:04:32] So that's the type of node that we're looking for. And so that's what we're gonna write out here. So we're gonna say parent, and the type of our parent needs to be a MemberExpression, so that's going to actually filter out this, because this isn't a member expression. So we won't see that in here anymore, which is pretty cool.
[00:04:56] And this should also have a parent that is a call expression. So they type is a call expression. And then it should have a property. So where we'll look at that member expression has a property that is one of our disallowed arguments. Or has a name with one of our disallowed methods.
[00:05:23] So this is where things get a little bit tricky. So far it's just been literal stuff. But if there's something that's dynamic, like it needs to have one of these disallowed methods, then our looks like function can allow us to use a function so that will give us the value and we'll say disallowed methods includes the value.
[00:05:46] And that is it. So that looksLike function really makes things a lot simpler, right? The alternative is a lot of checking whether properties exist and stuff. Okay, let's go ahead and we'll copy this, make sure this actually does pass tests, and we're good, and then also fill out the feedback and all of that elaboration stuff.
[00:06:17] Any questions?
>> Speaker 2: So that call expression check is to actually check on the function?
>> Kent C. Dodds: Yes.
>> Speaker 2: If you it, it would just be a member expression.
>> Kent C. Dodds: Yeah, and we didn't really have a test case for that, so I probably shouldn't have even included that, but that would be a console.log.
[00:06:35] And if I didn't have this in here, then that console log would not be filtered out, so good point. Any other questions?
>> Speaker 3: From your experience, have you learned a best practice in regards to enforcing a rule like this? For instance, I solved mine with another expression, a keyword function rather than an identifier.
>> Kent C. Dodds: Yeah, it really kinda depends. It would look similar to this solution if we use this looks like function too if you used a member expression instead. So the difference is whether you're going up the tree to find the parent or whether you're going down the tree to find the children or the relevant identifier information.
[00:07:23] So the only real difference or consideration might be that one performs better because there are way more identifiers in your program than there are member expressions, and so the member expression one would probably be faster. And so, yeah, if that was really an issue, I try not to think of performance in especially context like this where it probably really doesn't matter but it could.
[00:07:50] If it's just as easy to do it one way as it is the other, may as well do the faster one. So yeah, you could totally do a member expression or even a call expression if you're filtering out the call expressions. Yeah.
>> Speaker 4: Not to hit too much on performance, cuz as you said-
>> Kent C. Dodds: Yeah.
>> Speaker 4: It's not necessarily important in this context, but is it cheaper to reach up the tree or down the tree?
>> Kent C. Dodds: It doesn't make a difference. Well, all that matters is how often is your function gonna be called, and if you're using an identifier, it'll be called a lot because there are tons of them.
[00:08:17] But if you use call expression or function declaration then it won't be as much. Yeah.
>> Speaker 4: There is a question from online just working do you find what the method looks like?
>> Kent C. Dodds: Yeah, yeah. So when we bumb up to the next exercise, your plugin will have the looks like function.
[00:08:33] So, yeah, I literally just wrote this last night and it made all of my examples some much simpler. So that's kinda nice shoutout to cowboy for creating this. It's primitive in a gist that I just ripped. And it's great. I want at least one person to tell me a key takeaway that they wrote down in their elaboration piece.
[00:08:59] What did you learn from this, from working with this plugin.
>> Speaker 5: Writing these rules is just like any other program you'd write. So you'd wanna dry it up. You'd wanna write utility functions. Wanna make it as readable as possible.
>> Kent C. Dodds: Absolutely. And I'm really glad you got that takeaway because that's what I'm trying to communicate in this workshop.
[00:09:46] And so if you're struggling with it, that's fine. It takes a while to get used to these things. But at the same time, it is something that you can learn. It's not something for computer science only, like really smart writing compilers type people. This is for everybody, and anybody can use ASTs.
[00:10:06] So yeah, it's just a program. Yeah, and dry up, and utility functions, and stuff like that.