
Lesson Description
The "Installing & Running Nx" Lesson is part of the full, TypeScript Monorepos: Architect Maintainable Codebases course featured in this preview video. Here's what you'd learn in this lesson:
Mike shows how to install Nx and run build targets with pnpm exec, highlighting the speed of Nx and Turbo Build caching. He also explains why Nx is better suited than pnpm alone for managing tasks and dependencies in a monorepo.
Transcript from the "Installing & Running Nx" Lesson
[00:00:00]
>> Mike: So now we're installing Nx. My workspace is ready. It's giving me a link to connect to it. This is going to take me to NX cloud. I already have given it access here, so I think we're good. Great. So now I want to run pnpm exec nx browser run-many -t build.
[00:00:28]
So this is gonna run the build target for every project in the workspace. We're going to run it again to see the result of cache computation. So here we go. And we don't even need this. Exec is this is superfluous. So simplify things. So we're saying pnpm and then because this is a JavaScript CLI tool, NX Run Many is the command we're using when invoking the CLI tool.
[00:01:00]
And this is saying T for target build. So it's running build for three targets. It has a nice fancy UI that'll exit. Great. Two seconds. And here's the same command again. 23 milliseconds or sorry, 202 milliseconds. Maybe 23 for each of those tasks to run or something like that, it is lightning fast.
[00:01:34]
If you use Turbo build, the reason I got a question at the beginning of this course about which do I prefer, Nx or Turbo build? I love them both because they both do this, they both involve distributed caching and they both have the potential to dramatically speed up your builds Here again, as you can see from the CLI output, nothing stops us from continuing to sorry, pnpm.
[00:02:13]
Yep, nothing stops us from using Lerna for some things if we like that --since. Or we can drop down and use NX when we have to. It's all good. Just think of it as almost a high level API and a low level API. Here's the config file that it generated for us.
[00:02:35]
So here's your JSON schema and we've got target defaults. And what these mean is essentially we could start to get rid of the test command in our package JSONs of our monorepo packages like this models. I can get rid of test. Do not click that. We'll do test and lint.
[00:03:12]
We'll get rid of both of those in every package just to show how this works. Lint and test and lint and test. And the reason I'm doing this is it's like a subtle difference between LERNA and nx. So this should work. I know what's happening here, derp. All right, without further configuration here.
[00:04:06]
Yeah, if it's just arranged like this when we say there are targets, you can think of these almost like commands to run within packages without doing anything here. This is just simply saying run the script. But what you can do is set up additional configuration here, like, let's just go to the docs and xvite.
[00:04:31]
There it is. You could set up this plugin, which, let me find a good example, I just need one. Tasks and caching, No, I want something that's using nice arguments here. Here we go. Look at this. So if we grab this here, we're no longer in a world where we're saying, I'm just running pnpm lint in each package.
[00:05:26]
What we can do here is in that one nxjson file we could say, here's how linting works. In my repo, I have a source folder, I have tests in a test folder, I have an eslint config at the root of my project. So this is like when you're using these executors, which these are referring to plugins effectively, where this is the package of the plugin and this is almost like the command that you're running within that plugin.
[00:06:00]
Then you can start to say, all right, the source of truth for running tasks in all of my monorepo packages. It's more in the NX configuration than anything else. And that's where you can start to factor out the individual per module NPM scripts. Now, there's still room on a per project basis for you to customize that.
[00:06:23]
What you end up doing is creating one of these project JSON files. And the nxjson files target defaults field is very, very similar to this. It's basically the fallbacks if there are no package specific instructions for how does lint run, how does build run, how does dev run?
[00:06:48]
But it really lets you strip a lot of responsibility out of those package JSON files and make it more about stating dependencies and stating exports and imports. And you're kinda moving the task definition into a tool that can have an increasingly high amount of awareness. I want to point you to one more thing.
[00:07:12]
This depends on thing we could do this and what that would let us do. Sorry, let me undo some of the changes I made here. Wanna add these back in, so that's in models. This is in server test and lint. Great. Lint and test. Great, great. Okay, so we're back out.
[00:07:43]
And now if I were to say nxrun test, see how it's running the build in addition to the test. Let's say you were set up in your Monorepo such that you really wanna point to that build output folder when you're depending on a Monorepo package. Some people would make this choice because you'd say, hey listen, that's the package I'm about to publish.
[00:08:17]
I want to actually be pointing at that dist folder and run against it in as close a way as can be to the way an external user would be integrating against this. So no tricks. Well, you'd say we gotta do the build first. We can't run tests without the build first.
[00:08:36]
Another good reason to do that is if you're not using something like vitest. If you're using Jest and it's operating in the world of needing compiled JavaScript and declaration files in order to run, it can't run just by pointing typescript modules at it. You do this as well.
[00:08:53]
But this gives you so much more control than lerna, which just knows task and then dependency graph. The additional degree of freedom here you get is like task dependency graph and interdependencies between tasks like run prettier first before you lint so that you auto fix all of the code formatting and then let's see what the linting says as opposed to the other direction where you'd say all right, eslint lit up a bunch of formatting issues and now we've auto formatted them after the fact.
[00:09:31]
But you could see how it's valuable to be able to sort of arrange your tasks and then have your dependency graph and then be able to say, all right, now taking all of that into account, only run the things that are relevant to what I've changed. Now we're starting to see the ability to have a development experience that feels small even as your project is starting to get large.
[00:09:57]
So we were using PNPM workspaces to organize packages. What are the benefits or trade offs of using NX's project based setup compared to relying just on PNPM for workspaces? I'm not sure how to answer that one. NX is not a package manager and so it's still going to be the thing that's managing your node_modules folder.
[00:10:27]
I think maybe this is the way to answer that question. Pnpm, obviously it still has it still has some awareness of if we run this command here, it knows what depends on what it knows models first, then server and UI can happen in any order. But what I would say is nx it can't replace PNPM nor Does it intend to?
[00:10:55]
Because that's your package manager that's managing your dependencies and things where they overlap is. I'd say PNPM is a less sophisticated task runner and orchestrator than Nx. If for no other reason, it doesn't really give you a great way of understanding interdependencies between tasks. PNPM has no knowledge of git diffs and how they can be joined with the dependency graph to run only the subset of things that you need to run.
[00:11:32]
And PNPM has no idea. It doesn't give you a way to say, well, when I say I want to run test, I really want to run build first anytime I'm running test or reuse an existing build artifact. So use package managers for the package manager role. But as your monorepo starts to get interesting and as build speeds start to be a little bit of a snag on productivity, which your project gets to a sizable point, it's going to happen.
[00:12:05]
That's where you want to lean on something like lerna, at least for basics. And then you can eject out to NX as you want to get more sophisticated about saying, all right, well, we now have a dozen tasks. We've got. We've got many package and syncpack and we've got formatting of the package JSON files and prettier to format other things.
[00:12:26]
We've got svelte type checking and then we've got eslint and maybe we've got one set of linting rules that we're actually enforcing and then another set that we aspirationally want to switch to and we want to see that report but not fail the build based on that. Then finally we can run our tests.
[00:12:44]
If you want that kind of control, that's where you're going to end up ejecting out of LERNA and getting into NX or maintaining the alternative, which is a very elaborate set of shell scripts that are trying to place these things in order and that will get very unwieldy very fast.
[00:13:03]
So I would say if you start to have these needs, just start using nx. Skip the point where you fall under the crushing weight of a massive pile of disorganized shell scripts.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops