Check out a free preview of the full Introduction to Node.js course:
The "CLI with Node Solution" Lesson is part of the full, Introduction to Node.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott live codes the solution to the exercise, instructs on how to add formatting to the output JSON, and discusses using path.resolve() versus path.join().

Get Unlimited Access Now

Transcript from the "CLI with Node Solution" Lesson

[00:00:00]
>> Speaker 1: I hope everyone had a chance to create their first CLI. Hopefully if it was your first CLI, congrats, you experience some power and just back in or system related program or languages in general. And I didn't just know anything like Python, PHP. Anything not related to mobile or web.

[00:00:19] This is what they can go. What's so special about this though is that you did it in JavaScript which a lot of people just thought it couldn't be done. So I remember when I created my first CLI that's when it clicked for me and I was like, okay I can pretty much do whatever I want here.

[00:00:32] Like, I own everything now so that was really fun. So let's walk through this solution. So there were a couple things, a lot of moving pieces here, a lot of things this literally whole file I'll say don't even worry about it. I'm sure everyone wholooked at it anyway was like.

[00:00:46] What is this, how's this working? We can talk a little bit about what that is but I don't wanna go on too specific about a package that's on internet. That probably has better documentation than what I can say. But so let's just get started, so you probably have like a contacts file here.

[00:01:00] It's just a JSON file and it's just an object with contexts where the key, you can't tell by this, but it's first name last name as the key and then you have all this other stuff. So what you would have to do is one, if you take a look at nxjs you even get a hint in here.

[00:01:17] Install any missing modules. How do you know a module is missing? Well, you can just try to install it and if it's already installed, it won't install it again. So you're fine. There's no double work there. It's not going to install it twice. You can install it and even if you already have it, there's no problem.

[00:01:33] But if you want to verify there's a couple of things you can do. For one, look at the tag. Is that JSON? Do you see in the tag? Is that JSON underneath of the dependencies? No, I don't see.
>> Speaker 1: I don't see commander and inquirer in package.json. And how do I know those are node modules?

[00:01:50] Well, they don't have dots in front of them. So that means they either have to be internal modules or node modules somewhere on the internet and you could have just looked like five seconds on the node documentation site. Is there something called commander here? I can promise you there's not.

[00:02:06] There's nothing internal to node called commander. There's nothing internal to node called inquirer. So most likely these are modules downloaded from the internet. And our package.json says we don't have them. So most likely they're not going to be in our node modules folder either. But, we can check.

[00:02:19] We can go to node modules and we can check. And I might have them here because I probably already installed them. But let's check it out. So commander. Look at all this stuff in there. So much stuff.
>> Speaker 1: Yeah, I already have it because I already installed it.

[00:02:36] But this is another way you can check. But like I said, it doesn't hurt to just install it again. So the way that you would do that, is, you might see some people come in like, I'm just gonna type in the name of it here, and be smart about it, type in commander.

[00:02:49] And then when I run mpm-install, it'll install it. Yeah, you could do that, but how do you know what version to put in, right? So you might get really smart, and look at the documentation. It's like, well yeah, I know what version to put in, I'll just do this.

[00:03:01] I'll just say commander and I'll just put a star. Yeah, you could do that. You're going to get the latest thing. But now, whenever there's a breaking change, you're going to download the breaking change too, which could break your app. So you don't want to do that. These little carats and stuff have small significance, like you know, you could download the next version, but only up to a breaking change.

[00:03:21] And that has a lot to do with something called symver, if you've ever heard of that. It's a system that open-source developers use to release versions on their packages. So these packages should follow symver, where the first number is a major change, the second number is more like an addition or a fix, and this third number here is just a small thing.

[00:03:42] That doesn't really matter. So you've got a patch, a minor, and a major. So patches are small, minors are just fixes, and majors are huge version changes. So anything that's a huge version change. Even that breaks gets a version bump. Everything else is just patches and minors. So if this thing was 2.0 and it downloaded 2.0.

[00:04:01] But my coach version 1.0 and if there's a breaking change most likely my coach going to break her out me knowing about it. So that's kind of why you don't want to do that. So, yeah, don't do that. Oops. So instead just use what I told you here, which is just mpm.

[00:04:16] I for sure and then you can type in the name of the package. So commander, and then, yeah you can type in another one right next to it, and it works just as well, I just don't know how to spell it, so I'm gonna go copy it. [LAUGH] There we go, grab that.

[00:04:31] So you can type as many as you want here, you just put a space after it, no comma, no nothing, just npm. You could install, for the long the full version, so you just type in install. And then, we want to make sure we save these. We'll go save.

[00:04:48] Although I think the new version of npm might even save these by default now. But I'm so used to doing this, and I'm just gonna do it, anyway. So it'll save it. For me, it might go by really fast because I already have it. And again, I use yarn, which it caches all this stuff, anyway.

[00:05:00] So I'll let it do its job, and it'll save. So that's one fix. Did everybody get passed that fix, just installing the node modules. Right, pretty simple to install node modules. And if you didn't, you probably would have got an error saying, cannot find missing module or something like that.

[00:05:17]
>> Speaker 1: So the next thing we wanted to fix was these two functions in here. Get contacts. And save contacts. So yeah, they're just two function to fix, but there's a lot of small things happening here that a lot of people ran into. And I'm so glad a lot of people ran into the little trap that I had here and it felt so good.

[00:05:36] [LAUGH] But it taught you guys a lesson which was yeah, you have to use the path module. So let's get going with this. So the first thing is getContacts. It says it should read the contacts at the @contactsLocation, which is this path and convert it to a JavaScript object.

[00:05:51] So let's do that step-by-step. So first thing is, it should read the contacts at the contactsLocation. Okay, so let's see that. So we'll say contacts = fs.readfilesync. And then we need the contacts location, so contactsLocation. We'll do that. I was gonna put toString here just to make sure it's a string and not a buffer.

[00:06:20] And we got that and then now it says we need to convert it to a JavaScript object. Well, this is JSON and converting is a JavaScript is just JSON.parse. So I'm just going to say return JSON.parse(contacts). So you might have done this and realize that you were good to go.

[00:06:39] You were done, right? So let's test it out. So I'm gonna run this command. And for get contacts, it's gonna node exercises/cli/index. And then, forget context, that's gonna be list. So if I run that, it says right there. You'll get this error. It's like error: ENOENT, however you pronounce.

[00:07:05] It's basically saying, we tried to find this file, but it doesn't exist. So you can see that it said it tried to open the file. But that's a little weird. We didn't try to open a file. Well, actually, you did. When you go inside of Finder, or your files on Windows, and you click on a file, you're actually opening it.

[00:07:21] You're viewing it. This is actually doing the same thing. It does the same thing your GUI does. When your GUI, when you click on things, the first thing it does the exact same thing. It's doing the same operations that you're normally do with the mouse and the keyboard.

[00:07:32] So it actually did try to open a file which just didn't exist. So imagine if there was a file represented in your Finder or your File Explorer that didn't actually exist and you tried to open it. Yeah, you'd probably get a error too. This is what you just tried to do.

[00:07:44] You tried to open a file that doesn't exist. So yeah, you would get the same error. Luckily graphics using their faces don't really show you files that don't exist. So you should run into that problem. But if it did you would get the same error. So that means this path is awful.

[00:08:00] We look here. We know that context that JSON is a sibling. So maybe I just need to do that. Change that like that. And it's relative now, so let's try that. Okay, yeah, that didn't work either, so you got to go back and think, okay, I'm trying to add this path.

[00:08:19] It's relative, but it's not working although I know it's there. And there's a few things. One, remember I said, don't ever use paths. Without using the path module, unless you're doing a require. Don't ever do it, because you cannot rely on, whatever tool you're passing that path to, you cannot rely on the fact that it knows where you are, like, it knows where it is in the app.

[00:08:41] It doesn't know about the dir name. Like, I just wouldn't expect it to. So most of these tools actually just use like full paths, like full blown paths. So most of the time you've just got to give them the full path. You could just construct a full path here.

[00:08:56] You can just say, okay, I'm going to do cli exercises, I'm going to do all this stuff. But we have helpers for that. So what we can do instead is a few things. One, we have this dirname thing. We can do that. Dirname is gonna be the full path of this directory.

[00:09:13] So that's one thing, and then you might do something like this. You might do that. Well, now you gotta whole bunch of different issues. One, if you're in Windows, this might not work. Two, I don't know what's on the end of that. It could be a slash. There's a dot here like I don't have no idea what to do with this is too many things to in the edge cases.

[00:09:30] Most likely have the writer regex to fix this which is this annoying. So instead get rid of that put a comma here and then you can just use path.join. And you just join those two things and it'll figure it out for you and you're done. That's it. So now that we have the right path.

[00:09:46] Everything's good to go. Hopefully we should be able to run this command. And it's showing me contacts that I can pick, so I'm gonna pick that one just to test it out with a contact that actually has a real name. We can just say Ashley Smith and we'll just copy this stuff over really quick.

[00:10:22]
>> Speaker 1: There we go. So if I run this here you go, see, I can select more than one. So I'll select Ashley Smith and boom, I got her stuff, all right, so that worked. Everybody got this part done, any questions on this part?
>> Speaker 2: Did you need the ./ in front of contacts.JSON, there?

[00:10:40]
>> Speaker 1: This one?
>> Speaker 2: Yeah.
>> Speaker 1: Nope, let's try that again. Boom. That's the sweet hing about path. It just does it for us. Didn't need to. If I had it, it would have fixed it. I'm pretty sure I could have even done this. And it might still work, let's find out.

[00:11:03] Yeah. See? You don't have to thik about it, it figured it out for you. It knew that you made a boo-boo [LAUGH] and and fixed. It's like yeah, you clearly didn't mean to do that. So let me go ahead and fix that for you. So yeah, that's pretty legit.

[00:11:18] Any questions on that?
>> Speaker 1: Okay, the next part. Was the opposite. So this time, we're getting a context object, and we need to save it to the context file. Now, I didn't give you instructions on whether this was a full context object or a object with just one context that you had to update in a context, so you would have to figure that out.

[00:11:42] To see if you need to overwrite the file or append the file, because that's the difference. If there was a full context object here, you would just blow the whole file out and rewrite it. If there was just one contact that you had to append, then if you just did write file here, you would have all the missing context, plus the one you just added.

[00:11:57] So you don't want that. So this is a full blown context object, so you just want to rewrite the whole file. So the way you would do that is you would just say fs.writefilesync. And then what you would do is you would pass in the path to contactsLocation.

[00:12:17] And then you need to convert this contacts object to JSON, right? So you would say JSON.stringify. The contact, contacts, and there you go. And now you saved it to the file, so we can test this out. So what we'll do is we'll run the command for new, and you should get this prompt.

[00:12:42] It's like First Name Tom Cat and a Phone Number.
>> Speaker 1: Boom, and then we can go check our context file and.
>> Speaker 1: Where is Tom Cat? There it is, there's Tom Cat. That looks kinda ugly, so let's format the JSON. This is a small JSON trick. That you can use.

[00:13:07] Definitely use it for logging out stuff during debugging. You can put null 2 as arguments in JSON.stringify. And it'll do some pretty cool stuff for you. So let's run this again. And Jimmy Hendrix. And now, if we go look at it you can see the format ofcourse. All right and then there's Jimmy Hendrix.

[00:13:31] So we just made an address book. Pretty legit. Pretty simple. You can start your startup, get some pc funding with this now.
>> Speaker 2: [LAUGH]
>> Speaker 1: And yeah it will be good. You have a question?
>> Speaker 2: Yeah I'm just curious when you recommend using path.join versus resolve or is there any real-

[00:13:51]
>> Speaker 1: I ask myself that question all the time. Sometimes I use path.resolve if I'm not sure how to get to a path.
>> Speaker 2: Okay.
>> Speaker 1: Like, I know the path exists somewhere up here. I am just hoping, I just need path to figure it out for me. I just don't know how many levels up it is because it will look ahead and figure it out for you.

[00:14:16] Whereas like path.join is like I'm pretty sure it is here, I just want you to fix it for me. So that's the difference. But most of the time I always know where my stuff is, I just need it to canchatinate correctly. So always use path.join but your resolve is just like, I don't know if I'm ten levels deep or four levels deep, so figure it out for me.

[00:14:34] That's what resolve does.