Check out a free preview of the full JavaScript and TypeScript Monorepos course
The "Internal Dependencies" Lesson is part of the full, JavaScript and TypeScript Monorepos course featured in this preview video. Here's what you'd learn in this lesson:
Mike demonstrates how to use Lerna so that the UI and folders both dependent on types and utils by creating a local version of both the types and utils packages, and makes data dependent on UI since in a given application the UI makes requests to the data layer.
Transcript from the "Internal Dependencies" Lesson
[00:00:00]
>> Let me recap where we left things off just now. We saw that through using learner and automated automated change logs, we're able to focus only on the specific effect that a given code change would have. Like, is this a fix? Is this a feature? Is this a breaking change?
[00:00:25]
And then all of these numbers are of the release numbers and all the change, change log stuff is calculated for us automatically. And let me just run, yarn real quick to clear things out. Get us back to a good state here. Let me refresh. Good. Okay, so we're about to begin, Step nine here.
[00:00:52]
So you're going to see two little folders within like the course files and you know some folder number nine One is called data and one one is called UI. Data is our data layer. So that deals with making like fetches to a locally running little JSON API. And then this UI is a react app.
[00:01:14]
So in here you can see some TSX files. And there's even a little server folder with like a tiny little express server that's running and it's serving up these Jayson thing so everything is running locally. I leave as an exercise to you if you want to take this workshop project a little bit further This server concern is kind of begging to become its own package within the mono repo.
[00:01:41]
So you could either have the server, consume the UI, and build it as part of the startup process, and then serve up the UI and the API that goes with it. Or you could say, All right, this UI has an asset serving layer, and my JSON back end.
[00:01:59]
It's just a middleware for my asset serving layer. You could do it either of those two ways. But this makes perfect sense to separate, because you could imagine, maybe you just want to stand up the API. Maybe you don't need the UI. Maybe you have an iOS or an Android app.
[00:02:14]
That hits this API and it needs to use this, but doesn't need all the React stuff in there as well. So you could see how having these little composable building blocks allows your your system to be more flexible, right? We always draw these systems on whiteboards. And as we're building these things, evolve these independent little boxes.
[00:02:33]
Well now they each have an encapsulation boundary around them. They're each independently tested and each independently versioned but with none of the burden of them each being a separate repository. So, let's get started with this next step. We're going to sorry, I just need to blow some extra stuff away here.
[00:02:55]
You would not have seen these here yet. Just because that stuff that's not checked in to get so No code changes. We're on the ready to begin Step nine here. So I'm just going to grab these, copy them and then paste them right into my packages folder. So now we have four things in here.
[00:03:15]
One from two to four. We're going to need to modify one file here and it's the TS config inside your packages folder. This remember is what we're using. So we can say TSC build Everything in packages, right? This is what refers to each of those little like each package.
[00:03:38]
And it's what led us have these this nice composite Project Setup where we have this Ts config build info, right and so on. As these things stack up, everything just has its own dist folder, and projects that depend on things. They just need to look at the right dist folder, they don't need to make an entirely new build of their own.
[00:04:01]
really save some time as things gets get complicated. So, let's modify that file here. And it's pretty predictable. What we're gonna need to do. Select this and just create two more with a comma because that's how JSON works. And this will be data and this will be UI the ordering does not matter.
[00:04:24]
Just need to refer to each and that is it. So now In addition, we drop these folders in. They each have a package JSON. And I noticed that there are things in here that were not part of our project before, we're going to need to run yarn. Right to pull in any of those dependencies, remember with yarn workspaces, the package JSON files of each package in the workspace.
[00:04:50]
They're taking into account so we're gonna go and we're gonna obtain react and react router and all the other things here all this type information. Right, that gives us those nice autocompletes. This one's gonna take a little while because this, react app has a lot more going on in it compared to just these sort of low level, utility style dependencies.
[00:05:14]
And we are done. Let's see if the build works. Yarn built. So in this case, it's going to work. Now, it may just happen to work. And I say it may happen to work because we have things like we're effectively referring to things that happened to be present in our node modules folder.
[00:05:42]
This, in this case, the workspace level one. But we're not stating that we have a dependency on those things. This is bad. This is bad because when a user installs this package, right, we're going to need to resolve this thing and we may not have it. The user may not have it in their node modules.
[00:06:03]
We're not stating that this needs to be downloaded and set up. This is a common mistake that that people make. Usually not having to do with a mono repo but transitive dependencies, right? So you might import something from a dependency of your dependency. Well what happens if that changes like you thought lodash was gonna be brought into your project by express what happens if expressed up to using lodash.
[00:06:30]
That thing kind of just disappears. Probably not lodash because so many things use it, but things can just vanish, and your app will break and it'll be really confusing. Why? Because maybe just your lock file change but not in a way that makes it super obvious that this thing went from present.
[00:06:46]
Missing. So good rule of thumb. Everything you pull in from code that runs at runtime should be listed as a dependency, not a dev dependency. So here we got to make sure that this stuff is added properly. We can use learner to do this, right. So we want these two new things that we just added to our packages.
[00:07:08]
The UI and data layer, they should each depend on types and utils. Here's how we make that happen. lerna add slack types, and then we can provide a scope argument. This scope. Sorry, it's like an option. This scope option can be used with a. Wide range of learner sub commands.
[00:07:36]
And this is where we can control where this command is applied. So we have all ready done things like learn a run test, and it runs all of the tests. But if you just wanted to run them on one or two packages, you could scope it down and you could say only on types.
[00:07:56]
Just run the tests there. So this is sort of a general purpose thing that broadly applies across learner. Not every command but the most common ones. And we can use this syntax here, slacks and then we could say UI common data. This is you might look at this as the familiar kind of syntax you use maybe an es lint config to say, Yeah everything in this folder that's a Ts or TSX file or js and a JSX.
[00:08:29]
You'd have like multiple extensions. It's kind of like a regular expression, or just like anything from this set. And let's see, no packages where types can be added. Let me make sure that we're all synched up, that we don't have anything lingering around. Maybe it's already applied. Let's check it out.
[00:09:02]
Interesting, A bit of a spelling error. I tried this and it failed. I put quotes around it and it worked. What's happening here is zsh Is interpreting some of this as special syntax that I'm using to do the SH stuff in my shell. I just want to pass this as a string, right?
[00:09:30]
So the argument wasn't reaching learner intact. So I had to say, put quotes around it, and then it worked. All right, let's try Adding utils. So if we look at the package JSON here, we can see this is for the UI package, we can see that we've got types and utils.
[00:09:55]
And if we look at the local node modules, if I refresh, tell VS code take another look. Come on. Maybe I need to learn a link. That's probably what has to happen. Yep. And there it is a local version of types and utils. So if I were to drill into here and change stuff I could you know, this is just a reference to the, to my source code to my own source code.
[00:10:29]
But if I were, you know, using node mon or something and I just wanted to watch my node modules folder for any changes These would be picked up,you know because of their location,cool.So through using Lerna we have added those dependencies now have one more.Well,we've specified interdependencies amongst a set of packages,right?It's not just adding a dependency one more I have to worry about and that is I need data to be a dependency of the UI, right?
[00:11:01]
The UI makes requests. That all happens in the data layer. And there you go. So we've kind of got like the UI on the top data, and then types in utils below that nice little stack. And I'll do learn a link one more time just to make sure that the data folder ends up where it should be.
[00:11:27]
It should be in here and let's see if it is There it is. data types and utils. Let's just check another one to make sure we're looking good. Just types new tells. Seems good. Seems good.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops