@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,465 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.clearTemplateCache = clearTemplateCache;
37
+ exports.checkObjectProperties = checkObjectProperties;
38
+ exports.matchesProperties = matchesProperties;
39
+ exports.matchesTemplate = matchesTemplate;
40
+ exports.matchesParameters = matchesParameters;
41
+ exports.matchesDeclarations = matchesDeclarations;
42
+ exports.matchesPipeName = matchesPipeName;
43
+ exports.matchesOptionalChaining = matchesOptionalChaining;
44
+ exports.matchesNonNullAssertion = matchesNonNullAssertion;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const ts_morph_1 = require("ts-morph");
48
+ const matcher_helpers_1 = require("../utils/matcher-helpers");
49
+ /**
50
+ * Cache LRU pour les fichiers HTML externes (templateUrl)
51
+ * Clé: chemin absolu du fichier HTML
52
+ * Valeur: contenu du fichier
53
+ * Taille max: 100 fichiers (évite surcharge mémoire)
54
+ */
55
+ class TemplateCache {
56
+ cache = new Map();
57
+ maxSize = 100;
58
+ get(filePath) {
59
+ return this.cache.get(filePath);
60
+ }
61
+ set(filePath, content) {
62
+ // LRU: si cache plein, supprimer le plus ancien
63
+ if (this.cache.size >= this.maxSize) {
64
+ const firstKey = this.cache.keys().next().value;
65
+ if (firstKey !== undefined) {
66
+ this.cache.delete(firstKey);
67
+ }
68
+ }
69
+ this.cache.set(filePath, content);
70
+ }
71
+ clear() {
72
+ this.cache.clear();
73
+ }
74
+ }
75
+ // Instance globale du cache (partagée entre tous les appels)
76
+ const templateCache = new TemplateCache();
77
+ /**
78
+ * Efface le cache des templates (pour tests ou libération mémoire)
79
+ */
80
+ function clearTemplateCache() {
81
+ templateCache.clear();
82
+ }
83
+ /**
84
+ * Vérifie les propriétés d'un objet littéral
85
+ * @param objectNode Nœud ObjectLiteralExpression
86
+ * @param properties Patterns de propriétés à vérifier
87
+ * @param matchesAstPatternFn Fonction de matching récursif (optionnelle)
88
+ */
89
+ function checkObjectProperties(objectNode, properties, matchesAstPatternFn) {
90
+ for (const [propName, propPattern] of Object.entries(properties)) {
91
+ const property = objectNode.getProperty(propName);
92
+ // Vérifier si la propriété doit exister
93
+ // SAUF si le pattern a aussi 'elements' (traité plus tard pour valider le contenu)
94
+ if (typeof propPattern === 'object' && 'exists' in propPattern && !('elements' in propPattern)) {
95
+ const shouldExist = propPattern.exists;
96
+ const exists = property !== undefined;
97
+ if (shouldExist !== exists) {
98
+ return false;
99
+ }
100
+ }
101
+ // Vérifier si la propriété doit être manquante
102
+ if (typeof propPattern === 'object' && 'missing' in propPattern) {
103
+ const shouldBeMissing = propPattern.missing;
104
+ const exists = property !== undefined;
105
+ if (shouldBeMissing && exists) {
106
+ return false;
107
+ }
108
+ }
109
+ // Vérifier containsAny
110
+ if (typeof propPattern === 'object' && 'containsAny' in propPattern) {
111
+ const initializer = getPropertyInitializer(property);
112
+ if (!initializer) {
113
+ return false;
114
+ }
115
+ // Si l'initializer est un objet et que containsAny a des patterns avec valueMatches + nodeType: StringLiteral
116
+ // alors vérifier les noms de propriétés de l'objet (cas onpush_host_bindings)
117
+ const hasValueMatchesPattern = propPattern.containsAny.some((p) => typeof p === 'object' && 'valueMatches' in p && p.nodeType === 'StringLiteral');
118
+ if (hasValueMatchesPattern && ts_morph_1.Node.isObjectLiteralExpression(initializer)) {
119
+ // Cas spécial: vérifier les noms de propriétés de l'objet avec valueMatches
120
+ const propertyNames = initializer.getProperties().map(p => {
121
+ if (ts_morph_1.Node.isPropertyAssignment(p)) {
122
+ return p.getName();
123
+ }
124
+ return '';
125
+ });
126
+ const hasMatch = propPattern.containsAny.some((subPattern) => {
127
+ if (typeof subPattern === 'object' && subPattern.valueMatches) {
128
+ const regex = new RegExp(subPattern.valueMatches);
129
+ return propertyNames.some(name => regex.test(name));
130
+ }
131
+ return false;
132
+ });
133
+ if (!hasMatch) {
134
+ return false;
135
+ }
136
+ }
137
+ else {
138
+ // Vérification classique: matcher sur l'initializer avec matchesAstPatternFn
139
+ if (matchesAstPatternFn) {
140
+ const hasMatch = propPattern.containsAny.some((subPattern) => matchesAstPatternFn(initializer, subPattern));
141
+ if (!hasMatch) {
142
+ return false;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ // Vérifier les valeurs spécifiques des propriétés (string, boolean, etc.)
148
+ if (typeof propPattern === 'string') {
149
+ const initializer = getPropertyInitializer(property);
150
+ if (!initializer) {
151
+ return false;
152
+ }
153
+ const valueText = initializer.getText();
154
+ // Normaliser les valeurs (retirer quotes, etc.)
155
+ const normalizedValue = valueText.replace(/['"]/g, '');
156
+ if (normalizedValue !== propPattern && valueText !== propPattern) {
157
+ return false;
158
+ }
159
+ }
160
+ // Vérification générique pour les patterns AST complexes (avec nodeType, elements, etc.)
161
+ if (typeof propPattern === 'object' &&
162
+ !('exists' in propPattern) &&
163
+ !('missing' in propPattern) &&
164
+ !('containsAny' in propPattern) &&
165
+ matchesAstPatternFn) {
166
+ const initializer = getPropertyInitializer(property);
167
+ if (!initializer) {
168
+ return false;
169
+ }
170
+ // Appliquer le matcher récursif sur l'initializer
171
+ if (!matchesAstPatternFn(initializer, propPattern)) {
172
+ return false;
173
+ }
174
+ }
175
+ // Vérifier elements avec referTo (cas spécial pour résolution de symboles)
176
+ if (typeof propPattern === 'object' && 'elements' in propPattern && 'exists' in propPattern) {
177
+ // Ce pattern a à la fois exists (pour vérifier que la propriété existe)
178
+ // ET elements (pour vérifier le contenu du tableau)
179
+ const initializer = getPropertyInitializer(property);
180
+ if (!initializer) {
181
+ return false;
182
+ }
183
+ // IMPORTANT : Si elements.referTo existe, on doit construire un pattern AST complet
184
+ // avec nodeType: ArrayLiteralExpression pour que matchesAstPattern le reconnaisse
185
+ const elementsPattern = propPattern.elements;
186
+ // Si referTo est présent, créer un pattern AST complet
187
+ if (typeof elementsPattern === 'object' && 'referTo' in elementsPattern) {
188
+ // Créer un pattern AST complet pour ArrayLiteralExpression
189
+ const fullPattern = {
190
+ nodeType: 'ArrayLiteralExpression',
191
+ elements: elementsPattern
192
+ };
193
+ if (matchesAstPatternFn && !matchesAstPatternFn(initializer, fullPattern)) {
194
+ return false;
195
+ }
196
+ }
197
+ else {
198
+ // Pattern normal sans referTo
199
+ if (matchesAstPatternFn && !matchesAstPatternFn(initializer, elementsPattern)) {
200
+ return false;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ return true;
206
+ }
207
+ /**
208
+ * Vérifie les propriétés d'un décorateur ou objet littéral
209
+ */
210
+ function matchesProperties(node, properties, matchesAstPatternFn) {
211
+ // Pour les décorateurs @Component, @Directive, etc.
212
+ if (ts_morph_1.Node.isDecorator(node)) {
213
+ const expression = node.getExpression();
214
+ if (!ts_morph_1.Node.isCallExpression(expression)) {
215
+ return false;
216
+ }
217
+ const args = expression.getArguments();
218
+ if (args.length === 0) {
219
+ return false;
220
+ }
221
+ const configObject = args[0];
222
+ if (!ts_morph_1.Node.isObjectLiteralExpression(configObject)) {
223
+ return false;
224
+ }
225
+ return checkObjectProperties(configObject, properties, matchesAstPatternFn);
226
+ }
227
+ // Pour les ObjectLiteralExpression
228
+ if (ts_morph_1.Node.isObjectLiteralExpression(node)) {
229
+ return checkObjectProperties(node, properties, matchesAstPatternFn);
230
+ }
231
+ return false;
232
+ }
233
+ /**
234
+ * Extrait le contenu du template d'un @Component (inline ou externe via templateUrl)
235
+ * @returns Le contenu du template ou null si introuvable
236
+ */
237
+ function getTemplateContent(node) {
238
+ if (!ts_morph_1.Node.isDecorator(node)) {
239
+ return null;
240
+ }
241
+ const expression = node.getExpression();
242
+ if (!ts_morph_1.Node.isCallExpression(expression)) {
243
+ return null;
244
+ }
245
+ const args = expression.getArguments();
246
+ if (args.length === 0) {
247
+ return null;
248
+ }
249
+ const configObject = args[0];
250
+ if (!ts_morph_1.Node.isObjectLiteralExpression(configObject)) {
251
+ return null;
252
+ }
253
+ // Cas 1: Template inline (propriété 'template')
254
+ const templateProperty = configObject.getProperty('template');
255
+ if (templateProperty && ts_morph_1.Node.isPropertyAssignment(templateProperty)) {
256
+ const templateValue = templateProperty.getInitializer();
257
+ if (templateValue) {
258
+ return templateValue.getText().replace(/[`'"]/g, '');
259
+ }
260
+ }
261
+ // Cas 2: Template externe (propriété 'templateUrl')
262
+ const templateUrlProperty = configObject.getProperty('templateUrl');
263
+ if (templateUrlProperty && ts_morph_1.Node.isPropertyAssignment(templateUrlProperty)) {
264
+ const templateUrlValue = templateUrlProperty.getInitializer();
265
+ if (!templateUrlValue) {
266
+ return null;
267
+ }
268
+ // Extraire le chemin du templateUrl (retirer quotes)
269
+ const templateUrl = templateUrlValue.getText().replace(/[`'"]/g, '');
270
+ // Résoudre le chemin relatif au fichier TS
271
+ const sourceFile = node.getSourceFile();
272
+ const tsFilePath = sourceFile.getFilePath();
273
+ const tsDir = path.dirname(tsFilePath);
274
+ const htmlFilePath = path.resolve(tsDir, templateUrl);
275
+ // Vérifier le cache d'abord
276
+ const cached = templateCache.get(htmlFilePath);
277
+ if (cached !== undefined) {
278
+ return cached;
279
+ }
280
+ // Lire le fichier HTML
281
+ try {
282
+ if (fs.existsSync(htmlFilePath)) {
283
+ const content = fs.readFileSync(htmlFilePath, 'utf-8');
284
+ templateCache.set(htmlFilePath, content);
285
+ return content;
286
+ }
287
+ }
288
+ catch (error) {
289
+ // Fichier introuvable ou erreur de lecture : skip silencieusement
290
+ return null;
291
+ }
292
+ }
293
+ return null;
294
+ }
295
+ /**
296
+ * Vérifie le template d'un composant Angular (inline ou externe)
297
+ * Support des templateUrl (fichiers HTML séparés) avec cache LRU
298
+ */
299
+ function matchesTemplate(node, templatePattern) {
300
+ const templateText = getTemplateContent(node);
301
+ if (!templateText) {
302
+ return false;
303
+ }
304
+ // Nettoyer les commentaires HTML avant de vérifier les patterns
305
+ let cleanedTemplate = templateText.replace(/<!--[\s\S]*?-->/g, '');
306
+ // Pour "router-outlet", chercher la balise HTML, pas juste la substring
307
+ // Convertir "router-outlet" → regex qui matche "<router-outlet" (balise ouvrante ou autofermante)
308
+ if (templatePattern.contains) {
309
+ const searchTerm = templatePattern.contains;
310
+ // Si c'est un nom de balise HTML (lettres, chiffres, tirets uniquement), créer une regex pour matcher la balise
311
+ // Sinon, faire une recherche littérale de substring
312
+ const isHtmlTag = /^[a-zA-Z][\w-]*$/.test(searchTerm);
313
+ if (isHtmlTag) {
314
+ // Chercher <tag ou <tag> ou <tag/> ou <tag
315
+ const tagRegex = new RegExp(`<${searchTerm}(?:\\s|>|/|$)`, 'i');
316
+ if (!tagRegex.test(cleanedTemplate)) {
317
+ return false;
318
+ }
319
+ }
320
+ else {
321
+ // Recherche littérale (pour des patterns comme "| async", "<div>", etc.)
322
+ if (!cleanedTemplate.includes(searchTerm)) {
323
+ return false;
324
+ }
325
+ }
326
+ }
327
+ if (templatePattern.notContains) {
328
+ // Pour notContains, toujours chercher la substring
329
+ // (car on veut détecter "routerOutletData" dans [routerOutletData]="...")
330
+ if (cleanedTemplate.includes(templatePattern.notContains)) {
331
+ return false;
332
+ }
333
+ }
334
+ return true;
335
+ }
336
+ /**
337
+ * Vérifie les paramètres d'un constructeur ou d'une fonction
338
+ */
339
+ function matchesParameters(node, parametersPattern) {
340
+ let parameters = [];
341
+ if (ts_morph_1.Node.isFunctionDeclaration(node) || ts_morph_1.Node.isMethodDeclaration(node) || ts_morph_1.Node.isConstructorDeclaration(node)) {
342
+ parameters = node.getParameters();
343
+ }
344
+ if (parameters.length === 0) {
345
+ return false;
346
+ }
347
+ // Vérifier hasModifier
348
+ if (parametersPattern.hasModifier) {
349
+ const hasModifier = parameters.some(param => {
350
+ const modifiers = param.getModifiers().map((m) => m.getText());
351
+ return parametersPattern.hasModifier.some((mod) => modifiers.includes(mod));
352
+ });
353
+ if (!hasModifier) {
354
+ return false;
355
+ }
356
+ }
357
+ // Vérifier hasType
358
+ if (parametersPattern.hasType !== undefined) {
359
+ const hasType = parameters.some(param => param.getTypeNode() !== undefined);
360
+ if (hasType !== parametersPattern.hasType) {
361
+ return false;
362
+ }
363
+ }
364
+ return true;
365
+ }
366
+ /**
367
+ * Vérifie les déclarations (pour VariableStatement)
368
+ */
369
+ function matchesDeclarations(node, declarationsPattern, matchesAstPatternFn) {
370
+ if (!ts_morph_1.Node.isVariableStatement(node)) {
371
+ return false;
372
+ }
373
+ const declarations = node.getDeclarationList().getDeclarations();
374
+ // Vérifier name
375
+ if (declarationsPattern.name) {
376
+ const names = (0, matcher_helpers_1.ensureArray)(declarationsPattern.name);
377
+ const hasMatch = declarations.some(decl => {
378
+ const declName = decl.getName();
379
+ return names.includes(declName);
380
+ });
381
+ if (!hasMatch) {
382
+ return false;
383
+ }
384
+ }
385
+ // Vérifier initializer avec matchesAstPatternFn
386
+ if (declarationsPattern.initializer && matchesAstPatternFn) {
387
+ const hasMatch = declarations.some(decl => {
388
+ const initializer = decl.getInitializer();
389
+ if (!initializer) {
390
+ return false;
391
+ }
392
+ return matchesAstPatternFn(initializer, declarationsPattern.initializer);
393
+ });
394
+ if (!hasMatch) {
395
+ return false;
396
+ }
397
+ }
398
+ return true;
399
+ }
400
+ /**
401
+ * Vérifie le nom d'une pipe dans un template
402
+ */
403
+ function matchesPipeName(node, pipeName) {
404
+ // Cette fonction nécessiterait l'analyse du template HTML
405
+ // Pour l'instant, retourne false car elle nécessite @angular/compiler
406
+ return false;
407
+ }
408
+ /**
409
+ * Vérifie si le nœud utilise optional chaining
410
+ * Note: si shouldHaveOptionalChaining est true, on accepte TOUT nœud qui a ?.
411
+ * Cela inclut les PropertyAccessExpression avec ?. OU leurs parents NonNullExpression
412
+ */
413
+ function matchesOptionalChaining(node, shouldHaveOptionalChaining) {
414
+ // Vérifier si c'est une PropertyAccessExpression avec ?.
415
+ if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
416
+ const hasOptionalChaining = node.getQuestionDotTokenNode() !== undefined;
417
+ if (hasOptionalChaining) {
418
+ return shouldHaveOptionalChaining;
419
+ }
420
+ }
421
+ // Vérifier si c'est l'expression interne d'une NonNullExpression qui contient ?.
422
+ if (ts_morph_1.Node.isNonNullExpression(node)) {
423
+ const expr = node.getExpression();
424
+ if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
425
+ const hasOptionalChaining = expr.getQuestionDotTokenNode() !== undefined;
426
+ if (hasOptionalChaining) {
427
+ return shouldHaveOptionalChaining;
428
+ }
429
+ }
430
+ }
431
+ // Si shouldHaveOptionalChaining est false, accepter les nœuds sans ?.
432
+ return !shouldHaveOptionalChaining;
433
+ }
434
+ /**
435
+ * Vérifie si le nœud utilise non-null assertion
436
+ * Note: si shouldHaveNonNullAssertion est true, on accepte TOUT nœud qui a !
437
+ * Cela inclut les NonNullExpression OU les PropertyAccessExpression qui sont dans une NonNullExpression
438
+ */
439
+ function matchesNonNullAssertion(node, shouldHaveNonNullAssertion) {
440
+ // Cas 1: Le nœud est une NonNullExpression (ex: control!)
441
+ if (ts_morph_1.Node.isNonNullExpression(node)) {
442
+ return shouldHaveNonNullAssertion;
443
+ }
444
+ // Cas 2: Le nœud est l'expression interne d'une NonNullExpression (ex: dans control.valueChanges!)
445
+ const parent = node.getParent();
446
+ if (parent && ts_morph_1.Node.isNonNullExpression(parent)) {
447
+ return shouldHaveNonNullAssertion;
448
+ }
449
+ // Cas 3: Pas de non-null assertion
450
+ return !shouldHaveNonNullAssertion;
451
+ }
452
+ /**
453
+ * Helper: Extrait l'initializer d'une propriété
454
+ * Retourne null si la propriété n'existe pas, n'est pas une PropertyAssignment, ou n'a pas d'initializer
455
+ */
456
+ function getPropertyInitializer(property) {
457
+ if (!property) {
458
+ return null;
459
+ }
460
+ if (!ts_morph_1.Node.isPropertyAssignment(property)) {
461
+ return null;
462
+ }
463
+ const initializer = property.getInitializer();
464
+ return initializer || null;
465
+ }