@silvestv/migration-planificator 3.0.0

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.
Files changed (122) hide show
  1. package/LICENSE +96 -0
  2. package/README.fr.md +359 -0
  3. package/README.md +360 -0
  4. package/SECURITY.md +187 -0
  5. package/dist/client.bundle.js +357 -0
  6. package/dist/src/core/app-analyzer.js +134 -0
  7. package/dist/src/core/ast/matchers/html/html-attribute-matcher.js +86 -0
  8. package/dist/src/core/ast/matchers/html/html-component-matcher.js +40 -0
  9. package/dist/src/core/ast/matchers/html/html-element-matcher.js +54 -0
  10. package/dist/src/core/ast/matchers/html/html-parser.js +58 -0
  11. package/dist/src/core/ast/matchers/html/html-pipe-matcher.js +95 -0
  12. package/dist/src/core/ast/matchers/html/html-text-matcher.js +53 -0
  13. package/dist/src/core/ast/matchers/html/index.js +118 -0
  14. package/dist/src/core/ast/matchers/index.js +377 -0
  15. package/dist/src/core/ast/matchers/ts/collection-matcher.js +51 -0
  16. package/dist/src/core/ast/matchers/ts/context-matcher.js +275 -0
  17. package/dist/src/core/ast/matchers/ts/decorator-matcher.js +465 -0
  18. package/dist/src/core/ast/matchers/ts/expression-matcher.js +237 -0
  19. package/dist/src/core/ast/matchers/ts/file-matcher.js +97 -0
  20. package/dist/src/core/ast/matchers/ts/hierarchy-matcher.js +172 -0
  21. package/dist/src/core/ast/matchers/ts/import-matcher.js +39 -0
  22. package/dist/src/core/ast/matchers/ts/index.js +53 -0
  23. package/dist/src/core/ast/matchers/ts/node-matcher.js +156 -0
  24. package/dist/src/core/ast/matchers/ts/symbol-matcher.js +281 -0
  25. package/dist/src/core/ast/matchers/ts/type-matcher.js +207 -0
  26. package/dist/src/core/ast/matchers/utils/matcher-helpers.js +37 -0
  27. package/dist/src/core/ast/scanner-ast.js +444 -0
  28. package/dist/src/core/project-detector.js +196 -0
  29. package/dist/src/core/project-strategy/index.js +9 -0
  30. package/dist/src/core/project-strategy/nx-strategy.js +130 -0
  31. package/dist/src/core/project-strategy/project-strategy.interface.js +2 -0
  32. package/dist/src/core/project-strategy/standalone-strategy.js +74 -0
  33. package/dist/src/core/project-strategy/strategy-factory.js +15 -0
  34. package/dist/src/core/rules-loader.js +89 -0
  35. package/dist/src/core/scan-reporter.js +316 -0
  36. package/dist/src/core/scanner-delta.js +339 -0
  37. package/dist/src/core/scanner-orchestrator.js +266 -0
  38. package/dist/src/core/scanner-regex.js +298 -0
  39. package/dist/src/core/workload/calculator.js +82 -0
  40. package/dist/src/core/workload/constants.js +15 -0
  41. package/dist/src/core/workload/grouping.js +18 -0
  42. package/dist/src/core/workload/hierarchy-calculator.js +127 -0
  43. package/dist/src/core/workload/index.js +11 -0
  44. package/dist/src/core/workload/metadata.js +20 -0
  45. package/dist/src/core/workload/special-workload.js +101 -0
  46. package/dist/src/core/workload/target-resolver.js +34 -0
  47. package/dist/src/data/angular-migration-rules.json +2337 -0
  48. package/dist/src/data/markdown/angular-migration-17-18.md +408 -0
  49. package/dist/src/data/markdown/angular-migration-18-19.md +600 -0
  50. package/dist/src/data/markdown/angular-migration-19-20.md +521 -0
  51. package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +66 -0
  52. package/dist/src/data/rules/to18/rules-18-obligatoire.json +374 -0
  53. package/dist/src/data/rules/to18/rules-18-optionnelle.json +188 -0
  54. package/dist/src/data/rules/to18/rules-18-recommande.json +218 -0
  55. package/dist/src/data/rules/to19/rules-19-obligatoire.json +348 -0
  56. package/dist/src/data/rules/to19/rules-19-optionnelle.json +223 -0
  57. package/dist/src/data/rules/to19/rules-19-recommande.json +200 -0
  58. package/dist/src/data/rules/to20/rules-20-obligatoire.json +556 -0
  59. package/dist/src/data/rules/to20/rules-20-optionnelle.json +190 -0
  60. package/dist/src/data/rules/to20/rules-20-recommande.json +151 -0
  61. package/dist/src/index.js +161 -0
  62. package/dist/src/models/chip-config.js +45 -0
  63. package/dist/src/models/interfaces/app-details.interface.js +2 -0
  64. package/dist/src/models/interfaces/ast-interfaces.js +5 -0
  65. package/dist/src/models/interfaces/ast-pattern.interface.js +2 -0
  66. package/dist/src/models/interfaces/client-interfaces.js +6 -0
  67. package/dist/src/models/interfaces/detection-stats.interface.js +2 -0
  68. package/dist/src/models/interfaces/html-match.interface.js +2 -0
  69. package/dist/src/models/interfaces/html-report-data.interface.js +2 -0
  70. package/dist/src/models/interfaces/lib-details.interface.js +2 -0
  71. package/dist/src/models/interfaces/migration-rules.interface.js +2 -0
  72. package/dist/src/models/interfaces/parsed-args.interface.js +2 -0
  73. package/dist/src/models/interfaces/project-info.interface.js +2 -0
  74. package/dist/src/models/interfaces/project-overview-data.interface.js +2 -0
  75. package/dist/src/models/interfaces/rule-match.interface.js +2 -0
  76. package/dist/src/models/interfaces/rule.interface.js +2 -0
  77. package/dist/src/models/interfaces/rules-by-priority.interface.js +2 -0
  78. package/dist/src/models/interfaces/scanner-comparison.interface.js +2 -0
  79. package/dist/src/models/interfaces/special-workload.interface.js +2 -0
  80. package/dist/src/models/interfaces/workload-report.interface.js +2 -0
  81. package/dist/src/models/types/build-block-blob.type.js +2 -0
  82. package/dist/src/models/types/migration-version.type.js +2 -0
  83. package/dist/src/models/types/project-type.type.js +2 -0
  84. package/dist/src/models/types/risk-level.type.js +2 -0
  85. package/dist/src/models/types/rule-category.type.js +2 -0
  86. package/dist/src/models/types/rule-priority.type.js +2 -0
  87. package/dist/src/models/types/rule-workload-type.type.js +2 -0
  88. package/dist/src/templates/landing/applications-analyzed.template.js +18 -0
  89. package/dist/src/templates/landing/card-app-info.template.js +63 -0
  90. package/dist/src/templates/landing/card-lib-info.template.js +67 -0
  91. package/dist/src/templates/landing/libs-analyzed.template.js +22 -0
  92. package/dist/src/templates/landing/nx-summary.template.js +115 -0
  93. package/dist/src/templates/landing/project-overview.template.js +27 -0
  94. package/dist/src/templates/page/index-page.template.js +95 -0
  95. package/dist/src/templates/page/main.template.js +83 -0
  96. package/dist/src/templates/page/migration-guide.template.js +175 -0
  97. package/dist/src/templates/page/workload-report.template.js +53 -0
  98. package/dist/src/templates/workload/dashboard.template.js +184 -0
  99. package/dist/src/templates/workload/filters-panel.template.js +215 -0
  100. package/dist/src/templates/workload/guide-rule-card.template.js +107 -0
  101. package/dist/src/templates/workload/hierarchy-nx.template.js +104 -0
  102. package/dist/src/templates/workload/hierarchy-shared.js +163 -0
  103. package/dist/src/templates/workload/hierarchy-standalone.template.js +36 -0
  104. package/dist/src/templates/workload/hierarchy.template.js +35 -0
  105. package/dist/src/templates/workload/rule-modal.template.js +280 -0
  106. package/dist/src/utils/core/args-parser.js +123 -0
  107. package/dist/src/utils/core/array-helpers.js +18 -0
  108. package/dist/src/utils/core/ast-helpers.js +99 -0
  109. package/dist/src/utils/core/file-helpers.js +109 -0
  110. package/dist/src/utils/core/html-helpers.js +36 -0
  111. package/dist/src/utils/core/index.js +28 -0
  112. package/dist/src/utils/core/logger.js +38 -0
  113. package/dist/src/utils/core/rule-helpers.js +15 -0
  114. package/dist/src/utils/core/workload-formatter.js +6 -0
  115. package/dist/src/utils/shared/array-helpers.js +25 -0
  116. package/dist/src/utils/shared/date-helpers.js +109 -0
  117. package/dist/src/utils/shared/html-helpers.js +37 -0
  118. package/dist/src/utils/shared/index.js +25 -0
  119. package/dist/src/utils/shared/rule-helpers.js +20 -0
  120. package/dist/src/utils/shared/time-formatters.js +76 -0
  121. package/dist/styles.css +2 -0
  122. package/package.json +107 -0
@@ -0,0 +1,556 @@
1
+ [
2
+ {
3
+ "key": "angular_20_environment",
4
+ "summary": "Mise à jour environnement Angular 20 (Node 22.12.0+, TypeScript 5.8+)",
5
+ "description": "Angular 20 requiert Node.js 22.12.0 ou supérieur et TypeScript 5.8+. Cette version active par défaut le build ESM, introduit le support expérimental de Vitest, et adopte un nouveau guide de style sans suffixes. Les versions de Node 22.0 à 22.10 causent des instabilités.",
6
+ "estimated_time_per_occurrence": 25,
7
+ "onFile": "package.json",
8
+ "fileTypes": ["package.json"],
9
+ "regex": "\"@angular/(core|cli)\"\\s*:\\s*\"(?:(?:\\^|~)?1[0-9](?:\\.\\d+){0,2})\"",
10
+ "category": "environment",
11
+ "auto_fixable": false,
12
+ "migration_command": "npx ng update @angular/core@20 @angular/cli@20",
13
+ "risk_level": "critical",
14
+ "code_description": "// Migration Angular 20\n// Node : >= 22.12.0 (éviter 22.0 → 22.10)\n// TypeScript : ~5.8.0\n// Commande :\n// nvm install 22.12.0\n// npm i typescript@~5.8.0\n// npx ng update @angular/core@20 @angular/cli@20",
15
+ "doc_url": "https://angular.dev/reference/migrations"
16
+ },
17
+ {
18
+ "key": "node_version_22",
19
+ "summary": "Mise à jour Node.js vers 22.12.0+",
20
+ "description": "Angular 20 requiert Node.js 22.12.0 conseillé car cette version inclut des correctifs critiques pour les ESM et la stabilité. ATTENTION: ^20.19.0 || ^22.12.0 || ^24.0.0 Supporté only.",
21
+ "estimated_time_per_occurrence": 15,
22
+ "onFile": "package.json",
23
+ "fileTypes": [
24
+ "package.json"
25
+ ],
26
+ "regex": "\"engines\"\\s*:\\s*\\{[^}]*\"node\"\\s*:\\s*\"(?:(?:[<>=~^]*)(1[0-9]|2[0-1]|22\\.(?:[0-9]|1[0-1])(?:\\.[0-9]+)?))\"",
27
+ "category": "environment",
28
+ "auto_fixable": false,
29
+ "migration_command": null,
30
+ "risk_level": "critical",
31
+ "code_description": "// Vérifier et mettre à jour Node.js\n// Avant: Node v18.x ou v19.x\n// Après: Node v22.12.0+ REQUIS\n// ⚠️ PAS v22.0 à v22.10\n// Commande: node --version",
32
+ "doc_url": "https://angular.dev/reference/migrations"
33
+ },
34
+ {
35
+ "key": "typescript_5_8",
36
+ "summary": "Mise à jour TypeScript vers 5.8+",
37
+ "description": "TypeScript 5.8 est requis pour Angular 20 car il apporte des améliorations importantes pour les inferred types, const type parameters et path mapping nécessaires aux nouvelles fonctionnalités du compilateur Angular.",
38
+ "estimated_time_per_occurrence": 10,
39
+ "onFile": "package.json",
40
+ "fileTypes": [
41
+ "package.json"
42
+ ],
43
+ "regex": "^(?![\\s\\S]*\"typescript\"[\\s\\S]*\"[<>=~^]*(?:5\\.[8-9]|[6-9]\\d*)\\.)[\\s\\S]+",
44
+ "category": "environment",
45
+ "auto_fixable": true,
46
+ "migration_command": "npm install typescript@~5.8.0",
47
+ "risk_level": "medium",
48
+ "code_description": "// package.json\n// Avant: \"typescript\": \"~5.5.0\"\n// Après: \"typescript\": \"~5.8.0\"",
49
+ "doc_url": "https://angular.dev/reference/migrations"
50
+ },
51
+ {
52
+ "key": "afterRender_to_afterEveryRender",
53
+ "summary": "afterRender() → afterEveryRender()",
54
+ "description": "Le renommage de afterRender en afterEveryRender clarifie que ce hook s'exécute après chaque cycle de rendu, pas une seule fois. Ce changement améliore la compréhension du comportement et évite les bugs liés à une mauvaise interprétation de la sémantique.",
55
+ "estimated_time_per_occurrence": 3,
56
+ "onFile": null,
57
+ "fileTypes": [
58
+ "*.ts"
59
+ ],
60
+ "regex": "\\bafterRender\\s*\\(",
61
+ "category": "api",
62
+ "auto_fixable": true,
63
+ "migration_command": null,
64
+ "risk_level": "low",
65
+ "code_description": "// Avant:\nafterRender(() => { });\n\n// Après:\nafterEveryRender(() => { });",
66
+ "astPattern": {
67
+ "nodeType": "CallExpression",
68
+ "functionName": "afterRender",
69
+ "excludeContext": [
70
+ "Comment",
71
+ "StringLiteral"
72
+ ]
73
+ },
74
+ "doc_url": "https://angular.dev/reference/migrations"
75
+ },
76
+ {
77
+ "key": "provideExperimentalCheckNoChanges_to_provideCheckNoChangesConfig",
78
+ "summary": "provideExperimentalCheckNoChangesForDebug() → provideCheckNoChangesConfig()",
79
+ "description": "L'API de vérification des changements sort de sa phase expérimentale et devient stable. La propriété useNgZoneOnStable est supprimée car obsolète avec le mode zoneless. Simplement renommer la fonction provider.",
80
+ "estimated_time_per_occurrence": 3,
81
+ "onFile": null,
82
+ "fileTypes": [
83
+ "*.ts"
84
+ ],
85
+ "regex": "provideExperimentalCheckNoChangesForDebug",
86
+ "category": "api",
87
+ "auto_fixable": true,
88
+ "migration_command": null,
89
+ "risk_level": "low",
90
+ "code_description": "// Avant:\nprovideExperimentalCheckNoChangesForDebug()\n\n// Après:\nprovideCheckNoChangesConfig()\n// Note: useNgZoneOnStable supprimé",
91
+ "astPattern": {
92
+ "nodeType": "CallExpression",
93
+ "functionName": "provideExperimentalCheckNoChangesForDebug",
94
+ "excludeContext": [
95
+ "StringLiteral",
96
+ "Comment"
97
+ ]
98
+ },
99
+ "doc_url": "https://angular.dev/reference/migrations"
100
+ },
101
+ {
102
+ "key": "provideExperimentalZoneless_to_provideZoneless",
103
+ "summary": "provideExperimentalZonelessChangeDetection() → provideZonelessChangeDetection()",
104
+ "description": "Remplace l’API expérimentale `provideExperimentalZonelessChangeDetection` par l’API stable `provideZonelessChangeDetection()` introduite en Angular 20.",
105
+ "estimated_time_per_occurrence": 3,
106
+ "onFile": null,
107
+ "fileTypes": [
108
+ "*.ts"
109
+ ],
110
+ "regex": "\\bprovideExperimentalZonelessChangeDetection\\s*\\(",
111
+ "category": "api",
112
+ "auto_fixable": true,
113
+ "migration_command": null,
114
+ "risk_level": "medium",
115
+ "code_description": "// Avant:\nprovideExperimentalZonelessChangeDetection()\n\n// Après:\nprovideZonelessChangeDetection()",
116
+ "astPattern": {
117
+ "nodeType": "CallExpression",
118
+ "functionName": "provideExperimentalZonelessChangeDetection",
119
+ "excludeContext": [
120
+ "StringLiteral",
121
+ "Comment"
122
+ ]
123
+ },
124
+ "doc_url": "https://angular.dev/api/core/provideZonelessChangeDetection"
125
+ },
126
+ {
127
+ "key": "resource_request_to_params",
128
+ "summary": "resource(): 'request' → 'params'",
129
+ "description": "La propriété 'request' de l'API resource est renommée en 'params' pour mieux refléter son usage comme paramètres de requête réactifs. Le paramètre 'request' dans le loader reste inchangé pour la rétrocompatibilité.",
130
+ "estimated_time_per_occurrence": 3,
131
+ "onFile": null,
132
+ "fileTypes": [
133
+ "*.ts"
134
+ ],
135
+ "regex": "\\bresource\\s*\\(\\s*\\{[^}]*request\\s*:",
136
+ "category": "api",
137
+ "auto_fixable": true,
138
+ "migration_command": null,
139
+ "risk_level": "medium",
140
+ "code_description": "// Avant:\nresource({\n request: () => ({ id: userId() }),\n loader: ({ request }) => fetch(`/users/${request.id}`)\n})\n\n// Après:\nresource({\n params: () => ({ id: userId() }),\n loader: ({ request }) => fetch(`/users/${request.id}`)\n})",
141
+ "astPattern": {
142
+ "nodeType": "CallExpression",
143
+ "functionName": "resource",
144
+ "arguments": {
145
+ "properties": {
146
+ "request": {
147
+ "exists": true
148
+ }
149
+ }
150
+ },
151
+ "excludeContext": [
152
+ "StringLiteral",
153
+ "Comment"
154
+ ]
155
+ },
156
+ "doc_url": "https://angular.dev/reference/migrations"
157
+ },
158
+ {
159
+ "key": "rxResource_loader_to_stream",
160
+ "summary": "rxResource(): 'loader' → 'stream'",
161
+ "description": "rxResource renomme 'loader' en 'stream' pour clarifier qu'il retourne un Observable stream plutôt qu'une Promise. Ce changement aligne la terminologie avec les conventions RxJS et améliore la compréhension du comportement asynchrone.",
162
+ "estimated_time_per_occurrence": 3,
163
+ "onFile": null,
164
+ "fileTypes": [
165
+ "*.ts"
166
+ ],
167
+ "regex": "\\brxResource[\\s\\S]*?loader\\s*:",
168
+ "category": "api",
169
+ "auto_fixable": true,
170
+ "migration_command": null,
171
+ "risk_level": "medium",
172
+ "code_description": "// Avant:\nrxResource({\n loader: () => httpClient.get('/data')\n})\n\n// Après:\nrxResource({\n stream: () => httpClient.get('/data')\n})",
173
+ "astPattern": {
174
+ "nodeType": "CallExpression",
175
+ "functionName": "rxResource",
176
+ "arguments": {
177
+ "properties": {
178
+ "loader": {
179
+ "exists": true
180
+ }
181
+ }
182
+ },
183
+ "excludeContext": [
184
+ "StringLiteral",
185
+ "Comment"
186
+ ]
187
+ },
188
+ "doc_url": "https://angular.dev/reference/migrations"
189
+ },
190
+ {
191
+ "key": "resourceStatus_enum_to_string",
192
+ "summary": "ResourceStatus enum → strings littéraux",
193
+ "description": "ResourceStatus n'est plus un enum TypeScript mais un union type de strings littéraux pour un meilleur tree-shaking et des bundles plus petits. Remplacer ResourceStatus.Loading par 'loading', etc. Valeurs: 'idle', 'loading', 'resolved', 'error', 'reloading', 'local'.",
194
+ "estimated_time_per_occurrence": 2,
195
+ "onFile": null,
196
+ "fileTypes": [
197
+ "*.ts"
198
+ ],
199
+ "regex": "ResourceStatus\\.(Loading|Idle|Resolved|Error|Reloading|Local)",
200
+ "category": "api",
201
+ "auto_fixable": true,
202
+ "migration_command": null,
203
+ "risk_level": "medium",
204
+ "code_description": "// Avant:\nimport { ResourceStatus } from '@angular/core';\nif (resource.status() === ResourceStatus.Loading) { }\n\n// Après:\nif (resource.status() === 'loading') { }\n// Valeurs: 'idle' | 'loading' | 'resolved' | 'error' | 'reloading' | 'local'",
205
+ "astPattern": {
206
+ "nodeType": "PropertyAccessExpression",
207
+ "expression": {
208
+ "nodeType": "Identifier",
209
+ "name": "ResourceStatus"
210
+ },
211
+ "name": [
212
+ "Loading",
213
+ "Idle",
214
+ "Resolved",
215
+ "Error",
216
+ "Reloading",
217
+ "Local"
218
+ ],
219
+ "excludeContext": [
220
+ "StringLiteral",
221
+ "Comment"
222
+ ]
223
+ },
224
+ "doc_url": "https://angular.dev/reference/migrations"
225
+ },
226
+ {
227
+ "key": "template_operators_in_void",
228
+ "summary": "Opérateurs 'in' et 'void' réservés dans templates",
229
+ "description": "Les mots-clés JavaScript 'in' et 'void' sont maintenant réservés dans les templates Angular et ne peuvent plus être utilisés comme noms de propriétés sans qualification. Utiliser this.in/this.void ou renommer les propriétés pour éviter les conflits.",
230
+ "estimated_time_per_occurrence": 5,
231
+ "onFile": null,
232
+ "fileTypes": [
233
+ "*.html",
234
+ "*.ts"
235
+ ],
236
+ "regex": "\\{\\{\\s*(in|void)\\s*\\}\\}",
237
+ "astPattern": {
238
+ "nodeType": "BoundText",
239
+ "textMatches": "^\\s*(in|void)\\s*$",
240
+ "excludeContext": [
241
+ "StringLiteral",
242
+ "Comment"
243
+ ]
244
+ },
245
+ "category": "template",
246
+ "auto_fixable": false,
247
+ "migration_command": null,
248
+ "risk_level": "high",
249
+ "code_description": "// Avant:\n@Component({ template: `{{ in }} {{ void }}` })\nclass MyComponent {\n in = 'value';\n void = 'data';\n}\n\n// Après:\n// Option 1: utiliser this\n{{ this.in }} {{ this.void }}\n\n// Option 2: renommer\ninValue = 'value';\nvoidData = 'data';",
250
+ "doc_url": "https://angular.dev/reference/migrations"
251
+ },
252
+ {
253
+ "key": "testBed_flushEffects_removed",
254
+ "summary": "TestBed.flushEffects() supprimé → TestBed.tick()",
255
+ "description": "TestBed.flushEffects() est supprimé car redondant avec TestBed.tick() qui flush maintenant automatiquement les effects en plus des microtasks et macrotasks. Remplacer tous les appels par TestBed.tick().",
256
+ "estimated_time_per_occurrence": 3,
257
+ "onFile": null,
258
+ "fileTypes": [
259
+ "*.spec.ts"
260
+ ],
261
+ "regex": "TestBed\\.flushEffects\\s*\\(",
262
+ "category": "test",
263
+ "auto_fixable": true,
264
+ "migration_command": null,
265
+ "risk_level": "medium",
266
+ "code_description": "// Avant:\nTestBed.flushEffects();\n\n// Après:\nTestBed.tick();",
267
+ "astPattern": {
268
+ "nodeType": "CallExpression",
269
+ "expression": {
270
+ "nodeType": "PropertyAccessExpression",
271
+ "object": "TestBed",
272
+ "property": "flushEffects"
273
+ },
274
+ "excludeContext": [
275
+ "StringLiteral",
276
+ "Comment"
277
+ ]
278
+ },
279
+ "doc_url": "https://angular.dev/reference/migrations"
280
+ },
281
+ {
282
+ "key": "ng_reflect_attributes_removed",
283
+ "summary": "Attributs ng-reflect-* supprimés",
284
+ "description": "Les attributs ng-reflect-* utilisés pour le debugging sont supprimés par défaut pour réduire la taille du DOM. Si nécessaire temporairement, utiliser provideNgReflectAttributes() en dev mode uniquement, mais refactoriser le code pour ne plus dépendre de ces attributs.",
285
+ "estimated_time_per_occurrence": 10,
286
+ "onFile": null,
287
+ "fileTypes": [
288
+ "*.ts",
289
+ "*.spec.ts"
290
+ ],
291
+ "regex": "getAttribute\\s*\\(\\s*['\"]ng-reflect-",
292
+ "category": "test",
293
+ "auto_fixable": false,
294
+ "migration_command": null,
295
+ "risk_level": "high",
296
+ "code_description": "// Avant:\nconst value = element.getAttribute('ng-reflect-disabled');\n\n// Après:\n// Option 1: Provider temporaire (dev only)\nprovideNgReflectAttributes()\n\n// Option 2: Refactoriser le code (recommandé)",
297
+ "astPattern": {
298
+ "nodeType": "CallExpression",
299
+ "functionName": "getAttribute",
300
+ "arguments": {
301
+ "contains": {
302
+ "nodeType": "StringLiteral",
303
+ "text": {
304
+ "startsWith": "ng-reflect-"
305
+ }
306
+ }
307
+ },
308
+ "excludeContext": [
309
+ "StringLiteral",
310
+ "Comment"
311
+ ]
312
+ },
313
+ "doc_url": "https://angular.dev/reference/migrations"
314
+ },
315
+ {
316
+ "key": "redirectFn_async_support",
317
+ "summary": "RedirectFn peut retourner Observable/Promise",
318
+ "description": "RedirectFn supporte maintenant les valeurs asynchrones via Observable ou Promise en plus des strings/UrlTree synchrones. Permet des redirections conditionnelles basées sur des appels API ou l'état asynchrone de l'application.",
319
+ "estimated_time_per_occurrence": 5,
320
+ "onFile": null,
321
+ "fileTypes": [
322
+ "*.ts",
323
+ "*.spec.ts"
324
+ ],
325
+ "regex": ":\\s*RedirectFn\\s*=",
326
+ "category": "routing",
327
+ "auto_fixable": false,
328
+ "migration_command": null,
329
+ "risk_level": "low",
330
+ "code_description": "// Avant:\nconst redirectFn: RedirectFn = () => '/login';\n\n// Après:\nconst redirectFn: RedirectFn = () => {\n return '/login'; // string\n // OU return of('/login'); // Observable\n // OU return Promise.resolve('/login'); // Promise\n};",
331
+ "astPattern": {
332
+ "nodeType": "VariableDeclaration",
333
+ "type": "RedirectFn",
334
+ "initializer": {
335
+ "nodeType": [
336
+ "ArrowFunction",
337
+ "FunctionExpression"
338
+ ],
339
+ "returnType": {
340
+ "missing": [
341
+ "Observable",
342
+ "Promise"
343
+ ]
344
+ }
345
+ },
346
+ "excludeContext": [
347
+ "StringLiteral",
348
+ "Comment"
349
+ ]
350
+ },
351
+ "doc_url": "https://angular.dev/reference/migrations"
352
+ },
353
+ {
354
+ "key": "route_guards_no_strings",
355
+ "summary": "Guards: plus de strings dans canActivate",
356
+ "description": "L'utilisation de strings pour référencer des guards dans canActivate/canDeactivate est dépréciée et supprimée. Utiliser directement les classes de guards ou les fonctions guard, permettant un meilleur type checking et tree-shaking.",
357
+ "estimated_time_per_occurrence": 5,
358
+ "onFile": null,
359
+ "fileTypes": [
360
+ "*.ts"
361
+ ],
362
+ "regex": "canActivate\\s*:\\s*\\[\\s*['\"]",
363
+ "category": "routing",
364
+ "auto_fixable": false,
365
+ "migration_command": null,
366
+ "risk_level": "medium",
367
+ "code_description": "// Avant:\ncanActivate: ['authGuard']\n\n// Après:\ncanActivate: [AuthGuard] // ProviderToken ou fonction",
368
+ "astPattern": {
369
+ "nodeType": "PropertyAssignment",
370
+ "name": [
371
+ "canActivate",
372
+ "canActivateChild",
373
+ "canDeactivate",
374
+ "canMatch"
375
+ ],
376
+ "initializer": {
377
+ "nodeType": "ArrayLiteralExpression",
378
+ "elements": {
379
+ "contains": {
380
+ "nodeType": "StringLiteral"
381
+ }
382
+ }
383
+ },
384
+ "excludeContext": [
385
+ "StringLiteral",
386
+ "Comment"
387
+ ]
388
+ },
389
+ "doc_url": "https://angular.dev/reference/migrations"
390
+ },
391
+ {
392
+ "key": "template_parentheses_strict",
393
+ "summary": "Parenthèses respectées strictement (comportement JS natif)",
394
+ "description": "Angular 20 respecte maintenant strictement les règles de parenthèses JavaScript. Les expressions comme (foo?.bar).baz qui fonctionnaient par accident vont maintenant lancer une erreur si foo est null. Utiliser le chaînage optionnel correct: foo?.bar?.baz.",
395
+ "estimated_time_per_occurrence": 5,
396
+ "onFile": null,
397
+ "fileTypes": [
398
+ "*.html",
399
+ "*.ts"
400
+ ],
401
+ "regex": "\\(\\s*\\w+\\?\\.\\w+\\s*\\)\\.\\w+",
402
+ "astPattern": {
403
+ "nodeType": "BoundText",
404
+ "textMatches": "\\(\\s*[\\w.]+\\?\\.[\\w.]+\\s*\\)\\.[\\w.]+",
405
+ "excludeContext": [
406
+ "StringLiteral",
407
+ "Comment"
408
+ ]
409
+ },
410
+ "category": "template",
411
+ "auto_fixable": false,
412
+ "migration_command": null,
413
+ "risk_level": "high",
414
+ "code_description": "// Avant (fonctionnait par accident):\n{{ (foo?.bar).baz }}\n\n// Après:\n{{ foo?.bar?.baz }}\n// (foo?.bar).baz lancera erreur si foo est null",
415
+ "doc_url": "https://angular.dev/reference/migrations"
416
+ },
417
+ {
418
+ "key": "testBed_get_removed",
419
+ "summary": "TestBed.get() complètement supprimé",
420
+ "description": "TestBed.get() déprécié depuis Angular 9 est maintenant complètement supprimé. Utiliser TestBed.inject() qui offre un meilleur typage TypeScript et une API cohérente avec la fonction inject() du framework.",
421
+ "estimated_time_per_occurrence": 2,
422
+ "onFile": null,
423
+ "fileTypes": [
424
+ "*.spec.ts"
425
+ ],
426
+ "regex": "TestBed\\.get\\s*\\(",
427
+ "category": "test",
428
+ "auto_fixable": true,
429
+ "migration_command": null,
430
+ "risk_level": "medium",
431
+ "code_description": "// Avant:\nconst service = TestBed.get(MyService);\n\n// Après:\nconst service = TestBed.inject(MyService);",
432
+ "astPattern": {
433
+ "nodeType": "CallExpression",
434
+ "expression": {
435
+ "nodeType": "PropertyAccessExpression",
436
+ "object": "TestBed",
437
+ "property": "get"
438
+ },
439
+ "excludeContext": [
440
+ "StringLiteral",
441
+ "Comment"
442
+ ]
443
+ },
444
+ "doc_url": "https://angular.dev/reference/migrations"
445
+ },
446
+ {
447
+ "key": "injectFlags_enum_removed",
448
+ "summary": "InjectFlags enum supprimé",
449
+ "description": "L'enum InjectFlags est supprimé au profit d'un objet d'options plus explicite et extensible. Remplacer InjectFlags.Optional par { optional: true }, InjectFlags.Self par { self: true }, etc. Meilleure lisibilité et typage.",
450
+ "estimated_time_per_occurrence": 3,
451
+ "onFile": null,
452
+ "fileTypes": [
453
+ "*.ts"
454
+ ],
455
+ "regex": "InjectFlags\\.(Optional|Self|SkipSelf|Host)",
456
+ "category": "api",
457
+ "auto_fixable": true,
458
+ "migration_command": null,
459
+ "risk_level": "medium",
460
+ "code_description": "// Avant:\nimport { InjectFlags } from '@angular/core';\ninject(MyService, InjectFlags.Optional);\n\n// Après:\ninject(MyService, { optional: true });",
461
+ "astPattern": {
462
+ "nodeType": "PropertyAccessExpression",
463
+ "expression": {
464
+ "nodeType": "Identifier",
465
+ "name": "InjectFlags"
466
+ },
467
+ "name": [
468
+ "Optional",
469
+ "Self",
470
+ "SkipSelf",
471
+ "Host"
472
+ ],
473
+ "excludeContext": [
474
+ "StringLiteral",
475
+ "Comment"
476
+ ]
477
+ },
478
+ "doc_url": "https://angular.dev/reference/migrations"
479
+ },
480
+ {
481
+ "key": "pendingTasks_run_changed",
482
+ "summary": "PendingTasks.run() changé → add() + remove()",
483
+ "description": "PendingTasks.run() ne retourne plus la valeur de la fonction passée en paramètre. Utiliser add() pour ajouter une tâche, exécuter le code asynchrone, puis remove() dans un finally pour nettoyer. Offre plus de contrôle sur le cycle de vie des tâches.",
484
+ "estimated_time_per_occurrence": 8,
485
+ "onFile": null,
486
+ "fileTypes": [
487
+ "*.ts"
488
+ ],
489
+ "regex": "pendingTasks\\.run\\s*\\(",
490
+ "category": "api",
491
+ "auto_fixable": false,
492
+ "migration_command": null,
493
+ "risk_level": "medium",
494
+ "code_description": "// Avant:\nconst result = await pendingTasks.run(() => promise);\n\n// Après:\nconst taskId = pendingTasks.add();\ntry {\n const result = await promise;\n} finally {\n pendingTasks.remove(taskId);\n}",
495
+ "astPattern": {
496
+ "nodeType": "CallExpression",
497
+ "expression": {
498
+ "nodeType": "PropertyAccessExpression",
499
+ "object": "pendingTasks",
500
+ "property": "run"
501
+ },
502
+ "excludeContext": [
503
+ "StringLiteral",
504
+ "Comment"
505
+ ]
506
+ },
507
+ "doc_url": "https://angular.dev/reference/migrations"
508
+ },
509
+ {
510
+ "key": "asyncPipe_errors_to_errorHandler",
511
+ "summary": "AsyncPipe erreurs reportées à ErrorHandler",
512
+ "description": "Les erreurs dans les Observables utilisés avec AsyncPipe sont maintenant reportées au ErrorHandler global au lieu d'être silencieuses. Dans les tests, mocker ErrorHandler pour capturer et valider ces erreurs. Améliore le debugging des erreurs asynchrones.",
513
+ "estimated_time_per_occurrence": 5,
514
+ "onFile": null,
515
+ "fileTypes": [
516
+ "*.html",
517
+ "*.ts",
518
+ "*.spec.ts"
519
+ ],
520
+ "regex": "\\|\\s*async",
521
+ "category": "template",
522
+ "auto_fixable": false,
523
+ "migration_command": null,
524
+ "risk_level": "medium",
525
+ "code_description": "// En v20: erreurs AsyncPipe → ErrorHandler\n\n// Dans les tests:\nTestBed.configureTestingModule({\n providers: [{\n provide: ErrorHandler,\n useValue: { handleError: jasmine.createSpy('handleError') }\n }]\n});",
526
+ "astPattern": {
527
+ "nodeType": "Decorator",
528
+ "name": "Component",
529
+ "template": {
530
+ "contains": "| async"
531
+ },
532
+ "excludeContext": [
533
+ "StringLiteral",
534
+ "Comment"
535
+ ]
536
+ },
537
+ "doc_url": "https://angular.dev/reference/migrations"
538
+ },
539
+ {
540
+ "key": "angular_json_localize_name_to_project",
541
+ "summary": "angular.json localize: 'name' → 'project'",
542
+ "description": "Le builder @angular/localize utilise maintenant 'project' au lieu de 'name' pour plus de cohérence avec les autres builders Angular. Mise à jour automatisable via ng update mais nécessite une vérification manuelle des configurations personnalisées.",
543
+ "estimated_time_per_occurrence": 5,
544
+ "onFile": "angular.json",
545
+ "fileTypes": [
546
+ "angular.json"
547
+ ],
548
+ "regex": "\"builder\"[\\s\\S]*?\"@angular/localize\"[\\s\\S]*?\"options\"[\\s\\S]*?\\{[\\s\\S]*?\"name\"[\\s\\S]*?\"[^\"]+\"",
549
+ "category": "config",
550
+ "auto_fixable": true,
551
+ "migration_command": null,
552
+ "risk_level": "low",
553
+ "code_description": "// angular.json\n// Avant:\n\"options\": { \"name\": \"my-project\" }\n\n// Après:\n\"options\": { \"project\": \"my-project\" }",
554
+ "doc_url": "https://angular.dev/reference/migrations"
555
+ }
556
+ ]