docs(core): react tutorial with workspaces (#30149)

Updates the react monorepo tutorial to use `--workspaces`
Small updates to the angular monorepo tutorial
This commit is contained in:
Isaac Mann 2025-02-24 14:10:48 -05:00 committed by GitHub
parent 82169ace03
commit cd361ef0ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 213 additions and 159 deletions

View File

@ -47,7 +47,7 @@
"isExternal": false,
"children": [
{
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"path": "/getting-started/tutorials/typescript-packages-tutorial",
"id": "typescript-packages-tutorial",
"isExternal": false,
@ -123,7 +123,7 @@
"isExternal": false,
"children": [
{
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"path": "/getting-started/tutorials/typescript-packages-tutorial",
"id": "typescript-packages-tutorial",
"isExternal": false,
@ -158,7 +158,7 @@
"disableCollapsible": false
},
{
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"path": "/getting-started/tutorials/typescript-packages-tutorial",
"id": "typescript-packages-tutorial",
"isExternal": false,

View File

@ -59,7 +59,7 @@
"itemList": [
{
"id": "typescript-packages-tutorial",
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"description": "",
"mediaImage": "",
"file": "shared/tutorials/typescript-packages",
@ -164,7 +164,7 @@
"itemList": [
{
"id": "typescript-packages-tutorial",
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"description": "",
"mediaImage": "",
"file": "shared/tutorials/typescript-packages",
@ -213,7 +213,7 @@
},
"/getting-started/tutorials/typescript-packages-tutorial": {
"id": "typescript-packages-tutorial",
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"description": "",
"mediaImage": "",
"file": "shared/tutorials/typescript-packages",

View File

@ -41,7 +41,7 @@
"description": "Get started with basic information, concepts and tutorials.",
"itemList": [
{
"name": "TypeScript Packages",
"name": "TypeScript Monorepo",
"id": "typescript-packages-tutorial",
"file": "shared/tutorials/typescript-packages"
},

View File

@ -33,7 +33,7 @@ Someone who appreciates the flexibility of a package-based repository will be mo
- Easily create new projects or tools with [code generators](/features/generate-code)
{% cards %}
{% card title="TypeScript Packages Tutorial" description="Add Nx to an existing TypeScript repo" type="documentation" url="/getting-started/tutorials/typescript-packages-tutorial" /%}
{% card title="TypeScript Monorepo Tutorial" description="Add Nx to an existing TypeScript repo" type="documentation" url="/getting-started/tutorials/typescript-packages-tutorial" /%}
{% /cards %}
## Integrated Repos

View File

@ -136,7 +136,7 @@ To avoid potential issues, it is [recommended to update one major version of Nx
Try one of these tutorials for a full walkthrough of what to do after you install Nx
- [TypeScript Packages Tutorial](/getting-started/tutorials/typescript-packages-tutorial)
- [TypeScript Monorepo Tutorial](/getting-started/tutorials/typescript-packages-tutorial)
- [React Monorepo Tutorial](/getting-started/tutorials/react-monorepo-tutorial)
- [Angular Monorepo Tutorial](/getting-started/tutorials/angular-monorepo-tutorial)

View File

@ -61,7 +61,7 @@ Also, here are some recipes that give you more details based on the technology s
{% cards cols="2" lgCols="4" mdCols="4" smCols="2" %}
{% link-card title="TypeScript Packages" type="tutorial" url="/getting-started/tutorials/typescript-packages-tutorial" icon="jsMono" /%}
{% link-card title="TypeScript Monorepo" type="tutorial" url="/getting-started/tutorials/typescript-packages-tutorial" icon="jsMono" /%}
{% link-card title="React Monorepo" type="tutorial" url="/getting-started/tutorials/react-monorepo-tutorial" icon="reactMono" /%}

View File

@ -6,7 +6,7 @@
- [Why Nx?](/getting-started/why-nx)
- [Editor Setup](/getting-started/editor-setup)
- [Tutorials](/getting-started/tutorials)
- [TypeScript Packages](/getting-started/tutorials/typescript-packages-tutorial)
- [TypeScript Monorepo](/getting-started/tutorials/typescript-packages-tutorial)
- [React Monorepo](/getting-started/tutorials/react-monorepo-tutorial)
- [Angular Monorepo](/getting-started/tutorials/angular-monorepo-tutorial)
- [Gradle Monorepo](/getting-started/tutorials/gradle-tutorial)

View File

@ -130,7 +130,7 @@ Nx uses the following syntax to run tasks:
The project tasks are defined in the `project.json` file.
```json {% fileName="apps/angular-store/project.json"}
```json {% fileName="apps/angular-store/project.json" %}
{
"name": "angular-store",
...
@ -147,7 +147,7 @@ The project tasks are defined in the `project.json` file.
Each target contains a configuration object that tells Nx how to run that target.
```json {% fileName="project.json"}
```json {% fileName="project.json" %}
{
"name": "angular-store",
...
@ -468,16 +468,15 @@ export const appRoutes: Route[] = [
Let's also show products in the `inventory` app.
```ts {% fileName="apps/inventory/src/app/app.component.ts" highlightLines=[2,6] %}
```ts {% fileName="apps/inventory/src/app/app.component.ts" highlightLines=[2,5] %}
import { Component } from '@angular/core';
import { ProductsComponent } from '@angular-monorepo/products';
@Component({
standalone: true,
imports: [ProductsComponent],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'inventory';

View File

@ -17,9 +17,11 @@ What will you learn?
## Why Use an Nx Monorepo?
In this tutorial, we'll set up a monorepo that is configured with a set of features that work together toward the goal of allowing developers to focus on building features rather than the configuration, coordination and maintenance of the tooling in the repo.
Nx works along side your existing tooling to improve your experience developing in a monorepo. You can also use code generators that Nx provides to quickly spin up a new project that is pre-configured with industry standard tooling. In this tutorial, we'll set up a monorepo that leverages the same tooling you would typically use without Nx, but Nx will enable you to focus your time on the features of your application rather than the tooling that surrounds it.
You'll notice that instead of using npm/yarn/pnpm workspaces, projects within the repository are linked using typescript path aliases that are defined in the `tsconfig.base.json` file. Also, since we're creating projects using Nx plugin generators, all new projects come preconfigured with useful tools like Prettier, ESLint and Jest.
We'll use npm/yarn/pnpm workspaces to link projects and TypeScript project references to incrementally typecheck the repository. Nx works well whether you have a [single version policy](/concepts/decisions/dependency-management#single-version-policy) or have each project [separately define their dependencies](/concepts/decisions/dependency-management#independently-maintained-dependencies), but in this tutorial we'll define all dependencies in a single `package.json` at the root of the repository.
Nx generators will automatically configure new projects with useful tools like Prettier, ESLint and Jest. Those generators will also make sure that each project is referenced correctly in the root `workspaces` property and the `tsconfig` references properties accurately reflect the dependencies of each project.
Nx Plugins are optional packages that extend the capabilities of Nx, catering to various specific technologies. For instance, we have plugins tailored to React (e.g., `@nx/react`), Vite (`@nx/vite`), Cypress (`@nx/cypress`), and more. These plugins offer additional features, making your development experience more efficient and enjoyable when working with specific tech stacks.
@ -50,18 +52,21 @@ title="Nx React Monorepo Tutorial Walkthrough"
Create a new React monorepo with the following command:
```{% command="npx create-nx-workspace@latest react-monorepo --preset=react-monorepo" path="~" %}
```{% command="npx create-nx-workspace@latest react-monorepo --preset=react-monorepo --workspaces" path="~" %}
NX Let's create a new workspace [https://nx.dev/getting-started/intro]
✔ Application name · react-store
✔ Which bundler would you like to use? · vite
✔ Which unit test runner would you like to use? · vitest
✔ Test runner to use for end to end (E2E) tests · cypress
✔ Default stylesheet format · css
✔ Which CI provider would you like to use? · GitHub Actions
✔ Would you like to use ESLint? · Yes
✔ Would you like to use Prettier for code formatting? · Yes
✔ Which CI provider would you like to use? · github
```
Let's name the initial application `react-store`. In this tutorial we're going to use `vite` as a bundler, `cypress` for e2e tests and `css` for styling. We'll talk more about how Nx integrates with GitHub Actions later in the tutorial. The above command generates the following structure:
Let's name the initial application `react-store`. In this tutorial we're going to use `vite` as a bundler, `vitest` for unit testing, `cypress` for e2e tests, `css` for styling and also include ESLint and Prettier. We'll talk more about how Nx integrates with GitHub Actions later in the tutorial. The above command generates the following structure:
```
└─ react-monorepo
@ -80,7 +85,7 @@ Let's name the initial application `react-store`. In this tutorial we're going t
│ │ │ ├─ main.tsx
│ │ │ └─ styles.css
│ │ ├─ index.html
│ │ ├─ project.json
│ │ ├─ package.json
│ │ ├─ tsconfig.app.json
│ │ ├─ tsconfig.json
│ │ ├─ tsconfig.spec.json
@ -98,11 +103,11 @@ The setup includes..
- a Playwright based set of e2e tests (`apps/react-store-e2e/`)
- Prettier preconfigured
- ESLint preconfigured
- Jest preconfigured
- Vitest preconfigured
One way to structure an Nx monorepo is to place application projects in the `apps` folder and library projects in the `libs` folder. Applications are encouraged to be as light-weight as possible so that more code is pushed into libraries and can be reused in other projects. This folder structure is just a suggestion and can be modified to suit your organization's needs.
The [`nx.json` file](/reference/nx-json) contains configuration settings for Nx itself and global default settings that individual projects inherit. The `apps/react-store/project.json` file contains [settings that are specific to the `react-store` project](/reference/project-configuration). We'll examine that file more in the next section.
The [`nx.json` file](/reference/nx-json) contains configuration settings for Nx itself and global default settings that individual projects inherit.
## Serving the App
@ -210,23 +215,35 @@ npx nx show project react-store
{% /project-details %}
If you expand the `build` task, you can see that it was created by the `@nx/vite` plugin by analyzing your `vite.config.ts` file. Notice the outputs are defined as `{workspaceRoot}/dist/apps/react-store`. This value is being read from the `build.outDir` defined in your `vite.config.ts` file. Let's change that value in your `vite.config.ts` file:
If you expand the `build` task, you can see that it was created by the `@nx/vite` plugin by analyzing your `vite.config.ts` file. Notice the outputs are defined as `{projectRoot}/dist`. This value is being read from the `build.outDir` defined in your `vite.config.ts` file. Let's change that value in your `vite.config.ts` file:
```ts {% fileName="apps/react-store/vite.config.ts" %}
export default defineConfig({
// ...
build: {
outDir: './build/react-store',
outDir: './build',
// ...
},
});
```
Now if you look at the project details view, the outputs for the build target will say `{workspaceRoot}/build/react-store`. This feature ensures that Nx will always cache the correct files.
Now if you look at the project details view, the outputs for the build target will say `{projectRoot}/build`. This feature ensures that Nx will always cache the correct files.
You can also override the settings for inferred tasks by modifying the [`targetDefaults` in `nx.json`](/reference/nx-json#target-defaults) or setting a value in your [`project.json` file](/reference/project-configuration). Nx will merge the values from the inferred tasks with the values you define in `targetDefaults` and in your specific project's configuration.
You can also override the settings for inferred tasks by modifying the [`targetDefaults` in `nx.json`](/reference/nx-json#target-defaults) or setting a value in your [`package.json` file](/reference/project-configuration). Nx will merge the values from the inferred tasks with the values you define in `targetDefaults` and in your specific project's configuration.
## Adding Another Application
Set the `outDir` back to `./dist` so that we don't have to modify all the other tools to account for a different build output folder.
```ts {% fileName="apps/react-store/vite.config.ts" %}
export default defineConfig({
// ...
build: {
outDir: './dist',
// ...
},
});
```
## Add Another Application
{% video-link link="https://youtu.be/gc4N7kxiA50?t=259" /%}
@ -272,11 +289,11 @@ More info can be found in [the integrate with editors article](/getting-started/
Run the following command to generate a new `inventory` application. Note how we append `--dry-run` to first check the output.
```{% command="npx nx g @nx/react:app apps/inventory" path="react-monorepo" %}
NX Generating @nx/react:application
NX Generating @nx/react:application
✔ Would you like to add React Router to this application? (y/N) · false
✔ What unit test runner should be used? · vitest
✔ Which E2E test runner would you like to use? · cypress
✔ What should be the project name and where should it be generated? · inventory @ apps/inventory
CREATE apps/inventory/index.html
CREATE apps/inventory/public/favicon.ico
CREATE apps/inventory/src/app/app.spec.tsx
@ -288,11 +305,12 @@ CREATE apps/inventory/src/app/app.module.css
CREATE apps/inventory/src/app/app.tsx
CREATE apps/inventory/src/styles.css
CREATE apps/inventory/tsconfig.json
CREATE apps/inventory/project.json
CREATE apps/inventory/package.json
UPDATE nx.json
CREATE apps/inventory/tsconfig.spec.json
CREATE apps/inventory/vite.config.ts
CREATE apps/inventory/.eslintrc.json
CREATE apps/inventory-e2e/project.json
CREATE apps/inventory/eslint.config.mjs
CREATE apps/inventory-e2e/package.json
CREATE apps/inventory-e2e/src/e2e/app.cy.ts
CREATE apps/inventory-e2e/src/support/app.po.ts
CREATE apps/inventory-e2e/src/support/e2e.ts
@ -300,7 +318,8 @@ CREATE apps/inventory-e2e/src/fixtures/example.json
CREATE apps/inventory-e2e/src/support/commands.ts
CREATE apps/inventory-e2e/cypress.config.ts
CREATE apps/inventory-e2e/tsconfig.json
CREATE apps/inventory-e2e/.eslintrc.json
UPDATE tsconfig.json
CREATE apps/inventory-e2e/eslint.config.mjs
NOTE: The "dryRun" flag means no changes were made.
```
@ -311,7 +330,7 @@ As you can see, it generates a new application in the `apps/inventory/` folder.
npx nx g @nx/react:app apps/inventory
```
## Sharing Code with Local Libraries
## Share Code with Local Libraries
{% video-link link="https://youtu.be/gc4N7kxiA50?t=324" /%}
@ -333,7 +352,7 @@ When you develop your React application, usually all your logic sits in the `app
│ │ ├─ ...
│ │ └─ main.tsx
│ ├─ ...
│ └─ project.json
│ └─ package.json
├─ nx.json
├─ ...
```
@ -346,7 +365,7 @@ Nx allows you to separate this logic into "local libraries". The main benefits i
- better scalability in CI by enabling independent test/lint/build commands for each library
- better scalability in your teams by allowing different teams to work on separate libraries
### Creating Local Libraries
### Create Local Libraries
{% video-link link="https://youtu.be/gc4N7kxiA50?t=366" /%}
@ -369,7 +388,7 @@ Running the above commands should lead to the following directory structure:
├─ libs
│ ├─ products
│ │ ├─ ...
│ │ ├─ project.json
│ │ ├─ package.json
│ │ ├─ src
│ │ │ ├─ index.ts
│ │ │ └─ lib
@ -381,7 +400,7 @@ Running the above commands should lead to the following directory structure:
│ │ └─ vite.config.ts
│ ├─ orders
│ │ ├─ ...
│ │ ├─ project.json
│ │ ├─ package.json
│ │ ├─ src
│ │ │ ├─ index.ts
│ │ │ └─ ...
@ -389,7 +408,7 @@ Running the above commands should lead to the following directory structure:
│ └─ shared
│ └─ ui
│ ├─ ...
│ ├─ project.json
│ ├─ package.json
│ ├─ src
│ │ ├─ index.ts
│ │ └─ ...
@ -400,28 +419,20 @@ Running the above commands should lead to the following directory structure:
Each of these libraries
- has a project details view where you can see the available tasks (e.g. running tests for just orders: `npx nx test orders`)
- has its own `project.json` file where you can customize targets
- has the name you specified in the generate command; you can find the name in the corresponding `project.json` file
- has its own `package.json` file where you can customize targets
- has the name you specified in the generate command; you can find the name in the corresponding `package.json` file
- has a dedicated `index.ts` file which is the "public API" of the library
- is mapped in the `tsconfig.base.json` at the root of the workspace
- is included in the `workspaces` property of the `package.json` file at the root of the workspace
### Importing Libraries into the React Applications
### Import Libraries into the React Applications
{% video-link link="https://youtu.be/gc4N7kxiA50?t=456" /%}
All libraries that we generate automatically have aliases created in the root-level `tsconfig.base.json`.
All libraries that we generate are automatically included in the `workspaces` defined in the root-level `package.json`.
```json {% fileName="tsconfig.base.json" %}
```json {% fileName="package.json" %}
{
"compilerOptions": {
...
"paths": {
"@react-monorepo/products": ["libs/products/src/index.ts"],
"@react-monorepo/orders": ["libs/orders/src/index.ts"],
"@react-monorepo/shared-ui": ["libs/shared/ui/src/index.ts"]
},
...
},
"workspaces": ["apps/*", "libs/*", "libs/shared/*"]
}
```
@ -557,7 +568,7 @@ export function App() {
export default App;
```
## Visualizing your Project Structure
## Visualize your Project Structure
{% video-link link="https://youtu.be/gc4N7kxiA50?t=530" /%}
@ -658,9 +669,9 @@ You should be able to see something similar to the following in your browser.
{% /graph %}
Notice how `shared-ui` is not yet connected to anything because we didn't import it in any of our projects.
Notice how `ui` is not yet connected to anything because we didn't import it in any of our projects.
Exercise for you: change the codebase such that `shared-ui` is used by `orders` and `products`. Note: you need to restart the `npx nx graph` command to update the graph visualization or run the CLI command with the `--watch` flag.
Exercise for you: change the codebase such that `ui` is used by `orders` and `products`. Note: you need to restart the `npx nx graph` command to update the graph visualization or run the CLI command with the `--watch` flag.
## Testing and Linting - Running Multiple Tasks
@ -680,28 +691,28 @@ More conveniently, we can also run tasks in parallel using the following syntax:
npx nx run-many -t test
```
### Caching
### Cache Tasks
One thing to highlight is that Nx is able to [cache the tasks you run](/features/cache-task-results).
Note that all of these targets are automatically cached by Nx. If you re-run a single one or all of them again, you'll see that the task completes immediately. In addition, (as can be seen in the output example below) there will be a note that a matching cache result was found and therefore the task was not run again.
```{% command="npx nx run-many -t test lint e2e" path="react-monorepo" %}
✔ nx run e2e:lint [existing outputs match the cache, left as is]
✔ nx run react-store:lint [existing outputs match the cache, left as is]
✔ nx run react-store:test [existing outputs match the cache, left as is]
✔ nx run e2e:e2e [existing outputs match the cache, left as is]
✔ nx run @react-monorepo/ui:lint [existing outputs match the cache, left as is]
✔ nx run inventory-e2e:lint [existing outputs match the cache, left as is]
✔ nx run react-store-e2e:lint [existing outputs match the cache, left as is]
✔ nx run @react-monorepo/ui:test [existing outputs match the cache, left as is]
——————————————————————————————————————————————————————
NX Successfully ran targets test, lint, e2e for 5 projects (54ms)
NX Successfully ran targets test, lint, e2e for 7 projects (54ms)
Nx read the output from the cache instead of running the command for 10 out of 10 tasks.
```
Not all tasks might be cacheable though. You can configure the `cache` settings in the `targetDefaults` property of the `nx.json` file. You can also [learn more about how caching works](/features/cache-task-results).
### Testing Affected Projects
### Test Affected Projects
{% video-link link="https://youtu.be/gc4N7kxiA50?t=614" /%}
@ -835,41 +846,66 @@ npx nx graph --affected
{% /graph %}
## Building the Apps for Deployment
## Build the Apps for Deployment
{% video-link link="https://youtu.be/gc4N7kxiA50?t=713" /%}
If you're ready and want to ship your applications, you can build them using
```{% command="npx nx run-many -t build" path="react-monorepo" %}
vite v4.3.5 building for production...
✓ 33 libs transformed.
dist/react-store/index.html 0.48 kB │ gzip: 0.30 kB
dist/react-store/assets/index-e3b0c442.css 0.00 kB │ gzip: 0.02 kB
dist/react-store/assets/index-378e8124.js 165.64 kB │ gzip: 51.63 kB
✓ built in 496ms
✔ nx run @react-monorepo/inventory:build (841ms)
✔ nx run @react-monorepo/react-store:build (968ms)
——————————————————————————————————————————————————————————————————————————————————————————————————————————
—————————————————————————————————————————————————————————————
NX Successfully ran target build for project reactutorial (1s)
NX Successfully ran target build for 2 projects (1s)
```
All the required files will be placed in `dist/react-store` and `dist/inventory` and can be deployed to your favorite hosting provider.
All the required files will be placed in `/apps/react-store/dist` and `/apps/inventory/dist` and can be deployed to your favorite hosting provider.
You can even create your own `deploy` task that sends the build output to your hosting provider.
Nx will run any script defined in `package.json`, so you can create a `deploy` task that sends the build output to your hosting provider.
```json {% fileName="apps/react-store/project.json" %}
```json {% fileName="apps/react-store/package.json" %}
{
"targets": {
"deploy": {
"dependsOn": ["build"],
"command": "netlify deploy --dir=dist/react-store"
"scripts": {
"deploy": "netlify deploy --dir=dist"
}
```
We want to let Nx know that the `build` task needs to be run before the `deploy` task, so we add a `dependsOn` property for that target.
```json {% fileName="apps/react-store/package.json" %}
{
"scripts": {
"deploy": "netlify deploy --dir=dist"
},
"nx": {
"targets": {
"deploy": {
"dependsOn": ["build"]
}
}
}
}
```
Replace the `command` with whatever terminal command you use to deploy your site.
If you want to keep the script next to its Nx configuration, you can rewrite the above configuration like this:
```json {% fileName="apps/react-store/package.json" %}
{
"scripts": {},
"nx": {
"targets": {
"deploy": {
"command": "netlify deploy --dir=dist",
"dependsOn": ["build"]
}
}
}
}
```
Replace the `deploy` script with whatever terminal command you use to deploy your site.
The `"dependsOn": ["build"]` setting tells Nx to make sure that the project's `build` task has been run successfully before the `deploy` task.
@ -894,30 +930,36 @@ When building these kinds of constraints you usually have two dimensions:
- **type of project:** what is the type of your library. Example: "feature" library, "utility" library, "data-access" library, "ui" library
- **scope (domain) of the project:** what domain area is covered by the project. Example: "orders", "products", "shared" ... this really depends on the type of product you're developing
Nx comes with a generic mechanism that allows you to assign "tags" to projects. "tags" are arbitrary strings you can assign to a project that can be used later when defining boundaries between projects. For example, go to the `project.json` of your `orders` library and assign the tags `type:feature` and `scope:orders` to it.
Nx comes with a generic mechanism that allows you to assign "tags" to projects. "tags" are arbitrary strings you can assign to a project that can be used later when defining boundaries between projects. For example, go to the `package.json` of your `orders` library and assign the tags `type:feature` and `scope:orders` to it.
```json {% fileName="libs/orders/project.json" %}
```json {% fileName="libs/orders/package.json" %}
{
...
"tags": ["type:feature", "scope:orders"]
"nx": {
"tags": ["type:feature", "scope:orders"]
}
}
```
Then go to the `project.json` of your `products` library and assign the tags `type:feature` and `scope:products` to it.
```json {% fileName="libs/products/project.json" %}
```json {% fileName="libs/products/package.json" %}
{
...
"tags": ["type:feature", "scope:products"]
"nx": {
"tags": ["type:feature", "scope:products"]
}
}
```
Finally, go to the `project.json` of the `shared-ui` library and assign the tags `type:ui` and `scope:shared` to it.
```json {% fileName="libs/shared/ui/project.json" %}
```json {% fileName="libs/shared/ui/package.json" %}
{
...
"tags": ["type:ui", "scope:shared"]
"nx": {
"tags": ["type:ui", "scope:shared"]
}
}
```
@ -932,51 +974,55 @@ Next, let's come up with a set of rules based on these tags:
To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.base.json` at the root of the workspace and add the following `depConstraints` in the `@nx/enforce-module-boundaries` rule configuration:
```json {% fileName=".eslintrc.base.json" %}
{
...
"overrides": [
{
...
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{
"sourceTag": "type:feature",
"onlyDependOnLibsWithTags": ["type:feature", "type:ui"]
},
{
"sourceTag": "type:ui",
"onlyDependOnLibsWithTags": ["type:ui"]
},
{
"sourceTag": "scope:orders",
"onlyDependOnLibsWithTags": [
"scope:orders",
"scope:products",
"scope:shared"
]
},
{
"sourceTag": "scope:products",
"onlyDependOnLibsWithTags": ["scope:products", "scope:shared"]
},
{
"sourceTag": "scope:shared",
"onlyDependOnLibsWithTags": ["scope:shared"]
}
]
}
]
}
```js {% fileName="eslint.config.mjs" %}
import nx from '@nx/eslint-plugin';
export default [
// ...
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'],
depConstraints: [
{
sourceTag: 'type:feature',
onlyDependOnLibsWithTags: ['type:feature', 'type:ui'],
},
{
sourceTag: 'type:ui',
onlyDependOnLibsWithTags: ['type:ui'],
},
{
sourceTag: 'scope:orders',
onlyDependOnLibsWithTags: [
'scope:orders',
'scope:products',
'scope:shared',
],
},
{
sourceTag: 'scope:products',
onlyDependOnLibsWithTags: ['scope:products', 'scope:shared'],
},
{
sourceTag: 'scope:shared',
onlyDependOnLibsWithTags: ['scope:shared'],
},
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
},
...
]
}
},
// ...
];
```
To test it, go to your `libs/products/src/lib/products.tsx` file and import the `Orders` component from the `orders` project:
@ -1002,36 +1048,37 @@ export default Products;
If you lint your workspace you'll get an error now:
```{% command="npx nx run-many -t lint" %}
Running target lint for 7 projects
✖ nx run products:lint
Linting "products"...
/Users/isaac/Documents/code/nx-recipes/react-monorepo/libs/products/src/lib/products.tsx
4:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries
4:10 warning 'Orders' is defined but never used @typescript-eslint/no-unused-vars
✔ nx run @react-monorepo/orders:lint [existing outputs match the cache, left as is]
✔ nx run @react-monorepo/react-store:lint [existing outputs match the cache, left as is]
✔ nx run @react-monorepo/inventory:lint [existing outputs match the cache, left as is]
✔ nx run @react-monorepo/ui:lint [existing outputs match the cache, left as is]
✔ nx run inventory-e2e:lint [existing outputs match the cache, left as is]
✔ nx run react-store-e2e:lint (877ms)
✖ 2 problems (1 error, 1 warning)
Lint warnings found in the listed files.
Lint errors found in the listed files.
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
✖ nx run @react-monorepo/products:lint
> eslint .
✔ nx run orders:lint (996ms)
✔ nx run react-store:lint (1s)
✔ nx run react-store-e2e:lint (581ms)
✔ nx run inventory-e2e:lint (588ms)
✔ nx run inventory:lint (836ms)
✔ nx run shared-ui:lint (753ms)
/Users/isaac/Documents/code/nx-recipes/react-monorepo/libs/products/src/lib/products.tsx
3:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries
3:10 warning 'Orders' is defined but never used @typescript-eslint/no-unused-vars
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
✖ 2 problems (1 error, 1 warning)
NX Ran target lint for 7 projects (2s)
✔ 6/7 succeeded [0 read from cache]
✖ 1/7 targets failed, including the following:
- nx run products:lint
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
NX Ran target lint for 7 projects (1s)
✔ 6/7 succeeded [5 read from cache]
✖ 1/7 targets failed, including the following:
- nx run @react-monorepo/products:lint
```
If you have the ESLint plugin installed in your IDE you should also immediately see an error.
@ -1093,17 +1140,25 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# This enables task distribution via Nx Cloud
# Run this command as early as possible, before dependencies are installed
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
# Uncomment this line to enable task distribution
# - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci"
- uses: actions/setup-node@v3
# Cache node_modules
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci --legacy-peer-deps
- run: npx cypress install
- uses: nrwl/nx-set-shas@v4
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
# - run: npx nx-cloud record -- echo Hello World
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
# When you enable task distribution, run the e2e-ci task instead of e2e
- run: npx nx affected -t lint test build e2e

View File

@ -1,9 +1,9 @@
---
title: 'TypeScript Packages Tutorial'
title: 'TypeScript Monorepo Tutorial'
description: In this tutorial you'll add Nx to an existing TypeScript repo
---
# TypeScript Packages Tutorial
# TypeScript Monorepo Tutorial
In this tutorial, you'll learn how to add Nx to a repository with an existing TypeScript project. The starting repository uses [NPM workspaces](https://docs.npmjs.com/cli/using-npm/workspaces) for project linking and is configured to build with [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html).