@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.
- package/LICENSE +96 -0
- package/README.fr.md +359 -0
- package/README.md +360 -0
- package/SECURITY.md +187 -0
- package/dist/client.bundle.js +357 -0
- package/dist/src/core/app-analyzer.js +134 -0
- package/dist/src/core/ast/matchers/html/html-attribute-matcher.js +86 -0
- package/dist/src/core/ast/matchers/html/html-component-matcher.js +40 -0
- package/dist/src/core/ast/matchers/html/html-element-matcher.js +54 -0
- package/dist/src/core/ast/matchers/html/html-parser.js +58 -0
- package/dist/src/core/ast/matchers/html/html-pipe-matcher.js +95 -0
- package/dist/src/core/ast/matchers/html/html-text-matcher.js +53 -0
- package/dist/src/core/ast/matchers/html/index.js +118 -0
- package/dist/src/core/ast/matchers/index.js +377 -0
- package/dist/src/core/ast/matchers/ts/collection-matcher.js +51 -0
- package/dist/src/core/ast/matchers/ts/context-matcher.js +275 -0
- package/dist/src/core/ast/matchers/ts/decorator-matcher.js +465 -0
- package/dist/src/core/ast/matchers/ts/expression-matcher.js +237 -0
- package/dist/src/core/ast/matchers/ts/file-matcher.js +97 -0
- package/dist/src/core/ast/matchers/ts/hierarchy-matcher.js +172 -0
- package/dist/src/core/ast/matchers/ts/import-matcher.js +39 -0
- package/dist/src/core/ast/matchers/ts/index.js +53 -0
- package/dist/src/core/ast/matchers/ts/node-matcher.js +156 -0
- package/dist/src/core/ast/matchers/ts/symbol-matcher.js +281 -0
- package/dist/src/core/ast/matchers/ts/type-matcher.js +207 -0
- package/dist/src/core/ast/matchers/utils/matcher-helpers.js +37 -0
- package/dist/src/core/ast/scanner-ast.js +444 -0
- package/dist/src/core/project-detector.js +196 -0
- package/dist/src/core/project-strategy/index.js +9 -0
- package/dist/src/core/project-strategy/nx-strategy.js +130 -0
- package/dist/src/core/project-strategy/project-strategy.interface.js +2 -0
- package/dist/src/core/project-strategy/standalone-strategy.js +74 -0
- package/dist/src/core/project-strategy/strategy-factory.js +15 -0
- package/dist/src/core/rules-loader.js +89 -0
- package/dist/src/core/scan-reporter.js +316 -0
- package/dist/src/core/scanner-delta.js +339 -0
- package/dist/src/core/scanner-orchestrator.js +266 -0
- package/dist/src/core/scanner-regex.js +298 -0
- package/dist/src/core/workload/calculator.js +82 -0
- package/dist/src/core/workload/constants.js +15 -0
- package/dist/src/core/workload/grouping.js +18 -0
- package/dist/src/core/workload/hierarchy-calculator.js +127 -0
- package/dist/src/core/workload/index.js +11 -0
- package/dist/src/core/workload/metadata.js +20 -0
- package/dist/src/core/workload/special-workload.js +101 -0
- package/dist/src/core/workload/target-resolver.js +34 -0
- package/dist/src/data/angular-migration-rules.json +2337 -0
- package/dist/src/data/markdown/angular-migration-17-18.md +408 -0
- package/dist/src/data/markdown/angular-migration-18-19.md +600 -0
- package/dist/src/data/markdown/angular-migration-19-20.md +521 -0
- package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +66 -0
- package/dist/src/data/rules/to18/rules-18-obligatoire.json +374 -0
- package/dist/src/data/rules/to18/rules-18-optionnelle.json +188 -0
- package/dist/src/data/rules/to18/rules-18-recommande.json +218 -0
- package/dist/src/data/rules/to19/rules-19-obligatoire.json +348 -0
- package/dist/src/data/rules/to19/rules-19-optionnelle.json +223 -0
- package/dist/src/data/rules/to19/rules-19-recommande.json +200 -0
- package/dist/src/data/rules/to20/rules-20-obligatoire.json +556 -0
- package/dist/src/data/rules/to20/rules-20-optionnelle.json +190 -0
- package/dist/src/data/rules/to20/rules-20-recommande.json +151 -0
- package/dist/src/index.js +161 -0
- package/dist/src/models/chip-config.js +45 -0
- package/dist/src/models/interfaces/app-details.interface.js +2 -0
- package/dist/src/models/interfaces/ast-interfaces.js +5 -0
- package/dist/src/models/interfaces/ast-pattern.interface.js +2 -0
- package/dist/src/models/interfaces/client-interfaces.js +6 -0
- package/dist/src/models/interfaces/detection-stats.interface.js +2 -0
- package/dist/src/models/interfaces/html-match.interface.js +2 -0
- package/dist/src/models/interfaces/html-report-data.interface.js +2 -0
- package/dist/src/models/interfaces/lib-details.interface.js +2 -0
- package/dist/src/models/interfaces/migration-rules.interface.js +2 -0
- package/dist/src/models/interfaces/parsed-args.interface.js +2 -0
- package/dist/src/models/interfaces/project-info.interface.js +2 -0
- package/dist/src/models/interfaces/project-overview-data.interface.js +2 -0
- package/dist/src/models/interfaces/rule-match.interface.js +2 -0
- package/dist/src/models/interfaces/rule.interface.js +2 -0
- package/dist/src/models/interfaces/rules-by-priority.interface.js +2 -0
- package/dist/src/models/interfaces/scanner-comparison.interface.js +2 -0
- package/dist/src/models/interfaces/special-workload.interface.js +2 -0
- package/dist/src/models/interfaces/workload-report.interface.js +2 -0
- package/dist/src/models/types/build-block-blob.type.js +2 -0
- package/dist/src/models/types/migration-version.type.js +2 -0
- package/dist/src/models/types/project-type.type.js +2 -0
- package/dist/src/models/types/risk-level.type.js +2 -0
- package/dist/src/models/types/rule-category.type.js +2 -0
- package/dist/src/models/types/rule-priority.type.js +2 -0
- package/dist/src/models/types/rule-workload-type.type.js +2 -0
- package/dist/src/templates/landing/applications-analyzed.template.js +18 -0
- package/dist/src/templates/landing/card-app-info.template.js +63 -0
- package/dist/src/templates/landing/card-lib-info.template.js +67 -0
- package/dist/src/templates/landing/libs-analyzed.template.js +22 -0
- package/dist/src/templates/landing/nx-summary.template.js +115 -0
- package/dist/src/templates/landing/project-overview.template.js +27 -0
- package/dist/src/templates/page/index-page.template.js +95 -0
- package/dist/src/templates/page/main.template.js +83 -0
- package/dist/src/templates/page/migration-guide.template.js +175 -0
- package/dist/src/templates/page/workload-report.template.js +53 -0
- package/dist/src/templates/workload/dashboard.template.js +184 -0
- package/dist/src/templates/workload/filters-panel.template.js +215 -0
- package/dist/src/templates/workload/guide-rule-card.template.js +107 -0
- package/dist/src/templates/workload/hierarchy-nx.template.js +104 -0
- package/dist/src/templates/workload/hierarchy-shared.js +163 -0
- package/dist/src/templates/workload/hierarchy-standalone.template.js +36 -0
- package/dist/src/templates/workload/hierarchy.template.js +35 -0
- package/dist/src/templates/workload/rule-modal.template.js +280 -0
- package/dist/src/utils/core/args-parser.js +123 -0
- package/dist/src/utils/core/array-helpers.js +18 -0
- package/dist/src/utils/core/ast-helpers.js +99 -0
- package/dist/src/utils/core/file-helpers.js +109 -0
- package/dist/src/utils/core/html-helpers.js +36 -0
- package/dist/src/utils/core/index.js +28 -0
- package/dist/src/utils/core/logger.js +38 -0
- package/dist/src/utils/core/rule-helpers.js +15 -0
- package/dist/src/utils/core/workload-formatter.js +6 -0
- package/dist/src/utils/shared/array-helpers.js +25 -0
- package/dist/src/utils/shared/date-helpers.js +109 -0
- package/dist/src/utils/shared/html-helpers.js +37 -0
- package/dist/src/utils/shared/index.js +25 -0
- package/dist/src/utils/shared/rule-helpers.js +20 -0
- package/dist/src/utils/shared/time-formatters.js +76 -0
- package/dist/styles.css +2 -0
- 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
|
+
}
|