Code Transformation and Linting with ASTs

Challenge 10: Solution

Kent C. Dodds

Kent C. Dodds

Professional Trainer
Code Transformation and Linting with ASTs

Check out a free preview of the full Code Transformation and Linting with ASTs course

The "Challenge 10: Solution" Lesson is part of the full, Code Transformation and Linting with ASTs course featured in this preview video. Here's what you'd learn in this lesson:

Kent walks through the solution to Challenge 10.


Transcript from the "Challenge 10: Solution" Lesson

>> Kent C. Dodds: So what we're looking for is we want to change, yeah, basically change. Well, here it'd be handy to have what we're ultimately looking to have, we'll just stick that right there. So we wanna change this first line to that, but not change this, right? So looks like we want to really change this CallExpression, well, pretty much this whole thing.

But we're gonna start out by looking for this CallExpression and see if that CallExpression is part of a MemberExpression that is part of the CallExpression. Lots of words, let's get some code. So we're gonna start out with the CallExpression with our path. And we're going to filter out anything that isn't a CallExpression that looks like this.

So to do that, we're gonna use a looksLike. So we'll say const isJqueryCallExpression, say, looksLike path and a giant object here. So the node callee should be the dollar sign. Let's move this down a little bit, more space here. So here's our CallExpression, has a callee, and the name is dollar sign.

So we can say node callee.
>> Kent C. Dodds: So name, that is the dollar sign. Okay, that actually, just right there, we'll apply that, if isJqueryCallExpression, we'll return. If it's not, we console.log the path. We should only see one. Yep, that filters out the foo.hide, so we're great. In addition to that, I'm gonna just add another little feature into what we've got here.

We don't want to add in anything that's like forEach. Maybe this would be something to add to our plugin eventually, but right now, we don't want to select that. And with what we have right now, we're gonna get both of those. So let's filter that out as well to just be the stuff that we're interested in.

So in addition to just doing the node, we're gonna actually take that, let's see what we want, this one. This is the CallExpression that we're visiting. We wanna take this one and check its parent path that the parent path's property has a name with hide.
>> Kent C. Dodds: So we'll say, not on the node, but parentPath,

>> Kent C. Dodds: I'm gonna do things differently than my solution, so let me just check. The type is a,
>> Kent C. Dodds: Yeah, the type of the parentPath is a MemberExpression.
>> Kent C. Dodds: And it has a property. Actually, we want the parent, not the parentPath. Because we want the parent node so we can inspect this stuff.

So the type of the parent is a MemberExpression and the property is going to be an identifier that has a name of hide. So now let's see our console logs. We're limiting it down to exactly what we need, perfect.
>> Kent C. Dodds: Now, let's see, so if we look at what it is that we're gonna be replacing with, this is a perfect use case for using a template.

So I'm gonna copy this and I'm gonna give myself a templateString.
>> Kent C. Dodds: And I use single quotes because that's the way I roll. And that can be our templateString. But this el thing needs to be the same thing that's being referenced in here, so that's gonna be the dynamic part of our template.

So we can do el or element. We'll just do all caps EL. It doesn't actually have to be all caps. What the important piece is, is that when you compile this template into an AST, that the key for the data that you're passing in needs to be the same.

So it's pretty common to just use all caps. Okay, great, and I'm pretty sure we have template right there. So let's create our const.
>> Kent C. Dodds: I don't know, it's our assignmentBuilder,
>> Kent C. Dodds: template with the templateString. And then we'll get our assignment with the assignmentBuilder. And we'll pass it our data for the EL.

That's going to be the thing that's inside of our CallExpression, so the argument. And it needs to be an identifier. We can reuse that identifier if we want to. But things can get messy if we reuse existing nodes because maybe some other plugin is gonna remove that node or something like that.

So if you don't absolutely have to reuse the same node as something else that exists, it's nice to clone it. And I'm pretty sure that there is a clone function that we could use. Now, let's just find out really quick, console.log to path.
>> Kent C. Dodds: And luckily, this is alphabetical, I think.

Yeah, it doesn't look like there's a clone here, but there might be something off of Babel core that we could look at. But we'll just make one ourselves really quick so I don't waste your time looking for that, and,
>> Kent C. Dodds: Actually, my solution is reusing the same node, so there's that.

Okay, we'll do path.node.arguments. So this is a CallExpression path. And so CallExpressions have arguments. And there is the argument, it is that el. So I'm gonna take the first one and get the name, or, yeah, we got that name, poof, like magic.
>> Kent C. Dodds: And we'll say t.identifier and make a new identifier with that, and-

>> Speaker 2: You have a typo in that function call, yeah.
>> Kent C. Dodds: Thank you. And t, I need to bring in types. There we go. Cool, so we've created an assignment. Actually, I haven't shown you this before, but I'm pretty sure this would be a good thing to show you.

So we can log out our AST that we just created. It's an expression statement, has an expression that's an assignment expression. And yeah, interesting stuff. But there is, look, there's a clone function. Looks like we probably shouldn't use it, though.
>> Kent C. Dodds: With the path, now how do we do this?

There's a way to, just like with the ESLint plugin that we're developing, where you could get the source text from a node. You can do that with Babel. And I didn't think about telling you that until just now. So you'll have to go look up in the docs later cuz I don't wanna fish around in the docs right now, but there is a way to do that and it's very useful sometimes.

So when you're debugging stuff and whatnot, I recommend taking a look at that. Okay, so now, I'm gonna go ahead and save.
>> Kent C. Dodds: Well, okay, we have this assignment. And this assignment needs to replace the full text. Not just the thing that we're visiting right now, but the whole thing.

And so the path that we have right now is just this $(el), but we need to find the overall path. And that is going to be, if you look at this for just for a sec, this is the path we're visiting. This is the parent path that's a MemberExpression.

And this is the parent path of the MemberExpression, which is a CallExpression. We can explore that really quick. We can see the CallExpression we're visiting, the parent path is a MemberExpression and that parent is a CallExpression. So we'll say, parentPath.parentPath. Let's get that really quick. That is what we're looking for, the CallExpression.

So we're gonna call that our overallPath or we can call that whatever. And then we'll say, overallPath.replaceWith our new assignment.
>> Kent C. Dodds: assignment, you gotta spell correctly. Otherwise, it doesn't work. [LAUGH] Okay, that is the plugin. So let's copy that over, make sure that our tests are working, and they are.

And we felt the feedback and the elaboration and learn things. So did we learn anything?
>> Speaker 3: Template [CROSSTALK] Babel, awesome.
>> Kent C. Dodds: Yeah, babel.template. I loved all those things you said, especially awesome.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now