Automating Tasks with Node CLIs
Creating and automating workflows unlocks a ton of developer productivity. In this episode, Ahmad Awais will teach us how to design and build our own Node powered CLI automation!
Links & Resources
Full Transcript
Click to toggle the visibility of the transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Jason: Hello, everybody, and welcome to another episode of Learn With Jason. Today on the show, we're bringing in Ahmad Awais. Thank you so much for being here today.
Ahmad: Thank you for having me, Jason. It's awesome to be on the Learn With Jason show. I learn a lot from this show.
Jason: Yeah, yeah. I appreciate you taking the time. I think this is going to be a lot of fun. So for folks who aren't familiar with you and your work, do you want to give us a little bit of a background?
Ahmad: Yeah, sure. So, I am Ahmad Awais, as you already know. I spent a mere part of my career writing open-source code. Most of it is small packages, especially with Node JS, writing small CLI scripts, automating stuff. I really am into improving productivity. I even have a Node CLI course. Beyond that, I'm the head of developer relations. I'm also humbled to be part of the GitHub stars program. I think I've been around the block for like 19 years. And I love the purple color.
Jason: Oh, nice, nice. Very cool. Very cool. Well, I'm excited to talk to you today because I like CLIs. I like Node. I like automation. So these are all things that I'm into. I am interested to just kind of dig into this. What we're talking about here is actually a topic that is pretty near and dear to my heart, which I've heard it described as meta programming. So this is a lot of what I end up describing it as, this concept of meta programming. There's code that you write to ship a feature. There's code that you write to make your customers have a new experience. Then there's code that you write that makes it easier to write code. So when you get into this idea of you're basically building things out that make your experience as a developer easier, that's what I'm referencing when I'm talking about meta programming. So for you, what drew you to this? It sounds like this is something you do a fair amount of. So what drew you to this as a developer?
Ahmad: Well, that is an interesting -- for example, for me when I was growing up, there was not a lot of community around, community of developers. There were no conferences happening or stuff like that. People who are non-technical think of programmers as sort of hackers. That sound of typing on the keyboard and saying "I'm in."
Jason: (Whispering) I'm in.
Ahmad: I still do that.
Jason: (Laughter)
Ahmad: I had an older cousin that was doing engineering. It stuck with me that the senior programs who are really awesome with their jobs can work in the terminal. I didn't know the command line stuff and whatnot. Even today, we represent the command line with that dollar sign. If there's anything I'm against, it's that dollar sign because it kept me away from everything demand line for years. I would run that and it would say dollar not found. I would just get scared that I don't know what that thing is. I would just move away. Okay, I'm never going to look into that. Let's not go there. I think it was 2004 or 2005, I was watching a video of Chris. Big fan of the guy. He said you can just drag and drop a folder inside of your terminal. I was like, this is super easy. I could never figure it out. I know this folder is on my desktop, but I think never for the life of me figure out that USR folder that many people call user source. It's like user system resources or something. I could never figure out that part on my own when I was a kid. Just by that thing, you know, you can just drag and drop. I felt like half of the struggle was already done for me. I found out, okay, that dollar sign doesn't actually mean anything. It's just for context. History got the better of me. I ended up becoming an electrical engineer. I actually wrote manually those zeros and ones. I was kind of already used to the no UI kind of thing. I wanted to be a designer, front-end developer or something. Then it just hit me when I was at my sister's wedding. There are so many photo, and I want them to be renamed in a particular way. I can just see that in my head, right now. Here's what I need to write, and this is what will happen when I run it. I just don't know how I can do that with command line. Like, I just don't know that. But I'm a programmer. I can figure that stuff out. So it took me a lot of time to figure that out. Once I did, I was like, this is easy. Why don't we talk about this more? Like, why are not more people creating more automations?
Jason: You bring up an interesting point, which is that everything seems easy after you figure it out. I think that's the joy of programming for me. So many of these things, when I first look at them, are so intimidating. I'm looking at somebody doing something. I remember when I first got into real-time programming, I was at a conference. It was called keeping it real-time or something like that. I remember seeing -- I can't remember his real name, but he goes by Sub Stack. He got up on stage and did this live coding session that was unbelievably fast. It looked like a cut scene that they would put into a show about hackers that would then get posted on like the sub Reddit. Look, they copy/pasted the jquery source code. I walked away going like, I'm not a very good programmer. I could never do something like that. I sat with that for a while and thought, that's not fair. He's practiced this. He knows this stuff. I've never looked at it before. The fact it seems like magic to me just means that I haven't looked behind the curtain yet. It doesn't mean it's not accessible to me. That kind of drives a lot of what we do on the show. We're trying to pull back that curtain a little bit. Anybody can do any of this stuff. Once you figure it out, it'll feel easy. So you just got to get behind the curtain. You got to see what's actually happening. You learn what the jargon means, what does that dollar sign mean. If I'm looking at a folder on my desktop, where does that live in the actual guts of the machine if I want to find it in the CLI? Once those pieces start to come together, all the sudden it's like, wait a minute, I can do this. I do get this. This isn't magic. This is just computers. Then you can get further and further. I love that. That's such a great feeling, right?
Ahmad: Yeah, I don't agree more with you. I could probably take it one step further. I remember when React was new back in like 2014. I saw this whole different paradigm of understanding like JavaScript. I used to contribute in the core, and I remember the time when there were like seven lines of JavaScript. Now I think there's more JavaScript. So what I learned was you actually also learn by looking at the code. Even if you don't understand it. I often tell people, especially beginners, read the source code. You may not understand it. Just read the source code. A lot of things -- I just keep reading. When I start learning them, if feels like I already know this somehow. When I'm writing code, I kind of can figure out, okay, maybe there's this. Just because I was reading this in a repository somewhere, I think it could be implemented here. So reading the source code. We put a lot of pressure on doing it by hand. Just by reading, you can learn a lot about different styles of programming and different ways and different design systems.
Jason: Yeah, so let's apply that to something a little more tangible. Let's talk about something that we can do today. So you mentioned one of your first automation experiences was you were looking at a folder full of photos and wanted to rename them all. You thought you could work that out. So what did you have in mind for today? What did you want to automate?
Ahmad: So a fun bit about me is I generally don't watch news. I was actually the last person to find out that COVID is actually happening.
Jason: The last one. They actually gave you a certificate. (Laughter)
Ahmad: I left my job, and I was going to travel more than 40 times in 2020. My wife was telling me this thing is here. I was like, no, it's over there, it's not coming here. Then it started happening. It's before, you know, the lockdowns, when there were COVID stats, applications by everybody, right. I found myself struggling to find out what are the actual stats. Whenever I opened up a news channel, they would elaborate it, overexplain things, scare you with their voice and whatever. So in finding those statistics on WHO's website, going to the menu, like, where is the actual data? Give me the data, right. So I ended up writing this small CLI called Corona CLI. It went viral. I think it has more than 3 billion API requests by now.
Jason: Dang.
Ahmad: All it does is scraps data from a bunch of different resources and puts these statistics in your command line when you're writing code.
Jason: Right, right.
Ahmad: Then other developers came around on GitHub and started collaborating. Now it has graphs in command line. So for me, it's always -- you know, I love to write code and love to design. But sometimes, some things, it's easier if there's no UI. That is why I build a lot of CLIs. It could be a benign thing. For example, when you're at a restaurant and you know you were here and you know the Wi-Fi password and your friend is asking for it, I could just write Wi-Fi password in my terminal and it would show me the password of the current Wi-Fi I'm connected. So it's the little things like this.
Jason: Yeah, yeah, yeah.
Ahmad: So that's why I do it.
Jason: Yeah, yeah. So there's a couple things that we could kind of dive into today. Maybe this is as good a time as any to switch into pair programming view. So let's get over here. To the computer. Actually, before we start, let me do a quick shout out. We've had Rachel with us from the start today giving us all the captioning that we need. So if you are interested in reading along with the discussion today, Rachel from White Coat Captioning is here typing everything out for us right on the homepage. That's made possible through the support of our sponsors. We've got Netlify, Fauna, Hasura, and Auth0 all kicking in to make this show more accessible to more people. So thank you very much for that. While you're clicking on things on the internet, head over and follow Ahmad on the Twitter. And we're going to be working with Node today. So this is just if you're new to Node. This is where you would start. There's a lot to Node. I think we're probably not going to go too deep into the basics of Node, but we will try to walk through the bits that we're using. So if I'm going to start this, I imagine we're going to live mostly in the terminal today. So I'm going to create a new repo. Does that seem like the right first move?
Ahmad: Yeah. It really depends. Do you want to create something absolutely from scratch? I have a CLI that can give us a lot.
Jason: You have a CLI that can create a CLI. Actually, you know what, let's use -- okay, what's that called?
Ahmad: It's called Create Node CLI.
Jason: Create Node CLI.
Ahmad: You can just npx it in the terminal. It will create a folder in there.
Jason: Okay. So here's the project. I'm going to npx create node CLI. Do I need to provide a folder name?
Ahmad: No, it will ask a bunch of questions, hopefully. It will create that folder, the name of the project.
Jason: Gotcha.
Ahmad: So this is me trying to lower the bar of entry. How about we build a CLI to parse your episodes and schedule from Learn With Jason's API.
Jason: Okay. All right. So let's call this LWJ CLI. Okay. The CLI command, we probably want that to be LWJ, right?
Ahmad: Yep.
Jason: Okay. Description. Get episodes and schedule for Learn With Jason. 0.0.1 sounds about right. Let's go with an MIT license. Okay. This is like the Node package. Oh, it'll ask.
Ahmad: Yep, it will probably ask.
Jason: Look at it go. Okay. That was handy.
Ahmad: So it's creating the CLI project in the lwj-cli folder.
Jason: Okay. So we've got a nice straightforward -- it looks like an index. Let's open this thing up and peek at it. We will restart later. Thanks, code. All right. So we've got a CLI, init, log. These were the details you were showing me here. So we've got a bin of LWJ. If I wanted to change it, I could add Learn With Jason, index.js. Then I'd be able to run any one of these and I'd get my command.
Ahmad: Yep.
Jason: Okay. Cool. All right. So this is all set up. It's looking cool. Do you want to walk us through what's gotten set up here?
Ahmad: Actually, let's go back to packages first.
Jason: Okay. Here we are.
Ahmad: So the most important thing, as you just mentioned, are probably pretty much already set up, things that I think should be there. The most important one is the package name. That is generally what is going to be installed in the folder of your executables. So the first thing I would want to do here is just open up the terminal and npm link this.
Jason: Just run it without a command?
Ahmad: Yep, just like that.
Jason: Okay. All right.
Ahmad: So what happens now is our local folder, this lwj-cli folder, is acting as a local npm package and it's install. If you run lwj, it should run that CLI. So there we have it.
Jason: Oh, nice.
Ahmad: It has a welcome header. It doesn't really do anything. It's the same description and everything, the version we used when we were setting up the CLI. All of that is coming from the packages folder, right. The file.
Jason: Yeah, let's use the latest version of Node here. I don't know why it's on 16. I think I'm going to have to run npm link again. That did show up in the right place. It did. Now we're in the right version of Node. We've got ourselves a CLI. Okay. That's cool. It's already doing the thing that we want. I guess it's doing most of the thing that we want, which is I can type in a command and we've already got some CLI stuff. So do you -- go ahead.
Ahmad: And that's happening because of the bin command. All it really does is it links the file we mentioned. So the index.js file is going to be in the place where you run your executables in whatever OS you're using. The only thing you need to know if you go to index.js file now is that we are creating a Node JS CLI. So user system resources, bin, environment, node. We're able to use JavaScript's index for automation. So that should be the first line for whatever file you're trying to run through the bin command. With that done, everything else is Node JS. There are a couple ways I try to do this. There's an init file, which takes care of anything related to checking the Node version or stuff like that. If you go there, I'm using -- I have created a bunch of packages like CLI welcome, CLI handlers. What it does is it uses this data and creates this awesome welcome header. It clears the screen so your CLI gets the room. All of that is configureable. You can see the clear variable being passed as default true. It's just clearing the screen. If you don't want to clear it, you can not clear it, right. I like to handle errors in a better way than the Node JS does so you have more information. That's what's happening here.
Jason: Okay.
Ahmad: Go back to index.js. Then there's the CLI stuff. So there's a CLI file that I'm importing.
Jason: Okay.
Ahmad: You can use basic Node JS without any packages, but it just makes things a bit more harder to use. You know the boilerplate code or whatnot. So what I generally use here is CLI framework called meow. On top of it, I have created a package called CLI meow help to generate the help text.
Jason: Okay. So here's meow. That's good. I like it. Where's my -- I lost my Twitch chat here. So this is the create Node CLI. This is meow. Then it will dig into these other bits here. So we're setting up some flags.
Ahmad: Yeah, so a few things are happening here. We have flags, which are options for running your CLI. We have commands, which are inputs. For example, LWJ help. It should call you on your work phone and ask for help. So this is what this page is doing. It's creating the inputs, which we call commands in options, which we call flags. For our CLI. In my CLI package, it's making sure we can print this help without having to add space and whatnot. So here are some basic flags set up. If you want to not clear the screen, you can not clear it. There's a debugger.
Jason: I'm going to change it to not clear the screen. Right now if I try to scroll up, it's gone, which I think is -- that's cool in certain cases. I personally like being able to scroll up and look at what happened before. So this is probably more my speed. It won't clear. We can still scroll up and see what happened before. But I do dig that. That's nice.
Ahmad: Awesome. And we can remove the no clear option.
Jason: Yeah, let's remove the no clear option. So run that again. Look how quickly this happens. We make an edit in here, and now it's updating our help for us. I like this. This is a nice display, too. I like the way that this is all done. This is coming out of meow help.
Ahmad: Yeah, this is the package I created. Meow is really awesome, but then you're stuck writing template code in a giant JavaScript string. So what's happening here is I'm printing a table. You just don't see the borders. So everything is very well formatted right here. I'm using a couple other packages to make sure things are easier. For example, it shows me the default options, default values for all of the flags. So I don't have to go to GitHub and figure out the default. It's all configureable. You can remove whatever from it if you want to.
Jason: Yeah, so here's -- let's see. Here's the package, for anybody who wants to give that a try. All this is bundled up as part of this Create Node CLI. You can also find all of these right here in the dependencies. I probably won't link to all of them. But remember they will be there and in the show notes. So make sure you go check those out. It looks like we got a couple other options here. I'm going to run dash V. We can see the version. I'm going to do lwj-d. Debug! All right. We don't have anything here, right. So we're --
Ahmad: Yeah, it's just printing out the values for your CLI input.
Jason: The defaults, right?
Ahmad: Yeah, the defaults. For example, if you were to pass lwj-c is equal to one and hyphen B. I think it should make clear true.
Jason: Oh, do I need to make it -- there it is. It's not an equals. It's just -- okay. Cool. So yeah, there we go. We're able to check our work. We can see when we set it to clear true, I can't scroll up anymore. It's kind of -- what I like about this, it's kind of an interesting idea. If you're printing out a bunch of details and they're not details that anybody needs but they're useful while the process is running, you can kind of scroll, scroll, scroll and just get rid of it all so somebody can see. Again, I like having my history. So I'm like, let me have it. So without the clear, when we leave it off, we can still scroll up. I like that. It's good to have options, right.
Ahmad: Awesome.
Jason: Okay. So looking in here then, we've got our -- let's see. We have a few things going here. We've got our list of flags. I can set any flag in here. If I want to set another flag that's, let's say -- actually, yeah, we'll do a test. We'll go boop, type boolean. We'll go alias, b. And description, yeah, print boop. All right. It's not going to do anything, but what'll happen is if I do like an lwj-b -- or I guess I'll need the -- there it is. Now it's boop true. It defaults -- it doesn't default at all. So let's default to false. I also realize I'm showing shorthand here. We could do it like this, and I would have to add the debug flag. There we go.
Ahmad: Yep.
Jason: But because we set these aliases, that's what's letting us do whatever we want here.
Ahmad: Yeah, aliases plus the meow framework. You can capitalize it or add the value. You don't have to use a bunch of different rejects to figure out what is true and what is not. And it can infer type. For example, if it does not have a default, it can just infer type. When you don't have a default equals true because you were using that flag, you were adding hyphen b. That is all -- meow figures that out. Then there are commands. Commands are just things that you can build. For example, we can have a command for, let's say, episodes. Or schedule.
Jason: Oh, Co-pilot, you cheeky -- all right. So if I do LWJ episodes, it has a description, but it doesn't currently do anything yet. If I run the help, though, it'll show that we've got -- we can print our episodes, print the schedule. Okay. So we've identified a command, but we're not actually doing anything with the command just yet.
Ahmad: Yeah, it's like we now have a serverless function. We need to write the code for that function.
Jason: Right.
Ahmad: This is the meow help package. You give it a name, give it your flags and commands. And you give that help text as options for meow framework. I also have a couple of defaults. I handle the errors on my own, but the CLI handles unhandled packages. You can set it to true. A bunch of things. Now this file is returning back all the CLI to us, the flags and commands. So once you're on Create Node CLI, you put in the flags and you can just quickly go back and start writing code. So what's happening inside the code is I'm taking the CLI out of that file, importing it out. In the index.js file, as you can see --
Jason: In the index.js, yep.
Ahmad: I have created a CLI.input and CLI.flags, which are the options we can use. From flags, I'm taking out all the flags I care about. You can take out boop that you had, right.
Jason: Yeah, okay. So let's play with a flag first because that's probably the easiest thing to show. If I'm down here and I want to show a boop if the boop flag is set. Then it would be like input includes boop. Then I can do stuff. I'm assuming .showboop doesn't exist. What's the right way to do this? Is there a helper for showing text? Or I do just return some text?
Ahmad: You can console log anything here.
Jason: Let's do that. So now if I run lwj -- boop. I love it. This is great. This is fun. You can kind of see this doesn't feel too unfamiliar if you've written JavaScript before. We're getting some variables. So this is effectively calling a function, right. We're saying, like, call the LWJ function. Once this gets called, we can say I want the inputs and flags, which are like the props -- the arguments passed to the function. Then we can just do whatever we want in here. It starts to feel like, okay, this isn't so bad. We can make this work. Great. So what's my -- you were talking about showing episodes and schedule, which is going to be a little more involved than console logging something. So what's our next step if we want to do that?
Ahmad: Yeah, for example, I generally try to keep the commands to the minimum. I generally use more flags because the concept of the alias exists for flags, not inputs, right. Also, it's a bit hard to figure out -- you know, a user can type any kind of input. It's not some sort of data I have. So for example, if you type in help, what I'm trying to check is does your user input include help? If it does, meow provides that function.
Jason: Oh, gotcha.
Ahmad: So that is that. And how about we change the episodes thing to a flag. It's more controllable that way.
Jason: Okay. Let's do that. I'm going to take these right out of here, put them in here instead, and we will clean these up a bit. So let's do a type. This will be a string.
Ahmad: If someone has that flag, we will show them episodes.
Jason: And this one, we can do -- well, let's go without an alias on this one. You want to be clear about what you're asking for. Then we can do the same thing for the schedule. We'll set a type of boolean. A default of false. I think that puts us in pretty good shape. Then we're back to having the help text. So if I run help, you can see we've got flags for episodes now. Episodes and schedule.
Ahmad: Now we need to take them out, that destructure thing from flags.
Jason: Episodes, schedule.
Ahmad: Awesome. So now if someone is asking for episodes, what I would do is -- like for example, I would check if someone is asking for an episode. I would try to print out the episodes, right. So episodes in and something.
Jason: All right. Then in here, yeah, we're going to have to do some work. So the work that we want to do --
Ahmad: What I generally do is I don't destructure it. I just now remembered. So I could do flags or episodes. Episodes could be a different file.
Jason: Oh, I get ya. Let's not do that. Instead, we'll do it like this. We'll say flags.episodes. Then you want to await --
Ahmad: Episodes.
Jason: And we want episodes to be its own file. So we're going to put that in utils.
Ahmad: Since we're awaiting, I think you need to put medium brackets.
Jason: Okay.
Ahmad: Now let's create an episodes file in our utils folder.
Jason: Episodes.js. Now this one is being included by our CLI. So we don't need the hash bang for this, right?
Ahmad: Yep.
Jason: Okay. So this one we can just kind of do whatever we want. If we wanted to, we'll create probably an async function episodes. That will, for now, I guess we could just return -- just to make sure this thing is working.
Ahmad: Just before async, write module.exports is equal to. That should work.
Jason: Okay. What doesn't it like here? Oh. That means that up here we should be able to just const episodes equals require utils episode. All right.
Ahmad: That should just work.
Jason: This should just work. We run lwj episodes. Oh, no! What have I done?
Ahmad: You're not console logging since you're returning. So you should console log.
Jason: Oh. Try that again. Hey! All right. So now we've got ourselves at least the basics of this thing. So I want to fetch -- my default here would be to reach for Node fetch. Is that where you would go as well? Or is there a utility I'm not aware of?
Ahmad: Yeah, I generally use --
Jason: I'm just going to npm install. I've had so much trouble with Axios. So I'm just going to use node fetch. Let's get node fetch in here. I have an API for Learn With Jason. So we can go to learnwithjason.dev/api/episodes. That'll give us a list of the upcoming episodes. So let's get those. Episodes equals await. And we're going to get fetch. And we can check -- I guess we'll check. This is like the response. If response okay, then I want to throw an error. Otherwise, we'll get our episodes await response JSON. Then I could just throw out that giant object, but that feels like maybe not the best thing to do.
Ahmad: You should do it.
Jason: You think so? Okay. I'm going to do it.
Ahmad: Oh, wait. No, I think you should take out title description.
Ahmad: Okay. So let's get episodes, subset. We'll have episodes.mapt and get the episode. We'll just return -- yeah, we'll do an ID, title. And what else do I have?
Ahmad: Description.
Jason: Oh, I have that ID. We'll do a title, description. That should be good. Then we'll probably want some way to actually, like, do something with that. Not just yet, right. So then we can do the episode subset. Should I limit this? This is going to be like 200-plus episodes.
Ahmad: So instead of just console logging it, how about you run another map and print it out with console logs in it. Print out everything, spaces and stuff like that.
Jason: Oh, I gotcha. I see what you're saying. Let's do console.log. Title and description. I guess for now, we can --
Ahmad: Just title and description. Then add an empty console log. It will just add another line.
Jason: Okay. Skip that part. Save that. Then I won't need to save that anymore. So we can just run it like this. Let's see what happens, everyone. Hey, look at it go. All right. So this is fun. We've got, like, title here. We've got our descriptions. It's out of order, I'm noticing, which is interesting. Why is it out of order? That should definitely be ordered. Weird. I wonder if I did something goofy. I must have done something goofy with it that's doing the order weird. But that's okay. We'll fix that some other time because that's in my API, not the CLI.
Ahmad: So now let's add links to it so it's more useful. You can press command and click a link in the terminal.
Jason: Okay. So let's get the link. The link is going to be a little bit weird because I have to pull it out. We'll go -- actually, I can do this the short way, lwj.dev/episode slug current. That should work. Let's run it again. Let's check this actually does what we want. I screwed it up.
Ahmad: You need to console log it.
Jason: Right, right. That whole thing.
Ahmad: Yeah, console log is a first-class citizen when writing CLIs.
Jason: Oh, no. I screwed it up. What did I screw up? Wait. There we go. Let's give this a shot. This opened in the wrong window. Let's open it in this one. Still opening in the wrong window. Whatever. It's working. It's doing what we want. We're getting it over here. If I copy/paste this out, we can see that it's taking us to the right place. Okay, perfect. So that's exciting. This is fun. We can do quite a bit with this. That took relatively little effort, right. This is pretty exciting.
Ahmad: What I want to do next is what I call CLI user experience. So this is where the design comes in.
Jason: Yeah.
Ahmad: Let's use -- for example, I don't like -- you probably have a really good internet, but not everyone would have. So your CLI is going to be stuck doing nothing when the API is being called. So we inform the user something is happening there. Then everything is a giant big mess of text here, right.
Jason: There's a ton in here, yeah.
Ahmad: So I would like to, you know, add some sort of -- for example, if you -- let's go ahead and try the chalk package first.
Jason: Okay.
Ahmad: So install chalk.
Jason: So it looks like it was already installed, I think. We have access to chalk. So in episodes, I'm going to get chalk equals require chalk.
Ahmad: And I think you should not require const chalk. Let's destructure right here. Description would be italic and contrast with the title.
Jason: Okay.
Ahmad: Then bold. I think we need to have a bold title. So bold. Then I think Learn With Jason is yellow. So yellow.
Jason: Okay.
Ahmad: Awesome. So now what we need to do is we need to start using -- these are just functions. We can pass them inside the console log. So just pass bold for title.
Jason: You're doing it like this, right?
Ahmad: Yes. And description would be dim in italic. Can you move the link below the title?
Jason: Like that?
Ahmad: Yeah, and add an extra console log, an empty one. You don't need to pass an empty string at all.
Jason: Oh, you can just leave it empty like that? I thought it would just no op if you did that.
Ahmad: Yep, so I think that would look better.
Jason: Here?
Ahmad: Yep.
Jason: Hey, that's looking pretty good. All right. So that's exciting that it does it. You know, chalk, for folks who aren't familiar, that's its whole purpose, to make the CLI output look less like jumbly. If you just use console log, everything is formatted exactly the same. It's hard to get a sense of visual hierarchy. So doing this, with the bold and yellow, dimming the description, we have made it much easier to pick out the most important information as you scroll. We can see what the title and the link is for each one of these. Then there's more information below that's less visually attention demanding. So yeah, I like this. This is already looking pretty slick.
Ahmad: Awesome. And I think we can also build a more accessible experience. So for high contrast, if you prefer high contrast, you can create a flag so anyone could use that flag. It will just inverse everything and will be more readable for some people. So you can play around with that. I often provide a minimal layout for my CLIs. So if you provide X, that generally means give me the UI. So I can easily remove that and console log information. You could easily play around with that.
Jason: Yeah, okay.
Ahmad: Let's move to something more interesting. Let's add spinners, CLI spinners. I see that in the chat.
Jason: Everybody loves a spinner. Let's do it.
Ahmad: So there's a package called ora. Let's install that.
Jason: This one. Elegant terminal spinner. Let's do it. I'm going to npm install ora. All right. I have installed ora.
Ahmad: Required in this file. So let's create a constant called spinner.
Jason: Like all the way at the top here?
Ahmad: Yeah, anywhere. That works. Spinner is equal to ora. It takes in -- what I generally do is create an empty spinner. So ora, you can see text and don't provide it any text.
Jason: Co-pilot does not like that. It wants us to be descriptive.
Ahmad: Yeah, so Co-pilot is already doing its job really well.
Jason: I know, right?
Ahmad: Spinner start is what I was going to say.
Jason: Okay. So what we're doing here -- and the reason we're starting it before the fetch is because it's going to wait for this, right? So we need to start the spinner before we start this fetch. Or else this won't even try to start until this is already done loading. So this is going to give us an actual indication that something is happening. Once we get here, right, is when we would stop it?
Ahmad: No, how about you provide the text inside the start function. It's fetching episodes. Just a string.
Jason: Okay.
Ahmad: After that, we can do a bunch of stuff. You can just spinner.start it when you want to.
Jason: Okay. So we'll spinner.stop down here. And that way we are spinning and then we start printing everything out.
Ahmad: Yeah, and if it fails, you can do spinner.fail. It has a bunch of options you can read in the documentation.
Jason: Okay. I want to make this slower. Let's have it -- let's await -- what do I want to do here? I want to do a --
Ahmad: Sleep function?
Jason: Yeah, there's no built-in sleep, though. We have to fake it with promises, don't we?
Ahmad: Yep.
Jason: Okay. So we're going to promise new -- hold on. Can I do this? New promise. Then we're going to get a resolve object. We're going to set time-out. Let's do it down here. Set time-out, resolve true. We'll do it in like two seconds. That's a fake sleep. I think. Let's see if I got that on the first try. Look at it go. Yes! Oh, I love it when I'm good at code.
Ahmad: (Laughter)
Jason: All right. So that's super exciting. Then down here, if something goes wrong, we could spinner.error, you said, or spinner.fail.
Ahmad: I think it's fail. Or failed.
Jason: Okay. So let's force it to fail. And false.
Ahmad: I think you can provide an error message in there.
Jason: Is this here like that, or do I need to pass it as -- no, I need to make this true, don't I? So it always passes. I think this should cause the spinner to fail. Nope, it just worked. I need to return so it doesn't continue trying to do things. What are you doing?
Ahmad: You will have to actually accept the CLI.
Jason: There it goes. Okay. So it wasn't -- this was like if both things -- yeah, that was just me not remembering how code works. So what I needed to do instead of an and was an or. If I or this, it will fail.
You hackers! You dirty hackers.
Jason: Nope, nope. Turns out I don't know how code works.
Ahmad: What are you trying to do?
Jason: I was trying to force a failure. I just realized I just do it like this. That'll do it.
Ahmad: You can do that. Yeah, you can do that for the process variable in Node JS because your CLI is going to keep running. The process is still running. Actually, try to avoid that, you know, that kind of failure because the CLI should fail. But you can force it with, I think, process. I don't remember from the back of my head. You can force the process to accept.
Jason: Gotcha. So we don't want to return. We would just process.exit.
Ahmad: Something like that.
Jason: Then would I exit code? Exit or exit code? No, just exit.
Ahmad: You will have to import that process as well. So you have the option of, I think, process.exit with it. You can type in the exit code an zero or one.
Jason: Now, this should trigger that unhandled package when I do this?
Ahmad: Yeah, hopefully.
Jason: And it looks like it just did the regular old failure. Exits and it doesn't explode, but it also doesn't send anything other than the failed to fetch episodes. That seems okay, right?
Ahmad: Did you import process?
Jason: Oh, I didn't realize you had to import. Isn't that Node global?
Ahmad: I think it's a global, but I --
Jason: Is that right? That feels like that's going to explode. Yeah, I could have sworn process was --
Ahmad: Yeah, process is a global.
Jason: So it's exiting. It's exiting with a code one, which should be an error. Why is that happening? We don't want a break point. Don't touch my stuff. Also weird that it's timing out like that. It shouldn't get down to here. Right? But now it should skip this altogether because it actually works. We get the response that we wanted. It still didn't wait for this, though, which is interesting. Is something weird happening? What am I doing? Okay. Somebody smarter than me. I'm relying on you. What have I done?
Ahmad: It's not using that promise, right?
Jason: It's skipping this promise. It feels like it didn't do that until we got to here. What? Resolve true. Oh!
Ahmad: Oh, it's not a function.
Jason: Thank you, thank you, thank you, Andres, for calling that out. That was a good catch. Never would have caught that. Would have just sat there like a doofus looking at it. No, this is great. So, this is great. We've got our process exit in here. That does what we want. Now everybody is happy. Okay. If I make it fail, it just fails. So we're happy here. This is good. This is exactly what we want. We've got ourselves a nice process where it's going to show us a spinner, fetch the episodes, display those, give us the ability to quickly go and check those out. Yeah, this is -- I like this. I'm happy. Can we do pagination? We actually can do pagination because I think that's why I thought my API was out of date. It actually does pages. If we go to page two, that's -- yep, that's the rest. Then we can go to page three. I don't know how many pages I have now because I think it's like 50 per page. So we should get one more. Then I think six will be empty. There it is. Okay. So we've got five pages of API results. We can theoretically get five pages worth of these, but I think you had -- I don't want to get too far off. I think you had some things you wanted to show.
Ahmad: Yeah, I think that's pretty much -- if you were to paginate, it's basic JavaScript. It has nothing to do with automation, right. So that is the power of Node JS. You're not worried about the syntax of bash or whatnot. You're generally writing what you already write as a developer. You're generally playing around with the same. There are a bunch of gotchas that are covered with packages like meow. So whenever I have to do something, I just Create Node CLI, and I'm right there with a function that I had in my mind when I'm writing code. That's JavaScript code. So making it super easy.
Jason: Yeah.
Ahmad: Like, I have processes -- like, it takes me generally less than ten minutes to create a small CLI and publish it and document it on GitHub and npm because I have CLIs for all of that, right. For example, if you were to uninstall the CLI that you have, npm uninstall global lwj.
Jason: Is it unlink?
Ahmad: Unlink is generally really weird. You should just uninstall it.
Jason: Okay.
Ahmad: Yeah, let's see if it works. So there's no lwj now. If you run npx lwj. This is installing an npm package I created before this episode. Like half an hour before it.
Jason: Oh, (Laughter).
Ahmad: Look at your face.
Jason: I was like, what happened? Okay. All right. This is cool. So we were able to get --
Ahmad: Now everyone can try it.
Jason: That's really cool. Okay. So everybody can -- yeah, you can run this right now with npx lwj -- episodes.
Ahmad: And schedule.
Jason: Schedule. Dang, we have 16 episodes coming up in the future? Holy crap. Yeah, we have -- and actually, that's not even all of them. I've got like eight in my inbox I have to get listed on the site. So we're just -- we got a whole bunch of fun stuff coming on. Oh, man! Marie, oh, this is going to be so much fun. We're doing like a Notion one. So many good things happening. This is going to be so good. Sometimes I forget to look at my upcoming schedule, and then I get to look at it and I've forgotten all the great things we've put in place and get very excited. (Laughter) But yeah, look how great this is. This is fun. I love that you can do this sort of thing where you have the ability to just kind of throw in some basic stuff. You can get a CLI set up and running. You can get the formatting working. The fact that, you know, you've kind of packaged all that up in Create Node CLI is really handy. That's really nice. Where we're able to very quickly get a sense of what's going on here. I like this a lot.
Ahmad: And if you go to lwj-cli on my GitHub --
Jason: Uh, GitHub.
Ahmad: GitHub.com/ahmadawais/lwj-cli. So what you see here is the same CLI. What I'm trying to say is I have a bunch of automation going on around here. One thing I have, it actually writes a readme for me. So it's pretty much the same code.
Jason: Oh, so you got a whole thing.
Ahmad: I think it took me less than ten minutes to create this thing. And I have CLIs that are creating this from the source code. The change log is completely automated. It is based on top of a git commit spec because I love emojis.
Jason: Yeah.
Ahmad: If you click on the change log --
Jason: Uh, change log.
Ahmad: It's hiding down there.
Jason: Here, read the change log.
Ahmad: Yep. So this gets generated automatically. It's based on top of the git commit messages and everything.
Jason: This is like semantic versioning or conventional commits.
Ahmad: It's just semantic versioning. On top of emoji log spec, the git commit spec. It's just using the git log and putting it out in emoji log in a way that is more easily readable. It's the same thing. You only have to drive your car to the racing track. The rest will take care of itself, right. So for me creating packages and sharing them on GitHub or npm, I only wrote the code I wanted to write, not a bunch because I've spent time once to automate all of that. So I think more people should invest time in creating automation like this. You already know JavaScript.
Jason: Yeah, this is great. I think this is super fun. We've got a lot of potential for this sort of thing where we can, you know, go and take quite a bit out of just your day-to-day work, turn it into a CLI, and make it easy to quickly get a sense of what's going on in your -- like as a dev, I'm trying to think of things I look at all the time that I have to open up something for. Like the GitHub CLI is a good example. I started using the GitHub CLI, and a whole bunch of stuff just got easier for me. The Netlify CLI is another one I use all the time, where just so much stuff that I would otherwise have to go and check out is just kind of like here for me so that I can check things out and see what's going on. If I'm in a site that's actually running, I can see details about that site. So building these CLIs is a huge win for the people who have to work with this stuff every day because you bring the data to where they are instead of making them go to a UI that they would otherwise never need to open. Yeah, I really like this.
Ahmad: And it's like, this is how you feel super powerful, right. It's like your serverless workers, without hosting or whatever. It's a career-improving skill set. When I started automating things at work, it changed the way people felt about me. I was able to ride the career ladder much faster. For example, in 2020 when my travel plans got blocked, that is what I did. I started creating a course on creating Node CLIs. I think I recorded 30 hours of content and deleted two-thirds of it. It still has like 23 projects. All kind of fun stuff, like developer surveys in command line. The CLI I created, the Create Node CLI, I created that as a project in that course to each what does it feel like to create an actual production-level project. Not a bunch of console logs and whatnot. It can remember things. For example, if you run create Node CLI now, it will remember your name. You won't have to put it again. So it can do things like that. It's creative like that. And there are so many awesome npm packages out there. You know, type in corona, you get COVID stats. You type in weather, you get all kinds of weather updates. I lead developer relations right now, and I have a DevRel CLI. It just reports back to me what I need to know from Notion, from a Google sheet and whatnot. All of that. And I have a CLI called dashboard. When I wake up, I just dash in my command line, and it tells me what I need to know, stats, how my courses are performing. So it's super handy. Then I have a CLI that opens things for me. For example, if I'm working on a particular project named XYZ, I would say open XYZ and it will open all the browser bars and VS Code, set it the way I want on a multiple-screen setup. Then I'm right there coding. Instead of now I need to open that, now I need to open that. It just makes me super productive. Then I can just say exit XYZ and we're back to watching Netflix.
Jason: Yeah, that's really nice. So I have a question. Whenever we start talking about automation, there's the trade-off between how much work you actually will save versus how much work it takes to create the automation. I think XKCD has a good visual joke on this. You think you're going to write a little bit of code to automate, then this thing goes away. The automation ends up taking way more time than just doing the thing would. So I think that as with everything, this is one of those "it depends" scenarios. There are case where is it's a great idea. There are cases where it's a terrible idea to try to automate something. How do you draw that line? What's your heuristic for the way you approach this? When do you decide to automate versus when do you keep doing it without automation?
Ahmad: So, whenever I try to automate things in my brain, it defaults to true, right. So I'm a very bad example of this.
Jason: (Laughter)
Ahmad: Like, I have a real example. People watching this livestream, you see how good Jason is looking as compared to me. So I have a really good DSLR that I bought. It's just sitting there. The subscription kept upgrading, then it was expiring one day. Then I was like, maybe I should just download this course on how to do video on the internet and put it on my server. Then I started creating a Drop Box CLI, then a CLI to download things. So I downloaded a bunch of courses. They are sitting in my Drop Box, but my DSLR is also still sitting in the packed box. And it's been years. So I'm a bad example.
Jason: Yeah, I'm picturing this as the flowchart that happens in your brain. But no, I think that there is a good argument to be made. I'll share some examples on my side, right. There are things that I do all the time, and it made sense for me to automate them. A good example, every time that I book an episode of my show, I need to add someone to a calendar invite. Like, I need to invite the captioners to come in. I have to send it off to my virtual assistant, who's helping me with keeping the site up and running. I have to block out time on my calendar. There's a whole bunch of things that have to happen. When I was doing that manually, it didn't happen about 25% of the time, which is a really bad way to run a show. So by automating, I increased the consistency. I saved myself a bunch of time. But it's predictable and ongoing. It's a thing I know is going to happen. With experimental things, I find that if I'm going to do something and I think it's going to work but I haven't really established the pattern, I'll start automating it and then learn something new and change it. Now I have to go change my automation. Then I'll spend a bunch of time fixing it, go back to doing it, and learn something else and go and change it. So for me, the heuristic is, is this something that I've been doing manually for a long time that has not changed? If I found that for the last three months I've been doing this task over and over again and it hasn't evolved and there's no real horizon for when it would evolve, I will then decide this is a good target for automation. That's usually how I think about these problems. Otherwise, the only thing I hate more than doing manual work is doing painful maintenance work. If I feel like my maintenance work is becoming busy work because I overautomated and now I have to fix 15 processes every time I change my mind, I'd rather just do that work manually for a bit until I come to a conclusion.
Ahmad: Yeah, and I have two examples here that people would find useful. The first part is realizing what kind of person you are. I over-engineer things. I know this because I come from an extremely technical engineering background. So this default, yes, I know I'm going to over-engineer things. So what I do is I start with a spreadsheet. It's very, very hard to over-engineer. It only has rows, and it only has columns. You can over-engineer in Notion. You cannot do that in spreadsheets. It takes a lot to put in and create a workflow. When you're ready to move off of a spreadsheet mentally for something, then you know what kind of flow you have and how it's going to help you to automate that. Then you should automate, but you should not automate everything. For example, I have this one thing called a refund CLI. So when someone asks me for a refund -- by the way, that never happens -- but if someone asked me for a refund, I have automated a bunch of stuff but have left a bunch of stuff not automated intentionally. So I can reach out and take time to consider what happened and why it happened. Many times it's the human aspect you end up relying on. So don't automate everything, but there's a lot you can automate and feel really good about.
Jason: Absolutely. Well, great. I mean, I'm honestly so blown away by how quickly we were able to do this, how far we got and how much we were able to accomplish. So what if somebody wants to go further with this? Where should someone go if they want to learn more? Like, what's their next step? I think I saw that you have a course on this. Hey, thank you for the sub, indifferent ghost.
Ahmad: So, a shameless plug here is I have a course called nodecli.com.
Jason: Is that this? I think I saw it down here somewhere. This one?
Ahmad: Down below.
Jason: Here?
Ahmad: Yeah. So a fun thing about this course is that there are like 100 videos in it, 22 projects. I created a bunch of CLIs here. The first trial videos out of the course where I explain weird stuff that Node JS has and create my first CLI is completely free. So, I created this course because people would go on and create more CLIs that I could use. I love using CLIs. A bunch of my students have created awesome CLIs. So I'm being selfish as well. If you want to learn from me, I spent a lot of time putting out this course and videos how you can create production-level CLI, how you can create a CLI that can run a server or do a quiz, your Wi-Fi password, or pre-configureable CLIs. Maybe you're doing DevOps work. You want progress estimation. You know how to debug CLIs. It's a bunch of console logs and it's a bit weird. So you can do that. All of the projects are completely open source on my GitHub. If you don't want to pay, go ahead. Still do learn it. All of it is out there. It's called Node CLI tips and tricks that I did on my GitHub repository. Everything that you'll learn here is open source. A lot of packages that I use and find interesting, I just open source them in a way that they're more useful. We didn't create a welcome package. Every time you run that CLI, it looks you with, okay, this is the CLI you're running. This is the version of it and whatnot. You can did a lot with that.
Jason: Yeah, for sure. I mean, it's very cool how, again -- it seems like there's a lot of potential here. A lot of potential for making our lives easier, for making our lives easy at work, for helping our co-workers use things that we want them to use for taking boilerplate out of the equation. If it has to be the same every time, that's a great candidate for automation. Well, cool. So I think from here, maybe what we want to do -- oh, go ahead.
Ahmad: For example, before creating this course, I surveyed a lot of developers on Twitter. Automation is actually a bigger use case for Node JS for developers than creating APIs. Those are two major use case, right. As you saw today, it's all JavaScript. If you don't want to, you know, mess around with Node JS a lot, you can use tools like meow Create Node CLI. We have a project where you can learn Node JS in a lesser -- you know, it's not API reference stuff. It's a bunch of tutorials that people wrote. If you click on learn, you can see that.
Jason: Oh, wow. There's so much in here.
Ahmad: Yeah, it's not documentation. It's tutorials, right.
Jason: Yeah, yeah.
Ahmad: I'm a big fan of reading and going deep when you have a problem. Otherwise, you end up learning 26 languages and you don't know what to do with them.
Jason: Sure. Yeah, all right. Well, Ahmad, this was super fun. I think we've got next steps. We got a CLI built. You've got one published already, which is amazing. So everybody, make sure you check this out. Have a good time there. There's a lot of really interesting stuff going on here. Make sure you go and follow Ahmad on Twitter as well. As always, this episode has been live captioned by Rachel. She's here from White Coat Captioning. Thank you very much for that. That's due to the support of our sponsors, Netlify, Fauna, Hasura, Auth0, all chipping in to make that White Coat Captioning affordable for me so we can make this show more accessible to more people. While you're checking out things on the homepage of the site, you can click through to that schedule. Or you can run the CLI command. Npx lwj -- schedule. We have so much fun stuff coming up. It's going to be an absolute blast. I cannot wait to show all of these things to y'all. Make sure you add this to your calendar. You can click on the add on Google Calendar button. You know, just one more fun thing is don't forget we've got these Rainbow Corgi rubber ducks. You, too, can have one of these adorable little buddies for you or your kids. It'll listen to your code problems. It'll hang out with you in the bath. It'll be a dog chew toy. It can be whatever you want. You take this thing and have a blast with it. Just please get them out of my storage. (Laughter) I had to buy so many. No, go grab one of those. Ahmad, any parting words before we call this one done?
Ahmad: Yeah, like, if you build a CLI, tweet at me. I love using CLIs.
Jason: Yes, absolutely.
Ahmad: So, let's do this.
Jason: All right. Well, thank you so much for hanging out today. Chat, hang out. We're going to go find somebody to raid. Ahmad, I appreciate your time, and we'll see y'all next time.
Learn With Jason is made possible by our sponsors: