Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NX-Workspace: Simplified scaling for large Angular Projects

NX-Workspace: Simplified scaling for large Angular Projects

Production build and testing takes longer and longer to execute. It takes more and more time to find the right component or the desired service, the effort for on-boarding new colleagues grows. What does this tell you? That's right, you're working on a big Angular project - no more, no less. When the complexity of a project grows rapidly, one thing is needed: focus on the essentials without repetitive, manual tuning of the CI/CD pipeline. NX provides solutions for extending, testing and publishing your Angular project. In this talk, you'll learn how to organize multiple builds and unit & UI testing with NX. You'll see NX's extensions for central state management in action. You'll also see StoryBook's integration with Nx for living and up-to-date documentation of your component library. Another tool you'll learn about is Workspace Schematics, which allows you to further automate the development process.

Fabian Gosebrink

April 27, 2021
Tweet

More Decks by Fabian Gosebrink

Other Decks in Technology

Transcript

  1. App

  2. App

  3. App

  4. import { NgModule } from '@angular/core'; @NgModule({ imports: [ ...

    ], declarations: [ AppComponent, HelloComponent, Hello1Component, Hello2Component, Hello3Component, Hello4Component, Hello5Component, Hello6Component, Hello7Component, Hello8Component, Hello9Component, Hello10Component, Hello11Component, Hello12Component, Hello13Component, Hello14Component, Hello15Component 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  5. import { NgModule } from '@angular/core'; @NgModule({ imports: [ ...

    ], declarations: [ AppComponent, Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ... ], bootstrap: [AppComponent] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  6. import { NgModule } from '@angular/core'; import { CommonModule }

    from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [ Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, ] }) export class Feature1Module { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  7. import { NgModule } from '@angular/core'; import { CommonModule }

    from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [ Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ] }) export class Feature2Module { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  8. import { NgModule } from '@angular/core'; @NgModule({ imports: [ ],

    declarations: [ AppComponent, Feature1Component1, Feature1Component2, Feature1Component3, Feature1Component4, Feature1Component5, Feature1Component6, Feature2Component1, Feature2Component2, Feature2Component3, Feature2Component4, Feature2Component5, Feature2Component6, ], }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  9. import { NgModule } from '@angular/core'; import { Feature1Module }

    from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; @NgModule({ imports: [ BrowserModule, FormsModule, Feature1Module, Feature2Module ], declarations: [ AppComponent, HelloComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  10. import { NgModule } from '@angular/core'; import { Feature1Module }

    from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; @NgModule({ imports: [ BrowserModule, FormsModule, Feature1Module, Feature2Module ], declarations: [ AppComponent, HelloComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { Feature1Module } from './feature1-module/feature1.module'; import { Feature2Module } from './feature2-module/feature2.module'; Feature1Module, Feature2Module import { NgModule } from '@angular/core'; 1 2 3 4 @NgModule({ 5 imports: [ 6 BrowserModule, 7 FormsModule, 8 9 10 ], 11 declarations: [ 12 AppComponent, 13 HelloComponent 14 ], 15 bootstrap: [ AppComponent ] 16 }) 17 export class AppModule { } 18
  11. import { /* ... */ } from 'lib-to-configure'; import {

    NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8
  12. import { /* ... */ } from 'lib-to-configure'; import {

    NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8
  13. @NgModule({ declarations: [LibToConfigureComponent], imports: [CommonModule], exports: [LibToConfigureComponent], }) export class

    LibToConfigureModule {} 1 2 3 4 5 6 @NgModule({ declarations: [AppComponent], imports: [BrowserModule, LibToConfigureModule], providers: [], bootstrap: [AppComponent], }) export class AppModule {} 1 2 3 4 5 6 7
  14. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  15. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable<Meetup[]>; loading$: Observable<boolean>; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21
  16. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable<Meetup[]>; loading$: Observable<boolean>; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21
  17. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable<Meetup[]>; loading$: Observable<boolean>; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21
  18. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable<Meetup[]>; loading$: Observable<boolean>; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 19 } 20 } 21
  19. Stateful import { /* ... */ } from '...'; @Component({

    selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 items$: Observable<Meetup[]>; loading$: Observable<boolean>; import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 9 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 constructor(private store: Store) {} import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 15 16 17 18 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); 19 } 20 } 21 this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); import { /* ... */ } from '...'; 1 2 @Component({ 3 selector: 'workspace-groups-meetups-list', 4 templateUrl: './groups-meetups-list.component.html', 5 styleUrls: ['./groups-meetups-list.component.scss'], 6 }) 7 export class GroupsMeetupsListComponent implements OnInit { 8 items$: Observable<Meetup[]>; 9 loading$: Observable<boolean>; 10 11 constructor(private store: Store) {} 12 13 ngOnInit() { 14 this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); 15 16 this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); 17 18 19 } 20 } 21 import { /* ... */ } from '...'; @Component({ selector: 'workspace-groups-meetups-list', templateUrl: './groups-meetups-list.component.html', styleUrls: ['./groups-meetups-list.component.scss'], }) export class GroupsMeetupsListComponent implements OnInit { items$: Observable<Meetup[]>; loading$: Observable<boolean>; constructor(private store: Store) {} ngOnInit() { this.loading$ = this.store.pipe(select(fromGroupStore.selectIsLoading)); this.items$ = this.store.pipe(select(fromGroupStore.selectAllMeetups)); this.store.dispatch(fromGroupStore.getAllMeetupsFromGroup()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  20. Stateful <div class="container"> <h1 class="mt-3">All Gatherings of the Group</h1> <mat-progress-bar

    *ngIf="loading$ | async; else list" mode="indeterminate" ></mat-progress-bar> <ng-template #list> <workspace-meetup-list [items]="items$ | async"> </workspace-meetup-list> </ng-template> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13
  21. Stateful <div class="container"> <h1 class="mt-3">All Gatherings of the Group</h1> <mat-progress-bar

    *ngIf="loading$ | async; else list" mode="indeterminate" ></mat-progress-bar> <ng-template #list> <workspace-meetup-list [items]="items$ | async"> </workspace-meetup-list> </ng-template> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13 <workspace-meetup-list [items]="items$ | async"> </workspace-meetup-list> <div class="container"> 1 <h1 class="mt-3">All Gatherings of the Group</h1> 2 3 <mat-progress-bar 4 *ngIf="loading$ | async; else list" 5 mode="indeterminate" 6 ></mat-progress-bar> 7 8 <ng-template #list> 9 10 11 </ng-template> 12 </div> 13
  22. Stateless import { /* ... */ } from '...'; @Component({

    selector: 'workspace-meetup-list', templateUrl: './meetup-list.component.html', styleUrls: ['./meetup-list.component.scss'], }) export class MeetupListComponent { @Input() items: Meetup[] = []; } 1 2 3 4 5 6 7 8 9 10 11 12
  23. Stateless <div class="mb-20"> <ng-template #noItems> No items found... </ng-template> <div

    *ngIf="items?.length > 0; else noItems"> <ul> <li *ngFor="let item of items"> {{ item.name }} </li> </ul> </div> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  24. Stateless <workspace-meetup-item *ngFor="let item of items" [item]="item" > <div class="mb-20">

    1 <ng-template #noItems> 2 No items found... 3 </ng-template> 4 5 <div *ngIf="items?.length > 0; else noItems"> 6 7 8 9 10 </workspace-meetup-item> 11 </div> 12 </div> 13
  25. Stateless <workspace-meetup-item *ngFor="let item of items" [item]="item" > <div class="mb-20">

    1 <ng-template #noItems> 2 No items found... 3 </ng-template> 4 5 <div *ngIf="items?.length > 0; else noItems"> 6 7 8 9 10 </workspace-meetup-item> 11 </div> 12 </div> 13 <div class="mb-20"> <ng-template #noItems> No items found... </ng-template> <div *ngIf="items?.length > 0; else noItems"> <workspace-meetup-item *ngFor="let item of items" [item]="item" > </workspace-meetup-item> </div> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13
  26. Stateless <workspace-meetup-item *ngFor="let item of items" [item]="item" (itemClicked)="navigateToItem($event)" > <div

    class="mb-20"> 1 <ng-template #noItems> 2 No items found... 3 </ng-template> 4 5 <div *ngIf="items?.length > 0; else noItems"> 6 7 8 9 10 11 </workspace-meetup-item> 12 </div> 13 </div> 14
  27. Stateless <div class="mb-20"> <ng-template #noItems> No items found... </ng-template> <div

    *ngIf="items?.length > 0; else noItems"> <workspace-meetup-item *ngFor="let item of items" [item]="item" (itemClicked)="bubbleNavigateEvent($event)" > </workspace-meetup-item> </div> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  28. Stateless import { /* ... */ } from '...'; @Component({

    selector: 'workspace-meetup-list', templateUrl: './meetup-list.component.html', styleUrls: ['./meetup-list.component.scss'], }) export class MeetupListComponent { @Input() items: Meetup[] = []; @Output() itemClicked = new EventEmitter(); bubbleNavigationEvent(meetup: Meetup) { this.itemClicked.emit(meetup); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  29. “ Nx is built on a technology-agnostic core that maintains

    modular units of code and understands the dependency graph between them. nx.dev
  30. What is it? The dependency graph helps you overlook all

    your applications and libraries. You learn about relations and dependencies of each module in your workspace.
  31. Command nx dep-graph 1 2 > NX NOTE Dep graph

    started at http://127.0.0.1:4211 3
  32. Command nx dep-graph 1 2 > NX NOTE Dep graph

    started at http://127.0.0.1:4211 3 > NX NOTE Dep graph started at http://127.0.0.1:4211 nx dep-graph 1 2 3
  33. Why is it helpful? See impact a library has on

    the workspace. Optimize build, lint, serve, test commands.
  34. main feature HEAD nx affected:dep-graph \ --base main How it

    works Analyse differences between git branches
  35. Command nx affected:dep-graph 1 \ --base <branch-name> # default: main

    2 \ --head <branch-comparing> # default: HEAD 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5
  36. Command nx affected:dep-graph 1 \ --base <branch-name> # default: main

    2 \ --head <branch-comparing> # default: HEAD 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5 \ --head <branch-comparing> # default: HEAD nx affected:dep-graph 1 \ --base <branch-name> # default: main 2 3 4 > NX NOTE Dep graph started at http://127.0.0.1:4211 5
  37. nx.json "projects": { { 1 2 "gatherr": { 3 "tags":

    [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26
  38. nx.json "projects": { { 1 2 "gatherr": { 3 "tags":

    [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26 "gatherr": { "gatherr-e2e": { "auth-feature": { "groups-feature": { "home-feature": { "personal-feature": { "profile-feature": { { 1 "projects": { 2 3 "tags": [] 4 }, 5 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 10 "tags": [] 11 }, 12 13 "tags": [] 14 }, 15 16 "tags": [] 17 }, 18 19 "tags": [] 20 }, 21 22 "tags": [] 23 } 24 } 25 } 26
  39. nx.json "projects": { { 1 2 "gatherr": { 3 "tags":

    [] 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 "tags": [] 11 }, 12 "groups-feature": { 13 "tags": [] 14 }, 15 "home-feature": { 16 "tags": [] 17 }, 18 "personal-feature": { 19 "tags": [] 20 }, 21 "profile-feature": { 22 "tags": [] 23 } 24 } 25 } 26 "gatherr": { "gatherr-e2e": { "auth-feature": { "groups-feature": { "home-feature": { "personal-feature": { "profile-feature": { { 1 "projects": { 2 3 "tags": [] 4 }, 5 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 10 "tags": [] 11 }, 12 13 "tags": [] 14 }, 15 16 "tags": [] 17 }, 18 19 "tags": [] 20 }, 21 22 "tags": [] 23 } 24 } 25 } 26 "tags": [] "tags": [], "tags": [] "tags": [] "tags": [] "tags": [] "tags": [] { 1 "projects": { 2 "gatherr": { 3 4 }, 5 "gatherr-e2e": { 6 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 11 }, 12 "groups-feature": { 13 14 }, 15 "home-feature": { 16 17 }, 18 "personal-feature": { 19 20 }, 21 "profile-feature": { 22 23 } 24 } 25 } 26
  40. nx.json "tags": ["app"] "tags": ["app-feature"] "tags": ["app-feature"] "tags": ["app-feature"] "tags":

    ["app-feature"] "tags": ["app-feature"] { 1 "projects": { 2 "gatherr": { 3 4 }, 5 "gatherr-e2e": { 6 "tags": [], 7 "implicitDependencies": ["gatherr"] 8 }, 9 "auth-feature": { 10 11 }, 12 "groups-feature": { 13 14 }, 15 "home-feature": { 16 17 }, 18 "personal-feature": { 19 20 }, 21 "profile-feature": { 22 23 } 24 } 25 } 26
  41. linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error",

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15
  42. linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error",

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15
  43. linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error",

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15
  44. linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error",

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "depConstraints": [ { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15
  45. linter "@nrwl/nx/enforce-module-boundaries": [ { 1 "rules": { 2 3 "error",

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "enforceBuildableLibDependency": true, { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "allow": [], { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 "depConstraints": [ { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 } 12 }] 13 } 14 } 15 { "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] } { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 9 10 11 12 }] 13 } 14 } 15
  46. linter { "sourceTag": "app", "onlyDependOnLibsWithTags": ["app-feature"] }] { 1 "rules":

    { 2 "@nrwl/nx/enforce-module-boundaries": [ 3 "error", 4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 13 14 15 16 }] 17 } 18 } 19
  47. linter "error", { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 { 13 "sourceTag": "app", 14 "onlyDependOnLibsWithTags": ["app-feature"] 15 }] 16 }] 17 } 18 } 19
  48. linter "warn", { 1 "rules": { 2 "@nrwl/nx/enforce-module-boundaries": [ 3

    4 { 5 "enforceBuildableLibDependency": true, 6 "allow": [], 7 "depConstraints": [ 8 { 9 "sourceTag": "*", 10 "onlyDependOnLibsWithTags": ["*"] 11 }, 12 { 13 "sourceTag": "app", 14 "onlyDependOnLibsWithTags": ["app-feature"] 15 }] 16 }] 17 } 18 } 19
  49. "paths": { "@workspace/shared/ui-common": ["libs/shared/ui-common/src/index.ts"], "@workspace/shared/ui-layout": ["libs/shared/ui-layout/src/index.ts"], "@workspace/shared/data": ["libs/shared/data/src/index.ts"], "@workspace/shared/utils": ["libs/shared/utils/src/index.ts"],

    "@workspace/category/data": ["libs/category/data/src/index.ts"], "@workspace/category/api": ["libs/category/api/src/index.ts"], "@workspace/auth/api": ["libs/auth/api/src/index.ts"], "@workspace/auth/feature": ["libs/auth/feature/src/index.ts"], "@workspace/auth/util": ["libs/auth/util/src/index.ts"], "@workspace/auth/data": ["libs/auth/data/src/index.ts"], "@workspace/home/feature": ["libs/home/feature/src/index.ts"], "@workspace/home/ui": ["libs/home/ui/src/index.ts"], "@workspace/maps/util": ["libs/maps/util/src/index.ts"], "@workspace/groups/api": ["libs/groups/api/src/index.ts"], "@workspace/groups/feature": ["libs/groups/feature/src/index.ts"], "@workspace/groups/ui": ["libs/groups/ui/src/index.ts"], "@workspace/groups/data": ["libs/groups/data/src/index.ts"], "@workspace/personal/feature": ["libs/personal/feature/src/index.ts"], { 1 "compileOnSave": false, 2 "compilerOptions": { 3 "rootDir": ".", 4 "sourceMap": true, 5 "declaration": false, 6 "moduleResolution": "node", 7 "emitDecoratorMetadata": true, 8 "experimentalDecorators": true, 9 "importHelpers": true, 10 "target": "es5", 11 "module": "esnext", 12 "typeRoots": ["node_modules/@types"], 13 "lib": ["es2017", "dom"], 14 "skipLibCheck": true, 15 "skipDefaultLibCheck": true, 16 "baseUrl": ".", 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
  50. import { /* ... */ } from 'lib-to-configure'; import {

    NgModule, APP_INITIALIZER, Injectable } from '@angular/core'; 1 import { BrowserModule } from '@angular/platform-browser'; 2 3 4 5 import { AppComponent } from './app.component'; 6 7 export class AppModule {} 8
  51. Why is it helpful? Benefit from automatic migration scripts. Get

    new features enabling your team building more efficient solutions.
  52. nx migrate latest 1 2 > NX NOTE Dep graph

    started at http://127.0.0.1:4211 3 Command
  53. nx migrate latest 1 2 > NX NOTE Dep graph

    started at http://127.0.0.1:4211 3 > NX NOTE Dep graph started at http://127.0.0.1:4211 nx migrate latest 1 2 3 Command
  54. ↑ nx migrate latest Fetching meta data about packages. It

    may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 Prepare Migration
  55. ↑ nx migrate latest Fetching meta data about packages. It

    may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - migrations.json has been generated ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 Prepare Migration
  56. ↑ nx migrate latest Fetching meta data about packages. It

    may take a few minutes. Fetching @nrwl/workspace@latest Fetching @nrwl/[email protected] ... Fetching [email protected] Fetching @types/[email protected] Fetching [email protected] Fetching [email protected] .... Fetching @angular/cli@^11.2.0 ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - migrations.json has been generated ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 - Run 'nx migrate --run-migrations=migrations.json' 26 - Run 'nx migrate --run-migrations=migrations.json' ↑ nx migrate latest 1 2 Fetching meta data about packages. 3 It may take a few minutes. 4 Fetching @nrwl/workspace@latest 5 Fetching @nrwl/[email protected] 6 ... 7 8 Fetching [email protected] 9 Fetching @types/[email protected] 10 Fetching [email protected] 11 Fetching [email protected] 12 .... 13 14 Fetching @angular/cli@^11.2.0 15 ... 16 17 > NX The migrate command has run successfully. 18 19 - package.json has been updated 20 - migrations.json has been generated 21 22 > NX Next steps: 23 24 - Make sure package.json changes make sense and then run 25 26 Prepare Migration
  57. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 migrations.json
  58. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json
  59. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json
  60. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json
  61. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "package": "@nrwl/workspace", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 migrations.json
  62. "migrations": [ ] { 1 2 { 3 "version": "12.x.x",

    4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 11 } 12 "version": "12.x.x", { 1 "migrations": [ 2 { 3 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "description": "Update the decoration script when using Angular CLI", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 6 "package": "@nrwl/workspace", 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "package": "@nrwl/workspace", { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 7 "name": "update-decorate-angular-cli" 8 }, 9 ... 10 ] 11 } 12 "name": "update-decorate-angular-cli" { 1 "migrations": [ 2 { 3 "version": "12.x.x", 4 "description": "Update the decoration script when using Angular CLI", 5 "factory": "./src/migrations/update-11-0-0/update-decorate-angular-cli", 6 "package": "@nrwl/workspace", 7 8 }, 9 ... 10 ] 11 } 12 migrations.json
  63. > NX Running migrations from 'migrations.json' 1 2 Running migration

    rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Execute Migration
  64. > NX Running migrations from 'migrations.json' 1 2 Running migration

    rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Running migration rename-workspace-schematic-script Running migration update-ngcc-postinstall Running migration migration-v11.1-can-activate-with-redirect-to Running migration always-use-project-level-tsconfigs-with-eslint > NX Running migrations from 'migrations.json' 1 2 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Execute Migration
  65. > NX Running migrations from 'migrations.json' 1 2 Running migration

    rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 Running migration rename-workspace-schematic-script Running migration update-ngcc-postinstall Running migration migration-v11.1-can-activate-with-redirect-to Running migration always-use-project-level-tsconfigs-with-eslint > NX Running migrations from 'migrations.json' 1 2 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 > NX Successfully finished running migrations from 'migrations.json' 16 > NX Successfully finished running migrations from 'migrations.json' > NX Running migrations from 'migrations.json' 1 2 Running migration rename-workspace-schematic-script 3 Successfully finished rename-workspace-schematic-script 4 --------------------------------------------------------- 5 Running migration update-ngcc-postinstall 6 Successfully finished update-ngcc-postinstall 7 --------------------------------------------------------- 8 Running migration migration-v11.1-can-activate-with-redirect-to 9 Successfully finished migration-v11.1-can-activate-with-redirect-to 10 --------------------------------------------------------- 11 Running migration always-use-project-level-tsconfigs-with-eslint 12 Successfully finished always-use-project-level-tsconfigs-with-eslint 13 --------------------------------------------------------- 14 15 16 Execute Migration
  66. Caution Migrations can fail. Be aware of script errors or

    situations NX cannot automatically complete the migration for you.
  67. Trouble- shooting - Read errors carefully. - Check GitHub for

    existing issues. - File an issue if you have encountered a new bug.
  68. ESLint Updating to the latest NX workspace allows to migrating

    from TSLint to ESLint automatically. You can migrate one project after another to split the effort.
  69. nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular:

    3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 List nx commands
  70. nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular:

    3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 init : Initialize the @nrwl/angular plugin application : Create an Angular application library : Create an Angular library karma : Add karma configuration to a workspace karma-project : Add karma testing to a project ngrx : Add an ngrx config to a project downgrade-module : Setup Downgrade Module upgrade-module : Add an upgrade module storybook-configuration : Create stories/specs for all components declared in a library storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story stories : Create stories/specs for all components declared in a library component-cypress-spec : Create a cypress spec for a ui component that has a story component-story : Create a stories.ts file for a component move : Move an Angular application or library to another folder add-linting : Add linting configuration to an Angular project convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 List nx commands
  71. nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular:

    3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 convert-tslint-to-eslint : Convert a project from TSLint to ESLint 22 init : Initialize the @nrwl/angular plugin application : Create an Angular application library : Create an Angular library karma : Add karma configuration to a workspace karma-project : Add karma testing to a project ngrx : Add an ngrx config to a project downgrade-module : Setup Downgrade Module upgrade-module : Add an upgrade module storybook-configuration : Create stories/specs for all components declared in a library storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story stories : Create stories/specs for all components declared in a library component-cypress-spec : Create a cypress spec for a ui component that has a story component-story : Create a stories.ts file for a component move : Move an Angular application or library to another folder add-linting : Add linting configuration to an Angular project convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 convert-tslint-to-eslint : Convert a project from TSLint to ESLint nx list @nrwl/angular 1 2 > NX Capabilities in @nrwl/angular: 3 4 GENERATORS 5 6 init : Initialize the @nrwl/angular plugin 7 application : Create an Angular application 8 library : Create an Angular library 9 karma : Add karma configuration to a workspace 10 karma-project : Add karma testing to a project 11 ngrx : Add an ngrx config to a project 12 downgrade-module : Setup Downgrade Module 13 upgrade-module : Add an upgrade module 14 storybook-configuration : Create stories/specs for all components declared in a library 15 storybook-migrate-defaults-5-to-6 : Generate default Storybook configuration files using Story 16 stories : Create stories/specs for all components declared in a library 17 component-cypress-spec : Create a cypress spec for a ui component that has a story 18 component-story : Create a stories.ts file for a component 19 move : Move an Angular application or library to another folder 20 add-linting : Add linting configuration to an Angular project 21 22 List nx commands
  72. Why is it helpful? Benefit from automatic migration scripts. Get

    new features enabling your team building more efficient solutions.
  73. Run

  74. Wrap up Workspace Enterprise Applications Latest Toolset Affected Build &

    Cache SoC through Libs Dep Graph Two-Step Migration
  75. Wrap up Workspace Enterprise Applications Latest Toolset Affected Build &

    Cache SoC through Libs Dep Graph Two-Step Migration Plugin Architecture