@dsivd/prestations-ng 19.0.0-beta.1 → 19.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/UPGRADING_V19.md CHANGED
@@ -81,7 +81,7 @@ Replace `com.fasterxml.jackson.databind.ObjectMapper` with `tools.jackson.databi
81
81
 
82
82
  ### BO : Brave Tracer
83
83
 
84
- If you are using Brave Tracer (search `brace.Tracer` in the code, usually in backoffices), you need to replace it with
84
+ If you are using Brave Tracer (search `brave.Tracer` in the code, usually in backoffices), you need to replace it with
85
85
  OpenTelemetry by adding the following dependencies (already included by `ch.vd.cyber:starter-bo-jdbc-jms` and `ch.vd.cyber:starter-prestation`):
86
86
 
87
87
  ```xml
@@ -102,7 +102,8 @@ And replace `brave.Tracer` imports by `io.micrometer.tracing.Tracer`;
102
102
 
103
103
  This base class has been moved from `ch.vd.cyber.sdk:cyberadmin-be` to `ch.vd.cyber.sdk:backoffice-be`
104
104
  and renamed `EsbMessageListenerWithTracingPropagation`.
105
- the constructor has also been updated :
105
+
106
+ The constructor has also been updated :
106
107
 
107
108
  ```java
108
109
  import ch.vd.cyberadminbe.jms.EsbMessageListenerWithSleuthIntegration;
@@ -132,7 +133,7 @@ public class MyListener extends EsbMessageListenerWithTracingPropagation {
132
133
  }
133
134
  ```
134
135
 
135
- finally, if you have mock issues with you integration tests, you can use `ch.vd.cyber.backofficebe.jms.JmsTestUtils.mockTracingPropagation(final Tracer tracerMock, final Propagator propagatorMock)`
136
+ finally, if you have mock issues with your integration tests, you can use `ch.vd.cyber.backofficebe.jms.JmsTestUtils.mockTracingPropagation(final Tracer tracerMock, final Propagator propagatorMock)`
136
137
 
137
138
  ### BO : ESB Integration
138
139
 
@@ -153,6 +154,13 @@ Run your application with this dependency :
153
154
 
154
155
  Look for logs that indicate property you should migrate (watch for WARN logs of class PropertiesMigrationListener), update them and remove the dependency
155
156
 
157
+ ### BO using jpa repositories
158
+
159
+ As we also have jdbc in the classpath, to avoid having dozens of log like "Spring Data JDBC - Could not safely
160
+ identify store assignment for repository candidate interface", add the property in your application.properties :
161
+
162
+ spring.data.jdbc.repositories.enabled=false
163
+
156
164
  ### Debugging tips
157
165
 
158
166
  Spring boot 4 doesn't support calling a controller with a trailing slash, fix the url called (/api/ping != /api/ping**/**)
@@ -172,3 +180,1219 @@ mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=or
172
180
  ```
173
181
 
174
182
  Take a look at Sonar to see other things that you can improve or ignore.
183
+
184
+ ## PRESTATIONS-NG
185
+
186
+ For the commands in this guide, it is supposed that you are in the `front` folder of your project.
187
+
188
+ The main feature in `prestations-ng` v19 is the update of the `@angular/*` peer dependencies
189
+ to `^21.2.14`, using `node 22+` to build, `npm 10+` for package manager and `Foehn 2+ (Bootstrap 5+)` for our design system.
190
+
191
+ A lot of breaking because our library has migrated to **standalone** components, **signals** and **inject function** instead of constructor injection parameters.
192
+
193
+ > 🚨 **Read carefully following [PITFALLS](ANGULAR_SIGNALS.md#common_pitfalls), especially points 4, 6 & 8**
194
+
195
+ ### Migrate to ESLint
196
+
197
+ (NB : you can skip this step if you already have a `front/.eslintrc.json` file, it means it is already done.)
198
+
199
+ Follow [our migration guide](ESLINT_MIGRATION_GUIDE.md) to migrate your project from TSLint to ESLint.
200
+
201
+ ### Upgrade to Angular 20
202
+
203
+ Just FYI if you are curious about the details, you can find the Angular update guide at this address [https://angular.dev/update-guide?v=19.0-20.0&l=1](https://angular.dev/update-guide?v=19.0-20.0&l=1)
204
+
205
+ **NB: Commit before continuing so your repository is clean.**
206
+
207
+ Remove your `resolutions` in `package.json` before starting : if you have `resolutions` in `package.json`, remove manually the section.
208
+
209
+ In the next commands, commit before each ng update.
210
+
211
+ ```bash
212
+ npm install
213
+ npm install @angular/cli@19 -g
214
+ ng update @angular/core@19 @angular/cli@19
215
+ ng update @dsivd/prestations-ng@18
216
+ ```
217
+
218
+ #### Switch to node 22+
219
+
220
+ ##### If you are using our `slave-apps`
221
+
222
+ Be sure to update your `slave-apps` git repository and source your `.bashrc` (or whatever file you're using).
223
+
224
+ Verify your node version in your terminal by executing:
225
+
226
+ ```bash
227
+ node -v
228
+ ```
229
+
230
+ **NB: If version is still v20+ by default, type `node22` in your terminal to switch to node v22+**
231
+
232
+ ```bash
233
+ node22
234
+ ```
235
+
236
+ ##### If you are NOT using our `slave-apps`
237
+
238
+ Be sure to use a node version 22+ in your terminal:
239
+
240
+ ```bash
241
+ node -v
242
+ ```
243
+
244
+ #### Continue with Angular 20 migration
245
+
246
+ ```bash
247
+ npm install @angular/cli@20 -g
248
+ ng update @angular/core@20 @angular/cli@20
249
+ ```
250
+
251
+ - When asked "Select the migrations that you'd like to run",
252
+ - accept **"use-application-builder"** by hitting "Space" key
253
+ - hit "Enter" key
254
+ - When asked "Select the migrations that you'd like to run",
255
+ - accept **"control-flow-migration"** by hitting "Space" key
256
+ - hit "Down arrow" key
257
+ - accept **"router-current-navigation"** by hitting "Space" key
258
+ - hit "Enter" key
259
+
260
+ **IMPORTANT: Search for every `@for` in your project and check that `track` statement is present and has a unique identifier.
261
+ When a unique identifier is not possible, use `track $index`.**
262
+
263
+ **NB: When using `@angular/cdk` in your project**
264
+
265
+ ```bash
266
+ ng update @angular/cdk@20
267
+ ```
268
+
269
+ ### Running with node 22+
270
+
271
+ #### In your project
272
+
273
+ Update your `package.json` file to specify the node engine:
274
+
275
+ ```json
276
+ {
277
+ "engines": {
278
+ "node": ">=22.18.0"
279
+ }
280
+ }
281
+ ```
282
+
283
+ #### Update your jenkins files
284
+
285
+ - Change `nodeVersion: 20` to use node 22
286
+
287
+ ##### UT folder
288
+
289
+ ```diff
290
+ buildAppPipelinePodmanUT([
291
+ - node: 20,
292
+ + node: 22,
293
+ ])
294
+ ```
295
+
296
+ ### Upgrade to Angular 21
297
+
298
+ `prestations-ng` **v19+** now supports **Angular v21**.
299
+
300
+ Just FYI if you are curious about the details, you can find the Angular update guide at this address [https://angular.dev/update-guide?v=20.0-21.0&l=1](https://angular.dev/update-guide?v=20.0-21.0&l=1)
301
+
302
+ **NB: Commit before continuing so your repository is clean.**
303
+
304
+ ```bash
305
+ npm install @angular/cli@21 -g
306
+ ng update @dsivd/prestations-ng@latest
307
+ ```
308
+
309
+ - When asked again "Select the migrations that you'd like to run"
310
+ - accept **"router-current-navigation"** by hitting "Space" key
311
+ - hit "Enter" key
312
+
313
+ ```bash
314
+ ng update @angular/cli@21
315
+ ```
316
+
317
+ - When asked "Select the migrations that you'd like to run"
318
+ - accept **"use-application-builder"** by hitting "Space" key
319
+ - hit "Enter" key
320
+
321
+ ```bash
322
+ npm install @types/node@22 ts-node@10
323
+ ```
324
+
325
+ **NB: When using `@angular/cdk` in your project**
326
+
327
+ ```bash
328
+ ng update @angular/cdk@21
329
+ ```
330
+
331
+ **NB: When using `ngx-matomo-client` in your project**
332
+
333
+ ```bash
334
+ ng update ngx-matomo-client@9
335
+ ```
336
+
337
+ ### Quick fix
338
+
339
+ - In all custom components extending `FoehnInputComponent`, in `providers` providing `FoehnInputComponent` remove `multi: true,`.
340
+
341
+ ```diff
342
+ {
343
+ provide: FoehnInputComponent,
344
+ useExisting: forwardRef(() => ItemListComponent),
345
+ - multi: true,
346
+ }
347
+ ```
348
+
349
+ - In your `.eslintrc.json`, remove `"/src/main.ts",` from `ignorePatterns`.
350
+ - In `main.ts`, replace `platformBrowserDynamic` by `platformBrowser`.
351
+ - When using `<foehn-list-summary` component, replace `list` by `model`.
352
+
353
+ ### Now fix your project before continuing
354
+
355
+ **IMPORTANT: Fix your project because our new prestations-ng v19+ is standalone, uses signals instead of observables and inject function instead of constructor injection parameter.**
356
+
357
+ - Only keep `modules` relative to your application such as `AppRoutingModule`.
358
+ - Remove all other imports of **modules** and make sure you are using the new **standalone components** by adding them directly from your HTML file "alt+enter".
359
+ - Add `providePrestationsNgCore(),` in `providers` array of your `app.modules.ts` file.
360
+ - Check every `constructor` in your project, and remove useless `constructor` parameters that are not used anymore (like `ActivatedRoute` in components extending `AbstractPageComponent`).
361
+
362
+ Run the following command until you have no more errors.
363
+
364
+ ```bash
365
+ npm run build
366
+ ```
367
+
368
+ **TIP: If your IntelliJ is still showing you errors on `@if` or `@for`, close it and re-open it to fix.**
369
+
370
+ ### Migrate to inject function
371
+
372
+ ```bash
373
+ ng generate @angular/core:inject
374
+ ```
375
+
376
+ ✔ Which directory do you want to migrate? **./**
377
+
378
+ ✔ Do you want to migrate abstract classes? Abstract classes are not migrated by default, because their parameters aren't guaranteed to be injectable. **Yes**
379
+
380
+ ✔ Do you want to clean up all constructors or keep them backwards compatible? Enabling this option will include an additional signature of `constructor(...args:
381
+ unknown[]);` that will avoid errors for sub-classes, but will increase the amount of generated code by the migration. **No**
382
+
383
+ ✔ Do you want optional inject calls to be non-nullable? Enable this option if you want the return type to be identical to @Optional(), at the expense of worse
384
+ type safety. **Yes**
385
+
386
+ Then:
387
+
388
+ - Go through every file that has changed and fix what is broken.
389
+ - Reorder public / private and remove unused imports so ESLINT is happy again.
390
+
391
+ Little example of cleaning when extending `AbstractPageComponent`:
392
+
393
+ Before:
394
+
395
+ ```ts
396
+ sessionInfoData: SessionInfoWithApplicationService;
397
+
398
+ constructor(
399
+ protected override activatedRoute: ActivatedRoute,
400
+ private readonly dictionaryService: SdkDictionaryService,
401
+ sessionInfoData: SessionInfoWithApplicationService
402
+ ) {
403
+ super(activatedRoute);
404
+ this.sessionInfoData = sessionInfoData;
405
+ }
406
+ ```
407
+
408
+ After:
409
+
410
+ ```ts
411
+ sessionInfoData = inject(SessionInfoWithApplicationService);
412
+
413
+ private readonly dictionaryService = inject(SdkDictionaryService);
414
+ ```
415
+
416
+ Most of the time, you can remove `activatedRoute` because it was used as a constructor parameter for `AbstractPageComponent`.
417
+
418
+ ### Migrate to self-closing tags
419
+
420
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
421
+
422
+ ```bash
423
+ npm run build
424
+ ```
425
+
426
+ Fix remaining errors and warnings, then run the following command:
427
+
428
+ ```bash
429
+ ng generate @angular/core:self-closing-tag
430
+ ```
431
+
432
+ ✔ Which directory do you want to migrate? **./**
433
+
434
+ ### Migrate to standalone
435
+
436
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
437
+
438
+ **TIP: Check [Migrating from NgModules](COMPONENTS.md#migrating_from_ngmodules) for more information before running following commands.**
439
+
440
+ ```bash
441
+ npm run build
442
+ ```
443
+
444
+ Fix remaining errors and warnings, then run the following command:
445
+
446
+ ```bash
447
+ ng generate @angular/core:standalone
448
+ ```
449
+
450
+ Choose **"Convert all components, directives and pipes to standalone"** then
451
+
452
+ ✔ Which directory do you want to migrate? **./**
453
+
454
+ ```bash
455
+ npm run build
456
+ ```
457
+
458
+ ```bash
459
+ ng generate @angular/core:standalone
460
+ ```
461
+
462
+ Choose **"Remove unnecessary NgModule classes"** then
463
+
464
+ ✔ Which directory do you want to migrate? **./**
465
+
466
+ ```bash
467
+ npm run build
468
+ ```
469
+
470
+ ```bash
471
+ ng generate @angular/core:standalone
472
+ ```
473
+
474
+ Choose **"Bootstrap the application using standalone APIs"** then
475
+
476
+ ✔ Which directory do you want to migrate? **./**
477
+
478
+ #### Now check and fix manually the migration of your `app.component.ts` to standalone
479
+
480
+ It should look like that:
481
+
482
+ ```diff
483
+ @Component({
484
+ selector: 'app-root',
485
+ templateUrl: './app.component.html',
486
+ - // eslint-disable-next-line @angular-eslint/prefer-standalone
487
+ - standalone: false
488
+ + imports: [
489
+ + RouterOutlet,
490
+ + NgHttpLoaderComponent,
491
+ + RedirectComponent,
492
+ + ]
493
+ })
494
+ ```
495
+
496
+ Create a file under `src/app` called `app.routes.ts` with the following content:
497
+
498
+ ```ts
499
+ export const routes: Routes = [
500
+ // Your routes found in app-routing.module.ts go here
501
+ ];
502
+ ```
503
+
504
+ Create a file under `src/app` called `app.config.ts` with the following content:
505
+
506
+ ```ts
507
+ export const appConfig: ApplicationConfig = {
508
+ providers: [
509
+ provideZoneChangeDetection(),
510
+ providePrestationsNgCore(),
511
+ provideRouter(routes),
512
+ META_SERVICE_PROVIDER,
513
+ provideHttpClient(
514
+ withInterceptors([pendingRequestsInterceptor$]),
515
+ withXsrfConfiguration({
516
+ cookieName: 'XSRF-TOKEN',
517
+ headerName: 'X-XSRF-TOKEN',
518
+ }),
519
+ ),
520
+ ],
521
+ };
522
+ ```
523
+
524
+ Now replace the content of `main.ts` with the following content:
525
+
526
+ ```ts
527
+ import { bootstrapApplication } from '@angular/platform-browser';
528
+
529
+ import { AppComponent } from './app/app.component';
530
+ import { appConfig } from './app/app.config';
531
+
532
+ bootstrapApplication(AppComponent, appConfig).catch((err) =>
533
+ console.error(err),
534
+ );
535
+ ```
536
+
537
+ Clean up your project by removing `app-routing.module.ts` and `app.module.ts` files if it has not already been done.
538
+
539
+ #### Migration to lazy-loaded routes when working with large applications
540
+
541
+ You may have some other modules that are maybe lazy-loaded. Just be sure that all your components are standalone before continuing.
542
+
543
+ > You can run the following command to convert eagerly loaded component routes to lazy loaded routes. This allows the build process to split the production bundle into smaller chunks, to avoid a big JS bundle that includes all routes, which negatively affects initial page load of an application.
544
+
545
+ > This migration will also collect information about all the components declared in NgModules and output the list of routes that use them (including corresponding location of the file). Consider making those components standalone.
546
+
547
+ ```bash
548
+ ng generate @angular/core:route-lazy-loading
549
+ ```
550
+
551
+ Here is an example when doing it manually because schematic above does not work as intended:
552
+
553
+ Your `app.routing.ts` file should look something like this:
554
+
555
+ ```ts
556
+ const routes: Routes = [
557
+ {
558
+ path: '',
559
+ component: PageWrapperComponent,
560
+ data: { root: true },
561
+ children: [
562
+ {
563
+ path: 'demandes',
564
+ loadChildren: () =>
565
+ import('./modules/demandes/demandes.module').then(
566
+ (m) => m.DemandesModule,
567
+ ),
568
+ },
569
+ { path: '404', component: FoehnNotfoundComponent },
570
+ { path: '', redirectTo: 'demandes', pathMatch: 'full' },
571
+ { path: '**', redirectTo: '/404', pathMatch: 'full' },
572
+ ],
573
+ },
574
+ ];
575
+ ```
576
+
577
+ Now, navigate to `DemandesModule`, at the same file level as `demandes.module.ts`, you create a `demandes.routes.ts` file with the following content:
578
+
579
+ ```ts
580
+ export const demandes_routes: Routes = [
581
+ // Your routes found in demandes.module.ts go here
582
+ ];
583
+ ```
584
+
585
+ Then replace `loadChildren: () => import('./modules/demandes/demandes.module').then(m => m.DemandesModule)` by `loadChildren: () => import('./modules/demandes/demandes.routes').then(m => m.demandes_routes)`.
586
+
587
+ Now you can safely delete `demandes.module.ts` as all components are `standalone` now.
588
+
589
+ ---
590
+
591
+ You can `lazy-load` components as well, for example:
592
+
593
+ ```diff
594
+ export const demandes_routes: Routes = [
595
+ - { path: 'list', component: DemandeListPageComponent },
596
+ + { path: 'list', loadComponent: () => import('./demande-list-page/demande-list-page.component').then(m => m.DemandeListPageComponent) },
597
+ ];
598
+ ```
599
+
600
+ ### Migrate from NgClass to class bindings
601
+
602
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
603
+
604
+ ```bash
605
+ npm run build
606
+ ```
607
+
608
+ Fix remaining errors and warnings, then run the following command:
609
+
610
+ ```bash
611
+ ng generate @angular/core:ngclass-to-class
612
+ ```
613
+
614
+ ✔ Which directory do you want to migrate? **./**
615
+
616
+ ✔ Should the migration also migrate space-separated keys? **Yes**
617
+
618
+ - Check every file that has changed and fix what is broken.
619
+ - Check remaining `ngClass` usage in your project and replace it with class bindings.
620
+
621
+ **Be aware that `[class]` replaces all element classes. When you have static classes, concatenate them with dynamic ones.**
622
+
623
+ Replacement example:
624
+
625
+ Before:
626
+
627
+ ```html
628
+ [ngClass]="isLabelSrOnly ? 'visually-hidden' : 'vd-p'"
629
+ ```
630
+
631
+ After:
632
+
633
+ ```html
634
+ [class.visually-hidden]="isLabelSrOnly()" [class.vd-p]="!isLabelSrOnly()"
635
+ ```
636
+
637
+ ---
638
+
639
+ Before:
640
+
641
+ ```html
642
+ class="modal" [ngClass]="modalSize"
643
+ ```
644
+
645
+ After:
646
+
647
+ ```html
648
+ [class]="'modal ' + modalSize()"
649
+ ```
650
+
651
+ ---
652
+
653
+ Before:
654
+
655
+ ```html
656
+ class="form-label" [ngClass]="isLabelSrOnly() ? 'visually-hidden' :
657
+ labelStyleModifier()"
658
+ ```
659
+
660
+ After:
661
+
662
+ ```html
663
+ [class]="'form-label ' + (isLabelSrOnly() ? 'visually-hidden' :
664
+ (labelStyleModifier() ?? ''))"
665
+ ```
666
+
667
+ ### Migrate from NgStyle to style bindings
668
+
669
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
670
+
671
+ ```bash
672
+ npm run build
673
+ ```
674
+
675
+ Fix remaining errors and warnings, then run the following command:
676
+
677
+ ```bash
678
+ ng generate @angular/core:ngstyle-to-style
679
+ ```
680
+
681
+ ✔ Which directory do you want to migrate? **./**
682
+
683
+ ✔ Should the migration also migrate object references? **No**
684
+
685
+ - Check every file that has changed and fix what is broken.
686
+ - Check remaining `ngStyle` usage in your project and replace it with style bindings.
687
+
688
+ ### Convert CommonModule usage to standalone imports
689
+
690
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
691
+
692
+ ```bash
693
+ npm run build
694
+ ```
695
+
696
+ Fix remaining errors and warnings, then run the following command:
697
+
698
+ ```bash
699
+ ng generate @angular/core:common-to-standalone
700
+ ```
701
+
702
+ ✔ Which directory do you want to migrate? **./**
703
+
704
+ - Check every file that has changed and fix what is broken.
705
+
706
+ ### Migrate @HostListener
707
+
708
+ Search for `@HostListener` in your project and replace it with the `host` object in the `@Component` or `@Directive` decorator.
709
+
710
+ For example, when using `@HostListener` in a directive:
711
+
712
+ Before:
713
+
714
+ ```ts
715
+ @Directive({
716
+ selector: '[numberCurrencyFormatter]',
717
+ })
718
+ export class NumberCurrencyFormatterDirective implements OnInit {
719
+ @HostListener('blur', ['$event.target.value'])
720
+ onBlur(value: string): void {
721
+ this.hasFocus = false;
722
+ this.setFormatting(value);
723
+ }
724
+ }
725
+ ```
726
+
727
+ After:
728
+
729
+ ```ts
730
+ @Directive({
731
+ selector: '[numberCurrencyFormatter]',
732
+ host: {
733
+ '(blur)': 'onBlur($event)',
734
+ },
735
+ })
736
+ export class NumberCurrencyFormatterDirective implements OnInit {
737
+ onBlur(event: FocusEvent): void {
738
+ this.hasFocus = false;
739
+ const value = (event.target as HTMLInputElement).value;
740
+ this.setFormatting(value);
741
+ }
742
+ }
743
+ ```
744
+
745
+ ### Migrate to signal queries
746
+
747
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
748
+
749
+ ```bash
750
+ npm run build
751
+ ```
752
+
753
+ Fix remaining errors and warnings, then run the following command:
754
+
755
+ ```bash
756
+ ng generate @angular/core:signal-queries-migration --insert-todos
757
+ ```
758
+
759
+ ✔ Which directory do you want to migrate? **./**
760
+
761
+ ✔ Do you want to migrate as much as possible, even if it may break your build? **No**
762
+
763
+ - Check every file that has changed and fix what is broken.
764
+ - Check every remaining `@ViewChild`, `@ViewChildren`, `@ContentChild`, and `@ContentChildren` usage in your project and replace them with signal queries.
765
+ - Check every **TODO** comment added by the migration and fix them.
766
+
767
+ ### Migrate to output functions
768
+
769
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
770
+
771
+ ```bash
772
+ npm run build
773
+ ```
774
+
775
+ Fix remaining errors and warnings, then run the following command:
776
+
777
+ ```bash
778
+ ng generate @angular/core:output-migration
779
+ ```
780
+
781
+ ✔ Which directory do you want to migrate? **./**
782
+
783
+ Check every remaining `@Output` usage in your project and replace them with output function.
784
+
785
+ When needing to subscribe to an output, you can use `outputToObservable` from `@angular/core/rxjs-interop`.
786
+
787
+ ### Migrate to input signals
788
+
789
+ Check our [Angular signals guide](ANGULAR_SIGNALS.md) for more information about signals and input signals.
790
+
791
+ **WARNING: The following command will work only when your application compiles without errors. If you have errors, fix them before continuing.**
792
+
793
+ ```bash
794
+ npm run build
795
+ ```
796
+
797
+ Fix remaining errors and warnings, then run the following command:
798
+
799
+ ```bash
800
+ ng generate @angular/core:signal-input-migration --insert-todos
801
+ ```
802
+
803
+ ✔ Which directory do you want to migrate? **./**
804
+
805
+ ✔ Do you want to migrate as much as possible, even if it may break your build? **No**
806
+
807
+ - Check every file that has changed and fix what is broken.
808
+ - Check every remaining `@Input` usage in your project and replace them with input signals.
809
+ - Check every **TODO** comment added by the migration and fix them.
810
+
811
+ ### Migrate to vitest
812
+
813
+ [Angular migration guide: migrating to Vitest](https://angular.dev/guide/testing/migrating-to-vitest)
814
+
815
+ ```bash
816
+ npm install --save-dev vitest jsdom
817
+ ```
818
+
819
+ In your `angular.json` file, remove `"zone.js/dist/zone-error"` from `"allowedCommonJsDependencies"`
820
+
821
+ then find the `test` target for your project and change the `builder` to `@angular/build:unit-test` and add following options:
822
+
823
+ ```json
824
+ {
825
+ "projects": {
826
+ "your-project-name": {
827
+ "architect": {
828
+ "test": {
829
+ "builder": "@angular/build:unit-test",
830
+ "options": {
831
+ "buildTarget": "your-project-name:build"
832
+ }
833
+ }
834
+ }
835
+ }
836
+ }
837
+ }
838
+ ```
839
+
840
+ In your package.json file, add the following scripts:
841
+
842
+ ```diff
843
+ {
844
+ "scripts": {
845
+ - "test": "ng test",
846
+ + "test": "ng test --watch=false",
847
+ }
848
+ }
849
+ ```
850
+
851
+ In your `tsconfig.spec.json` file, remove from `types` `jasmine` and `node` and add `vitest/globals`:
852
+
853
+ ```diff
854
+ {
855
+ "compilerOptions": {
856
+ "types": [
857
+ - "jasmine",
858
+ - "node"
859
+ + "vitest/globals"
860
+ ]
861
+ }
862
+ }
863
+ ```
864
+
865
+ In your `.gitignore` file, add the following line to ignore the `screenshot` folder:
866
+
867
+ ```
868
+ __screenshots__
869
+ ```
870
+
871
+ Now remove karma and jasmine dependencies
872
+
873
+ ```bash
874
+ npm uninstall jasmine jasmine-core jasmine-spec-reporter
875
+ npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter
876
+ npm uninstall puppeteer
877
+ npm uninstall @types/jasmine @types/jasminewd2
878
+ rm karma.conf.js
879
+ ```
880
+
881
+ - search for `karma.conf.js` in your project and remove all references to it.
882
+ - search for `protractor.conf.js` in your project and remove them if they still exist.
883
+
884
+ Then run following command to migrate from jasmine to vitest:
885
+
886
+ ```bash
887
+ ng g @schematics/angular:refactor-jasmine-vitest
888
+ ```
889
+
890
+ Now you can run your tests with `npm test` and check that everything is up and running.
891
+
892
+ In your tests, replace `waitForAsync(() => ...)` by `async(...)` and add `await` before `TestBed.configureTestingModule`.
893
+ For example, replace this:
894
+
895
+ Before:
896
+
897
+ ```ts
898
+ import {
899
+ provideHttpClient,
900
+ withInterceptorsFromDi,
901
+ } from '@angular/common/http';
902
+ import { provideHttpClientTesting } from '@angular/common/http/testing';
903
+ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
904
+ import { RouterModule } from '@angular/router';
905
+
906
+ import { AppComponent } from './app.component';
907
+ import { AppModule } from './app.module';
908
+
909
+ describe('AppComponent', () => {
910
+ let component: AppComponent;
911
+ let fixture: ComponentFixture<AppComponent>;
912
+
913
+ beforeEach(waitForAsync(() => {
914
+ TestBed.configureTestingModule({
915
+ imports: [AppModule, RouterModule.forRoot([])],
916
+ providers: [
917
+ provideHttpClient(withInterceptorsFromDi()),
918
+ provideHttpClientTesting(),
919
+ ],
920
+ }).compileComponents();
921
+ }));
922
+
923
+ beforeEach(() => {
924
+ fixture = TestBed.createComponent(AppComponent);
925
+ component = fixture.componentInstance;
926
+ fixture.detectChanges();
927
+ });
928
+
929
+ it('should create', () => {
930
+ expect(component).toBeTruthy();
931
+ });
932
+ });
933
+ ```
934
+
935
+ After:
936
+
937
+ ```ts
938
+ import {
939
+ provideHttpClient,
940
+ withInterceptorsFromDi,
941
+ } from '@angular/common/http';
942
+ import { provideHttpClientTesting } from '@angular/common/http/testing';
943
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
944
+ import { provideRouter } from '@angular/router';
945
+
946
+ import { AppComponent } from './app.component';
947
+
948
+ describe('AppComponent', () => {
949
+ let component: AppComponent;
950
+ let fixture: ComponentFixture<AppComponent>;
951
+
952
+ beforeEach(async () => {
953
+ await TestBed.configureTestingModule({
954
+ imports: [AppComponent],
955
+ providers: [
956
+ provideHttpClient(withInterceptorsFromDi()),
957
+ provideHttpClientTesting(),
958
+ provideRouter([]),
959
+ ],
960
+ }).compileComponents();
961
+
962
+ fixture = TestBed.createComponent(AppComponent);
963
+ component = fixture.componentInstance;
964
+ await fixture.whenStable();
965
+ });
966
+
967
+ it('should create', () => {
968
+ expect(component).toBeTruthy();
969
+ });
970
+ });
971
+ ```
972
+
973
+ ### Migrate to eslint flat configuration
974
+
975
+ ```bash
976
+ npm uninstall @angular-eslint/builder @angular-eslint/eslint-plugin @angular-eslint/eslint-plugin-template @angular-eslint/template-parser
977
+ npm uninstall @typescript-eslint/eslint-plugin @typescript-eslint/parser @typescript-eslint/types @typescript-eslint/utils
978
+ npm uninstall eslint-import-resolver-typescript eslint-plugin-cypress eslint-plugin-import eslint-plugin-jasmine eslint-plugin-rxjs eslint-plugin-rxjs-angular eslint-plugin-simple-npm eslint-plugin-simple-import-sort
979
+ npm uninstall eslint
980
+
981
+ npm install --save-dev eslint@^9 angular-eslint@21 typescript-eslint@^8
982
+ npm install --save-dev @typescript-eslint/parser@8 @smarttools/eslint-plugin-rxjs@1
983
+ npm install --save-dev eslint-plugin-simple-import-sort@13
984
+ npm install --save-dev eslint-config-prettier@10
985
+ npm install --save-dev eslint-plugin-import-x@4
986
+ npm install --save-dev prettier
987
+ ```
988
+
989
+ In your `front` folder, add a `.prettierrc` file with the following content:
990
+
991
+ ```json
992
+ {
993
+ "printWidth": 100,
994
+ "singleQuote": true,
995
+ "arrowParens": "always",
996
+ "trailingComma": "all",
997
+ "htmlWhitespaceSensitivity": "css",
998
+ "overrides": [
999
+ {
1000
+ "files": ["*.html"],
1001
+ "options": {
1002
+ "parser": "angular"
1003
+ }
1004
+ }
1005
+ ]
1006
+ }
1007
+ ```
1008
+
1009
+ Add a `.prettierignore` file with the following content:
1010
+
1011
+ ```text
1012
+ node_modules
1013
+ .git
1014
+ target
1015
+ package.json
1016
+ package-lock.json
1017
+ dist
1018
+ coverage
1019
+ .angular
1020
+ ```
1021
+
1022
+ Remove your old `.eslintrc.json` file in your `front` folder:
1023
+
1024
+ ```bash
1025
+ rm .eslintrc.json
1026
+ ```
1027
+
1028
+ - search for `.eslintrc.json` in your project and remove them.
1029
+ - add a new `eslint.config.mjs` file with the following content:
1030
+
1031
+ ```ts
1032
+ // @ts-check
1033
+ import eslint from '@eslint/js';
1034
+ import tseslint from 'typescript-eslint';
1035
+ import angular from 'angular-eslint';
1036
+ import rxjs from '@smarttools/eslint-plugin-rxjs';
1037
+ import simpleImportSort from 'eslint-plugin-simple-import-sort';
1038
+ import prettier from 'eslint-config-prettier';
1039
+ import importX from 'eslint-plugin-import-x';
1040
+
1041
+ export default tseslint.config(
1042
+ {
1043
+ ignores: ['**/coverage/**', '**/dist/**'],
1044
+ },
1045
+
1046
+ // ─── TypeScript ────────────────────────────────────────────────────────────
1047
+ {
1048
+ files: ['**/*.ts'],
1049
+ extends: [
1050
+ eslint.configs.recommended,
1051
+ ...tseslint.configs.recommended, // covers: no-explicit-any, ban-ts-comment,
1052
+ // no-unused-expressions, no-var, no-unused-vars…
1053
+ ...tseslint.configs.stylistic, // covers: array-type, consistent-type-assertions,
1054
+ // dot-notation, no-inferrable-types (default options),
1055
+ // no-empty-function (error), prefer-for-of,
1056
+ // prefer-function-type…
1057
+ ...angular.configs.tsRecommended, // covers: prefer-inject, no-empty-lifecycle-method,
1058
+ // use-lifecycle-interface…
1059
+ ],
1060
+ languageOptions: {
1061
+ parserOptions: {
1062
+ projectService: true,
1063
+ tsconfigRootDir: import.meta.dirname,
1064
+ },
1065
+ },
1066
+ plugins: {
1067
+ rxjs,
1068
+ 'simple-import-sort': simpleImportSort,
1069
+ 'import-x': importX,
1070
+ },
1071
+ processor: angular.processInlineTemplates,
1072
+ rules: {
1073
+ // ── @angular-eslint ──────────────────────────────────────────────
1074
+ '@angular-eslint/component-selector': [
1075
+ 'error',
1076
+ {
1077
+ type: 'element',
1078
+ prefix: 'app',
1079
+ style: 'kebab-case',
1080
+ },
1081
+ ],
1082
+ '@angular-eslint/directive-selector': [
1083
+ 'error',
1084
+ {
1085
+ type: 'attribute',
1086
+ prefix: 'app',
1087
+ style: 'camelCase',
1088
+ },
1089
+ ],
1090
+
1091
+ // ── @typescript-eslint ───────────────────────────────────────────
1092
+ '@typescript-eslint/explicit-function-return-type': [
1093
+ 'error',
1094
+ {
1095
+ allowExpressions: true,
1096
+ allowTypedFunctionExpressions: true,
1097
+ allowHigherOrderFunctions: true,
1098
+ allowDirectConstAssertionInArrowFunctions: true,
1099
+ },
1100
+ ],
1101
+ '@typescript-eslint/explicit-member-accessibility': [
1102
+ 'error',
1103
+ { accessibility: 'no-public' },
1104
+ ],
1105
+ '@typescript-eslint/member-delimiter-style': 'error',
1106
+ '@typescript-eslint/member-ordering': 'error',
1107
+ '@typescript-eslint/no-base-to-string': 'warn',
1108
+ // stylistic enables no-inferrable-types with default options (ignoreParameters: false);
1109
+ // override here to allow typed parameters explicitly
1110
+ '@typescript-eslint/no-inferrable-types': [
1111
+ 'error',
1112
+ { ignoreParameters: true },
1113
+ ],
1114
+ // stylistic sets no-empty-function to error; downgrade to warn
1115
+ '@typescript-eslint/no-empty-function': 'warn',
1116
+ '@typescript-eslint/prefer-includes': 'warn',
1117
+ '@typescript-eslint/return-await': ['error', 'never'],
1118
+ '@typescript-eslint/typedef': ['error', { parameter: true }],
1119
+ '@typescript-eslint/unified-signatures': 'error',
1120
+ // off here — @typescript-eslint/no-shadow handles it correctly for TS
1121
+ 'no-shadow': 'off',
1122
+ '@typescript-eslint/no-shadow': ['error', { hoist: 'all' }],
1123
+ '@typescript-eslint/no-unused-vars': [
1124
+ 'error',
1125
+ {
1126
+ varsIgnorePattern: '^_',
1127
+ argsIgnorePattern: '^_',
1128
+ caughtErrorsIgnorePattern: '^_',
1129
+ },
1130
+ ],
1131
+
1132
+ // ── eslint core ──────────────────────────────────────────────────
1133
+ 'arrow-body-style': 'error',
1134
+ 'arrow-parens': ['error', 'as-needed'],
1135
+ curly: 'error',
1136
+ eqeqeq: ['error', 'always'],
1137
+ 'guard-for-in': 'error',
1138
+ 'no-bitwise': 'error',
1139
+ 'no-caller': 'error',
1140
+ 'no-console': ['error', { allow: ['log', 'warn', 'error'] }],
1141
+ 'no-eval': 'error',
1142
+ 'no-extra-boolean-cast': 'off',
1143
+ 'no-multiple-empty-lines': 'error',
1144
+ 'no-new-wrappers': 'error',
1145
+ 'no-restricted-imports': [
1146
+ 'error',
1147
+ { paths: ['rxjs/Rx', 'primeng/primeng', 'primeng'] },
1148
+ ],
1149
+ 'no-return-assign': 'error',
1150
+ 'no-throw-literal': 'error',
1151
+ 'no-undef-init': 'error',
1152
+ 'no-useless-concat': 'error',
1153
+ 'object-shorthand': ['error', 'always', { avoidQuotes: true }],
1154
+ 'one-var': ['error', 'never'],
1155
+ 'prefer-arrow-callback': 'error',
1156
+ 'prefer-template': 'error',
1157
+ radix: 'error',
1158
+
1159
+ // ── rxjs ─────────────────────────────────────────────────────────
1160
+ 'rxjs/no-implicit-any-catch': ['error', { allowExplicitAny: true }],
1161
+ 'rxjs/no-sharereplay': 'off',
1162
+
1163
+ // ── import-x ─────────────────────────────────────────────────────
1164
+ 'import-x/no-cycle': [
1165
+ 'warn',
1166
+ { maxDepth: 3, ignoreExternal: true },
1167
+ ],
1168
+ 'import-x/no-deprecated': 'warn',
1169
+ 'import-x/first': 'error',
1170
+ 'import-x/newline-after-import': 'error',
1171
+ 'import-x/no-duplicates': 'error',
1172
+
1173
+ // ── simple-import-sort ───────────────────────────────────────────
1174
+ 'simple-import-sort/imports': 'error',
1175
+ 'simple-import-sort/exports': 'error',
1176
+ },
1177
+ },
1178
+
1179
+ // ─── HTML templates ────────────────────────────────────────────────────────
1180
+ {
1181
+ files: ['**/*.html'],
1182
+ extends: [
1183
+ ...angular.configs.templateRecommended,
1184
+ ...angular.configs.templateAccessibility,
1185
+ ],
1186
+ rules: {
1187
+ '@angular-eslint/template/no-negated-async': 'off',
1188
+ '@angular-eslint/template/button-has-type': 'error',
1189
+ },
1190
+ },
1191
+
1192
+ // ─── Prettier (must be last — disables formatting rules) ───────────────────
1193
+ prettier,
1194
+ );
1195
+ ```
1196
+
1197
+ #### Delete `typings.d.ts`
1198
+
1199
+ In `front/src/`, remove the file `typings.d.ts` if it is still there.
1200
+
1201
+ #### Update your tsconfig\*.json files
1202
+
1203
+ - Replace your `tsconfig.json` content with:
1204
+
1205
+ ```json
1206
+ {
1207
+ "compileOnSave": false,
1208
+ "compilerOptions": {
1209
+ "outDir": "./dist/out-tsc",
1210
+ "noImplicitOverride": true,
1211
+ "noImplicitReturns": true,
1212
+ "noFallthroughCasesInSwitch": true,
1213
+ "skipLibCheck": true,
1214
+ "esModuleInterop": true,
1215
+ "sourceMap": true,
1216
+ "declaration": false,
1217
+ "experimentalDecorators": true,
1218
+ "useDefineForClassFields": false,
1219
+ "moduleResolution": "bundler",
1220
+ "importHelpers": true,
1221
+ "isolatedModules": true,
1222
+ "target": "ES2022",
1223
+ "module": "ES2022",
1224
+ "paths": {
1225
+ "@dsivd/prestations-ng": ["./node_modules/@dsivd/prestations-ng"]
1226
+ }
1227
+ },
1228
+ "angularCompilerOptions": {
1229
+ "enableI18nLegacyMessageIdFormat": false,
1230
+ "strictInjectionParameters": true,
1231
+ "strictInputAccessModifiers": true,
1232
+ "strictTemplates": true,
1233
+ "strictStandalone": true
1234
+ },
1235
+ "files": [],
1236
+ "references": [
1237
+ {
1238
+ "path": "./tsconfig.app.json"
1239
+ },
1240
+ {
1241
+ "path": "./tsconfig.spec.json"
1242
+ }
1243
+ ]
1244
+ }
1245
+ ```
1246
+
1247
+ - Replace your `tsconfig.app.json` content with:
1248
+
1249
+ ```json
1250
+ {
1251
+ "extends": "./tsconfig.json",
1252
+ "compilerOptions": {
1253
+ "outDir": "../out-tsc/app",
1254
+ "types": []
1255
+ },
1256
+ "include": ["src/**/*.ts"],
1257
+ "exclude": ["src/**/*.spec.ts"]
1258
+ }
1259
+ ```
1260
+
1261
+ - Update your package.json, add the following script to run eslint:
1262
+
1263
+ ```diff
1264
+ "scripts": {
1265
+ - "lint": "ng lint",
1266
+ + "lint": "ng lint --fix=true",
1267
+ + "format:prettier": "prettier --config ./.prettierrc --write \"{,!(.scannerwork|target|coverage|node_modules|.angular)/**/}*{.ts,.js,.jsx,.json,.css,.scss,.md,.html}\"",
1268
+ }
1269
+ ```
1270
+
1271
+ ### Husky pre-commit hook
1272
+
1273
+ ```bash
1274
+ npm install --save-dev husky lint-staged
1275
+ npx husky init
1276
+ echo "cd front" > .husky/pre-commit
1277
+ echo "npx lint-staged" >> .husky/pre-commit
1278
+ chmod +x .husky/pre-commit
1279
+ mv ./.husky ../.husky
1280
+ git add -A
1281
+ ```
1282
+
1283
+ - You can manually run formatting commands as well:
1284
+
1285
+ ```bash
1286
+ npm run format:prettier
1287
+ npm run lint
1288
+ ```
1289
+
1290
+ Fix your `meta.service.ts` by replacing:
1291
+
1292
+ Before:
1293
+
1294
+ ```ts
1295
+ export const metaServiceFactory =
1296
+ (metaService: MetaService): Function =>
1297
+ () =>
1298
+ lastValueFrom(metaService.load());
1299
+
1300
+ export const META_SERVICE_PROVIDER = provideAppInitializer(() => {
1301
+ const initializerFn = metaServiceFactory(inject(MetaService));
1302
+ return initializerFn();
1303
+ });
1304
+ ```
1305
+
1306
+ After:
1307
+
1308
+ ```ts
1309
+ export const META_SERVICE_PROVIDER = provideAppInitializer(() => {
1310
+ const metaService = inject(MetaService);
1311
+ return lastValueFrom(metaService.load());
1312
+ });
1313
+ ```
1314
+
1315
+ Run again until no more errors:
1316
+
1317
+ ```bash
1318
+ npm run lint
1319
+ ```
1320
+
1321
+ Then:
1322
+
1323
+ ```bash
1324
+ npm run format:prettier
1325
+ ```
1326
+
1327
+ ### Browsers list update
1328
+
1329
+ Run update schematic:
1330
+
1331
+ ```bash
1332
+ ng g @dsivd/prestations-ng:replace-browsers-list
1333
+ ```
1334
+
1335
+ ### Remove deprecated packages
1336
+
1337
+ ```bash
1338
+ npm uninstall @angular/platform-browser-dynamic
1339
+ npm uninstall @angular/animations
1340
+ ```
1341
+
1342
+ ### Configure IntelliJ IDEA to use ESLint and Prettier on save
1343
+
1344
+ - To configure ESLint automatically in the current project, open "_File -> Settings..._" (`Ctrl+Alt+S`) and either:
1345
+ - Search for `ESLint` and check radio box "_Automatic ESLint configuration_" and check checkbox "_Run eslint --fix on save_"
1346
+ - Search for `Prettier` and check radio box "_Automatic Prettier configuration_" and check checkbox "_Run on save_"
1347
+
1348
+ ### Language deprecations
1349
+
1350
+ When using `IntelliJ`, you can run code inspection on your project to fix deprecations manually. Two ways:
1351
+
1352
+ - IntelliJ window menu, go to: `Code > Analyze code > Run inspection by name...` (or **Ctrl+Alt+Maj+i**) and type `Deprecated symbol used`.
1353
+ - Or select your project in your project panel and `right click` on it then `Analyze > Run inspection by name...` (or **Ctrl+Alt+Maj+i**) and type `Deprecated symbol used`.
1354
+
1355
+ ### Last but not least, check if your application is working!!!
1356
+
1357
+ #### Be sure your application gets a fresh start
1358
+
1359
+ ```bash
1360
+ rm -rf .angular/
1361
+ rm -rf node_modules/
1362
+ npm install
1363
+ ```
1364
+
1365
+ #### Build and launch your application
1366
+
1367
+ **NB: You will most probably need to fix some compilation errors such as adding `override` when needed**
1368
+
1369
+ ```bash
1370
+ npm run build
1371
+ npm start
1372
+ ```
1373
+
1374
+ You're good if no errors are remaining!
1375
+ Finally check if your tests run well :
1376
+
1377
+ ```bash
1378
+ npm run test
1379
+ ```
1380
+
1381
+ > 🚨 **Read carefully following [PITFALLS](ANGULAR_SIGNALS.md#common_pitfalls), especially points 4, 6 & 8 and check again your application**
1382
+
1383
+ ### Release your application using `jenkins-libs`
1384
+
1385
+ When your code has been merged/rebased onto `master`, you need to update your application in `jenkins-libs` to use node 22 instead of node 20.
1386
+
1387
+ See [getImtPortailApplications](https://dsigit.etat-de-vaud.ch/outils/git/projects/PEE/repos/jenkins-libs/browse/vars/getImtPortailApplications.groovy)
1388
+
1389
+ ```diff
1390
+ [
1391
+ 'project': 'cybersdk',
1392
+ 'name' : 'skeleton',
1393
+ 'gitUrl' : 'ssh://git@bitbucket.etat-de-vaud.ch/cybsdk/skeleton.git',
1394
+ 'jdk' : '21',
1395
+ - 'node' : '20'
1396
+ + 'node' : '22'
1397
+ ],
1398
+ ```