docs(core): small tutorial updates (#26559)

Update the npm workspaces, react monorepo and angular monorepo tutorials
This commit is contained in:
Isaac Mann 2024-06-21 07:40:14 -04:00 committed by GitHub
parent 5b44085c81
commit 1d1c699c81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 106 deletions

View File

@ -385,24 +385,12 @@ All libraries that we generate automatically have aliases created in the root-le
} }
``` ```
Hence we can easily import them into other libraries and our Angular application. As an example, let's create and expose a `ProductListComponent` component from our `libs/products` library. Either create it by hand or run Hence we can easily import them into other libraries and our Angular application. As an example, let's use the pre-generated `ProductsComponent` component from our `libs/products` library.
```shell You can see that the `ProductsComponent` is exported via the `index.ts` file of our `products` library so that other projects in the repository can use it. This is our public API with the rest of the workspace. Only export what's really necessary to be usable outside the library itself.
nx g @nx/angular:component product-list --directory=libs/products/src/lib/product-list --standalone --export
```
We don't need to implement anything fancy as we just want to learn how to import it into our main Angular application.
```html {% fileName="libs/products/src/lib/product-list/product-list.component.html" %}
<p>product-list works!</p>
```
Make sure the `ProductListComponent` is exported via the `index.ts` file of our `products` library and is listed in the `exports` of the `ProductsModule`. This is our public API with the rest of the workspace. Only export what's really necessary to be usable outside the library itself.
```ts {% fileName="libs/products/src/index.ts" %} ```ts {% fileName="libs/products/src/index.ts" %}
export * from './lib/products/products.component'; export * from './lib/products/products.component';
export * from './lib/product-list/product-list.component';
``` ```
We're ready to import it into our main application now. First (if you haven't already), let's set up the Angular router. Configure it in the `app.config.ts`. We're ready to import it into our main application now. First (if you haven't already), let's set up the Angular router. Configure it in the `app.config.ts`.
@ -426,7 +414,7 @@ And in `app.component.html`:
<router-outlet></router-outlet> <router-outlet></router-outlet>
``` ```
Then we can add the `ProductListComponent` component to our `app.routes.ts` and render it via the routing mechanism whenever a user hits the `/products` route. Then we can add the `ProductsComponent` component to our `app.routes.ts` and render it via the routing mechanism whenever a user hits the `/products` route.
```ts {% fileName="apps/angular-store/src/app/app.routes.ts" highlightLines=[10,11,12,13,14] %} ```ts {% fileName="apps/angular-store/src/app/app.routes.ts" highlightLines=[10,11,12,13,14] %}
import { Route } from '@angular/router'; import { Route } from '@angular/router';
@ -441,7 +429,7 @@ export const appRoutes: Route[] = [
{ {
path: 'products', path: 'products',
loadComponent: () => loadComponent: () =>
import('@angular-monorepo/products').then((m) => m.ProductListComponent), import('@angular-monorepo/products').then((m) => m.ProductsComponent),
}, },
]; ];
``` ```
@ -452,8 +440,7 @@ Serving your app (`nx serve angular-store`) and then navigating to `/products` s
Let's apply the same for our `orders` library. Let's apply the same for our `orders` library.
- generate a new component `OrderListComponent` in `libs/orders` and export it in the corresponding `index.ts` file - import the `OrdersComponent` from `libs/orders` into the `app.routes.ts` and render it via the routing mechanism whenever a user hits the `/orders` route
- import it into the `app.routes.ts` and render it via the routing mechanism whenever a user hits the `/orders` route
In the end, your `app.routes.ts` should look similar to this: In the end, your `app.routes.ts` should look similar to this:
@ -470,12 +457,12 @@ export const appRoutes: Route[] = [
{ {
path: 'products', path: 'products',
loadComponent: () => loadComponent: () =>
import('@angular-monorepo/products').then((m) => m.ProductListComponent), import('@angular-monorepo/products').then((m) => m.ProductsComponent),
}, },
{ {
path: 'orders', path: 'orders',
loadComponent: () => loadComponent: () =>
import('@angular-monorepo/orders').then((m) => m.OrderListComponent), import('@angular-monorepo/orders').then((m) => m.OrdersComponent),
}, },
]; ];
``` ```
@ -484,12 +471,12 @@ 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,6] %}
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ProductListComponent } from '@angular-monorepo/products'; import { ProductsComponent } from '@angular-monorepo/products';
@Component({ @Component({
standalone: true, standalone: true,
imports: [ProductListComponent], imports: [ProductsComponent],
selector: 'angular-monorepo-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'], styleUrls: ['./app.component.css'],
}) })
@ -499,7 +486,7 @@ export class AppComponent {
``` ```
```ts {% fileName="apps/inventory/src/app/app.component.html" %} ```ts {% fileName="apps/inventory/src/app/app.component.html" %}
<angular-monorepo-product-list></angular-monorepo-product-list> <lib-products></lib-products>
``` ```
## Visualizing your Project Structure ## Visualizing your Project Structure
@ -1185,14 +1172,14 @@ To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.ba
} }
``` ```
To test it, go to your `libs/products/src/lib/product-list/product-list.component.ts` file and import the `OrderListComponent` from the `orders` project: To test it, go to your `libs/products/src/lib/product-list/product-list.component.ts` file and import the `OrdersComponent` from the `orders` project:
```ts {% fileName="libs/products/src/lib/product-list/product-list.component.ts" highlightLines=[4,5] %} ```ts {% fileName="libs/products/src/lib/product-list/product-list.component.ts" highlightLines=[4,5] %}
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
// This import is not allowed 👇 // This import is not allowed 👇
import { OrderListComponent } from '@angular-monorepo/orders'; import { OrdersComponent } from '@angular-monorepo/orders';
@Component({ @Component({
selector: 'angular-monorepo-product-list', selector: 'angular-monorepo-product-list',
@ -1201,7 +1188,7 @@ import { OrderListComponent } from '@angular-monorepo/orders';
templateUrl: './product-list.component.html', templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css'], styleUrls: ['./product-list.component.css'],
}) })
export class ProductListComponent {} export class ProductsComponent {}
``` ```
If you lint your workspace you'll get an error now: If you lint your workspace you'll get an error now:
@ -1213,7 +1200,7 @@ NX Running target lint for 7 projects
/Users/isaac/Documents/code/nx-recipes/angular-monorepo/libs/products/src/lib/product-list/product-list.component.ts /Users/isaac/Documents/code/nx-recipes/angular-monorepo/libs/products/src/lib/product-list/product-list.component.ts
5:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries 5:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries
5:10 warning 'OrderListComponent' is defined but never used @typescript-eslint/no-unused-vars 5:10 warning 'OrdersComponent' is defined but never used @typescript-eslint/no-unused-vars
✖ 2 problems (1 error, 1 warning) ✖ 2 problems (1 error, 1 warning)

View File

@ -302,7 +302,7 @@ First, let's delete the `outputs` array from `nx.json` so that we don't override
Now let's add the `@nx/vite` plugin: Now let's add the `@nx/vite` plugin:
```{% command="npx nx add @nx/vite" path="~/tuskydesign" %} ```{% command="npx nx add @nx/vite" path="~/tuskydesign" %}
✔ Installing @nx/vite@18.1.0... ✔ Installing @nx/vite...
✔ Initializing @nx/vite... ✔ Initializing @nx/vite...
NX Package @nx/vite added successfully. NX Package @nx/vite added successfully.
@ -311,13 +311,13 @@ Now let's add the `@nx/vite` plugin:
The `nx add` command installs the version of the plugin that matches your repo's Nx version and runs that plugin's initialization script. For `@nx/vite`, the initialization script registers the plugin in the `plugins` array of `nx.json` and updates any `package.json` scripts that execute Vite related tasks. Open the project details view for the `demo` app and look at the `build` task. The `nx add` command installs the version of the plugin that matches your repo's Nx version and runs that plugin's initialization script. For `@nx/vite`, the initialization script registers the plugin in the `plugins` array of `nx.json` and updates any `package.json` scripts that execute Vite related tasks. Open the project details view for the `demo` app and look at the `build` task.
```shell {% path="~/tuskydesigns" %} ```shell {% path="~/tuskydesigns" %}
npx nx show project @tuskdesign/demo --web npx nx show project @tuskdesign/demo
``` ```
{% project-details title="Project Details View" jsonFile="shared/tutorials/npm-workspaces-pdv.json" %} {% project-details title="Project Details View" jsonFile="shared/tutorials/npm-workspaces-pdv.json" %}
{% /project-details %} {% /project-details %}
If you hover over the settings for the `build` task, you can see where those settings come from. The `inputs` and `outputs` are defined by the `@nx/vite` plugin from the `vite.config.ts` file where as the `dependsOn` property we set earlier in the tutorial in the `targetDefaults` in the `nx.json` file. If you hover over the settings for the `vite:build` task, you can see where those settings come from. The `inputs` and `outputs` are defined by the `@nx/vite` plugin from the `vite.config.ts` file where as the `dependsOn` property we set earlier in the tutorial in the `targetDefaults` in the `nx.json` file.
Now let's change where the `build` results are output to in the `vite.config.ts` file. Now let's change where the `build` results are output to in the `vite.config.ts` file.
@ -403,7 +403,7 @@ This generator creates a `.github/workflows/ci.yml` file that contains a CI pipe
The key line in the CI pipeline is: The key line in the CI pipeline is:
```yml ```yml
- run: npx nx affected -t lint test build e2e-ci - run: npx nx affected -t lint test build
``` ```
### Connect to Nx Cloud ### Connect to Nx Cloud

View File

@ -98,7 +98,7 @@ Let's name the initial application `react-store`. In this tutorial we're going t
The setup includes.. The setup includes..
- a new React application (`apps/react-store/`) - a new React application (`apps/react-store/`)
- a Cypress based set of e2e tests (`apps/react-store-e2e/`) - a Playwright based set of e2e tests (`apps/react-store-e2e/`)
- Prettier preconfigured - Prettier preconfigured
- ESLint preconfigured - ESLint preconfigured
- Jest preconfigured - Jest preconfigured
@ -128,7 +128,7 @@ Nx uses the following syntax to run tasks:
Nx identifies available tasks for your project from [tooling configuration files](/concepts/inferred-tasks), `package.json` scripts and the targets defined in `project.json`. To view the tasks that Nx has detected, look in the [Nx Console](/getting-started/editor-setup) project detail view or run: Nx identifies available tasks for your project from [tooling configuration files](/concepts/inferred-tasks), `package.json` scripts and the targets defined in `project.json`. To view the tasks that Nx has detected, look in the [Nx Console](/getting-started/editor-setup) project detail view or run:
```shell ```shell
nx show project react-store --web nx show project react-store
``` ```
{% project-details title="Project Details View (Simplified)" height="100px" %} {% project-details title="Project Details View (Simplified)" height="100px" %}
@ -137,13 +137,9 @@ nx show project react-store --web
{ {
"project": { "project": {
"name": "react-store", "name": "react-store",
"type": "app",
"data": { "data": {
"metadata": {
"technologies": ["react"]
},
"root": "apps/react-store", "root": "apps/react-store",
"includedScripts": [],
"name": "react-store",
"targets": { "targets": {
"build": { "build": {
"options": { "options": {
@ -161,15 +157,13 @@ nx show project react-store --web
], ],
"outputs": ["{workspaceRoot}/dist/apps/react-store"], "outputs": ["{workspaceRoot}/dist/apps/react-store"],
"executor": "nx:run-commands", "executor": "nx:run-commands",
"configurations": {}, "configurations": {}
"metadata": {
"technologies": ["vite"]
}
} }
}, },
"name": "react-store",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/react-store/src", "sourceRoot": "apps/react-store/src",
"projectType": "application", "projectType": "application",
"$schema": "node_modules/nx/schemas/project-schema.json",
"tags": [], "tags": [],
"implicitDependencies": [] "implicitDependencies": []
} }
@ -433,35 +427,12 @@ All libraries that we generate automatically have aliases created in the root-le
} }
``` ```
Hence we can easily import them into other libraries and our React application. As an example, let's create and expose a `ProductList` component from our `libs/products` library. Either create it by hand or run Hence we can easily import them into other libraries and our Angular application. As an example, let's use the pre-generated `ProductsComponent` component from our `libs/products` library.
```shell You can see that the `Products` component is exported via the `index.ts` file of our `products` library so that other projects in the repository can use it. This is our public API with the rest of the workspace. Only export what's really necessary to be usable outside the library itself.
nx g @nx/react:component product-list --project=products --directory="libs/products/src/lib/product-list"
```
We don't need to implement anything fancy as we just want to learn how to import it into our main React application.
```tsx {% fileName="libs/products/src/lib/product-list/product-list.tsx" %}
import styles from './product-list.module.css';
/* eslint-disable-next-line */
export interface ProductListProps {}
export function ProductList(props: ProductListProps) {
return (
<div className={styles['container']}>
<h1>Welcome to ProductList!</h1>
</div>
);
}
export default ProductList;
```
Make sure the `ProductList` is exported via the `index.ts` file of our `products` library. This is our public API with the rest of the workspace. Only export what's really necessary to be usable outside the library itself.
```ts {% fileName="libs/products/src/index.ts" %} ```ts {% fileName="libs/products/src/index.ts" %}
export * from './lib/product-list/product-list'; export * from './lib/products';
``` ```
We're ready to import it into our main application now. First (if you haven't already), let's set up React Router. We're ready to import it into our main application now. First (if you haven't already), let's set up React Router.
@ -512,13 +483,13 @@ root.render(
); );
``` ```
Then we can import the `ProductList` component into our `app.tsx` and render it via the routing mechanism whenever a user hits the `/products` route. Then we can import the `Products` component into our `app.tsx` and render it via the routing mechanism whenever a user hits the `/products` route.
```tsx {% fileName="apps/react-store/src/app/app.tsx" %} ```tsx {% fileName="apps/react-store/src/app/app.tsx" %}
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
// importing the component from the library // importing the component from the library
import { ProductList } from '@react-monorepo/products'; import { Products } from '@react-monorepo/products';
function Home() { function Home() {
return <h1>Home</h1>; return <h1>Home</h1>;
@ -528,7 +499,7 @@ export function App() {
return ( return (
<Routes> <Routes>
<Route path="/" element={<Home />}></Route> <Route path="/" element={<Home />}></Route>
<Route path="/products" element={<ProductList />}></Route> <Route path="/products" element={<Products />}></Route>
</Routes> </Routes>
); );
} }
@ -542,15 +513,14 @@ Serving your app (`nx serve react-store`) and then navigating to `/products` sho
Let's apply the same for our `orders` library. Let's apply the same for our `orders` library.
- generate a new component `OrderList` in `libs/orders` and export it in the corresponding `index.ts` file - import the `Orders` component from `libs/orders` into the `app.tsx` and render it via the routing mechanism whenever a user hits the `/orders` route
- import it into the `app.tsx` and render it via the routing mechanism whenever a user hits the `/orders` route
In the end, your `app.tsx` should look similar to this: In the end, your `app.tsx` should look similar to this:
```tsx {% fileName="apps/react-store/src/app/app.tsx" %} ```tsx {% fileName="apps/react-store/src/app/app.tsx" %}
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import { ProductList } from '@react-monorepo/products'; import { Products } from '@react-monorepo/products';
import { OrderList } from '@react-monorepo/orders'; import { Orders } from '@react-monorepo/orders';
function Home() { function Home() {
return <h1>Home</h1>; return <h1>Home</h1>;
@ -560,8 +530,8 @@ export function App() {
return ( return (
<Routes> <Routes>
<Route path="/" element={<Home />}></Route> <Route path="/" element={<Home />}></Route>
<Route path="/products" element={<ProductList />}></Route> <Route path="/products" element={<Products />}></Route>
<Route path="/orders" element={<OrderList />}></Route> <Route path="/orders" element={<Orders />}></Route>
</Routes> </Routes>
); );
} }
@ -572,10 +542,10 @@ export default App;
Let's also show products in the `inventory` app. Let's also show products in the `inventory` app.
```tsx {% fileName="apps/inventory/src/app/app.tsx" %} ```tsx {% fileName="apps/inventory/src/app/app.tsx" %}
import { ProductList } from '@react-monorepo/products'; import { Products } from '@react-monorepo/products';
export function App() { export function App() {
return <ProductList />; return <Products />;
} }
export default App; export default App;
@ -735,22 +705,19 @@ git commit -a -m "some commit message"
And then make a small change to the `products` library. And then make a small change to the `products` library.
```tsx {% fileName="libs/products/src/lib/product-list/product-list.tsx" %} ```tsx {% fileName="libs/products/src/lib/products.tsx" %}
import styles from './product-list.module.css'; import styles from './products.module.css';
/* eslint-disable-next-line */ export function Products() {
export interface ProductListProps {}
export function ProductList(props: ProductListProps) {
return ( return (
<div className={styles['container']}> <div className={styles['container']}>
<h1>Welcome to ProductList!</h1> <h1>Welcome to Products!</h1>
<p>This is a change. 👋</p> <p>This is a change. 👋</p>
</div> </div>
); );
} }
export default ProductList; export default Products;
``` ```
One of the key features of Nx in a monorepo setting is that you're able to run tasks only for projects that are actually affected by the code changes that you've made. To run the tests for only the projects affected by this change, run: One of the key features of Nx in a monorepo setting is that you're able to run tasks only for projects that are actually affected by the code changes that you've made. To run the tests for only the projects affected by this change, run:
@ -970,10 +937,6 @@ To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.ba
"enforceBuildableLibDependency": true, "enforceBuildableLibDependency": true,
"allow": [], "allow": [],
"depConstraints": [ "depConstraints": [
{
"sourceTag": "*",
"onlyDependOnLibsWithTags": ["*"]
},
{ {
"sourceTag": "type:feature", "sourceTag": "type:feature",
"onlyDependOnLibsWithTags": ["type:feature", "type:ui"] "onlyDependOnLibsWithTags": ["type:feature", "type:ui"]
@ -1008,27 +971,24 @@ To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.ba
} }
``` ```
To test it, go to your `libs/products/src/lib/product-list/product-list.tsx` file and import the `OrderList` from the `orders` project: To test it, go to your `libs/products/src/lib/products.tsx` file and import the `Orders` component from the `orders` project:
```tsx {% fileName="libs/products/src/lib/product-list/product-list.tsx" %} ```tsx {% fileName="libs/products/src/lib/products.tsx" %}
import styles from './product-list.module.css'; import styles from './products.module.css';
// This import is not allowed 👇 // This import is not allowed 👇
import { OrderList } from '@react-monorepo/orders'; import { Orders } from '@react-monorepo/orders';
/* eslint-disable-next-line */ export function Products() {
export interface ProductListProps {}
export function ProductList(props: ProductListProps) {
return ( return (
<div className={styles['container']}> <div className={styles['container']}>
<h1>Welcome to ProductList!</h1> <h1>Welcome to Products!</h1>
<OrderList /> <p>This is a change. 👋</p>
</div> </div>
); );
} }
export default ProductList; export default Products;
``` ```
If you lint your workspace you'll get an error now: If you lint your workspace you'll get an error now:
@ -1038,9 +998,9 @@ If you lint your workspace you'll get an error now:
✖ nx run products:lint ✖ nx run products:lint
Linting "products"... Linting "products"...
/Users/isaac/Documents/code/nx-recipes/react-monorepo/libs/products/src/lib/product-list/product-list.tsx /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: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 'OrderList' is defined but never used @typescript-eslint/no-unused-vars 4:10 warning 'Orders' is defined but never used @typescript-eslint/no-unused-vars
✖ 2 problems (1 error, 1 warning) ✖ 2 problems (1 error, 1 warning)