@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,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesExpression = matchesExpression;
|
|
4
|
+
exports.matchesArguments = matchesArguments;
|
|
5
|
+
exports.matchesLeft = matchesLeft;
|
|
6
|
+
exports.matchesRight = matchesRight;
|
|
7
|
+
exports.matchesChainContains = matchesChainContains;
|
|
8
|
+
exports.matchesBody = matchesBody;
|
|
9
|
+
const ts_morph_1 = require("ts-morph");
|
|
10
|
+
const decorator_matcher_1 = require("./decorator-matcher");
|
|
11
|
+
/**
|
|
12
|
+
* Vérifie l'expression d'un nœud (CallExpression, PropertyAccessExpression, etc.)
|
|
13
|
+
* Supporte l'imbrication : expression.expression.expression etc.
|
|
14
|
+
*/
|
|
15
|
+
function matchesExpression(node, expressionPattern, matchesAstPatternFn) {
|
|
16
|
+
let expression;
|
|
17
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
18
|
+
expression = node.getExpression();
|
|
19
|
+
}
|
|
20
|
+
else if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
21
|
+
expression = node.getExpression();
|
|
22
|
+
}
|
|
23
|
+
else if (ts_morph_1.Node.isDecorator(node)) {
|
|
24
|
+
expression = node.getExpression();
|
|
25
|
+
}
|
|
26
|
+
else if (ts_morph_1.Node.isNonNullExpression(node)) {
|
|
27
|
+
expression = node.getExpression();
|
|
28
|
+
}
|
|
29
|
+
else if (ts_morph_1.Node.isNewExpression(node)) {
|
|
30
|
+
expression = node.getExpression();
|
|
31
|
+
}
|
|
32
|
+
if (!expression) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
// Si expressionPattern est une string, vérifier que l'expression est un Identifier avec ce nom
|
|
36
|
+
if (typeof expressionPattern === 'string') {
|
|
37
|
+
if (ts_morph_1.Node.isIdentifier(expression)) {
|
|
38
|
+
return expression.getText() === expressionPattern;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return matchesAstPatternFn(expression, expressionPattern);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Vérifie les arguments d'un CallExpression
|
|
46
|
+
*/
|
|
47
|
+
function matchesArguments(node, argumentsPattern, matchesAstPatternFn) {
|
|
48
|
+
if (!ts_morph_1.Node.isCallExpression(node)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const args = node.getArguments();
|
|
52
|
+
// Vérifier contains
|
|
53
|
+
if (argumentsPattern.contains) {
|
|
54
|
+
const hasMatch = args.some(arg => matchesAstPatternFn(arg, argumentsPattern.contains));
|
|
55
|
+
if (!hasMatch) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Vérifier containsAny
|
|
60
|
+
if (argumentsPattern.containsAny) {
|
|
61
|
+
const hasMatch = args.some(arg => argumentsPattern.containsAny.some((pattern) => matchesAstPatternFn(arg, pattern)));
|
|
62
|
+
if (!hasMatch) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Vérifier containsCall
|
|
67
|
+
if (argumentsPattern.containsCall) {
|
|
68
|
+
const hasCall = args.some(arg => {
|
|
69
|
+
if (!ts_morph_1.Node.isCallExpression(arg)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Vérifier le nom de la fonction
|
|
73
|
+
if (argumentsPattern.containsCall.functionName) {
|
|
74
|
+
const funcName = arg.getExpression().getText().split('.').pop() || '';
|
|
75
|
+
if (funcName !== argumentsPattern.containsCall.functionName) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Vérifier l'objet passé en argument
|
|
80
|
+
if (argumentsPattern.containsCall.object && argumentsPattern.containsCall.object.properties) {
|
|
81
|
+
const callArgs = arg.getArguments();
|
|
82
|
+
// Si des propriétés sont définies avec missing: true
|
|
83
|
+
const hasAnyMissing = Object.values(argumentsPattern.containsCall.object.properties).some((prop) => prop.missing === true);
|
|
84
|
+
if (hasAnyMissing) {
|
|
85
|
+
// Vérifier que l'appel n'a PAS d'objet ou que l'objet n'a PAS la propriété
|
|
86
|
+
if (callArgs.length === 0) {
|
|
87
|
+
// Pas d'arguments = propriété manquante
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (ts_morph_1.Node.isObjectLiteralExpression(callArgs[0])) {
|
|
91
|
+
// Vérifier que les propriétés "missing" sont bien absentes
|
|
92
|
+
return (0, decorator_matcher_1.checkObjectProperties)(callArgs[0], argumentsPattern.containsCall.object.properties);
|
|
93
|
+
}
|
|
94
|
+
// Pas un objet littéral = propriété manquante
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
// Vérification normale (exists, valeurs spécifiques)
|
|
98
|
+
if (callArgs.length > 0 && ts_morph_1.Node.isObjectLiteralExpression(callArgs[0])) {
|
|
99
|
+
return (0, decorator_matcher_1.checkObjectProperties)(callArgs[0], argumentsPattern.containsCall.object.properties);
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
105
|
+
if (!hasCall) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Vérifier properties (sur les arguments objets - par défaut le 1er, sinon chercher dans tous)
|
|
110
|
+
if (argumentsPattern.properties) {
|
|
111
|
+
// Chercher dans tous les arguments pour trouver un ObjectLiteralExpression
|
|
112
|
+
for (const arg of args) {
|
|
113
|
+
if (ts_morph_1.Node.isObjectLiteralExpression(arg)) {
|
|
114
|
+
const result = (0, decorator_matcher_1.checkObjectProperties)(arg, argumentsPattern.properties);
|
|
115
|
+
// Si on trouve un objet qui matche (ou qui ne matche pas les missing), on retourne le résultat
|
|
116
|
+
if (result || Object.values(argumentsPattern.properties).some((p) => p && typeof p === 'object' && p.missing)) {
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Aucun objet trouvé - si on cherche des "missing", c'est un match (l'objet n'existe pas du tout)
|
|
122
|
+
if (Object.values(argumentsPattern.properties).some((p) => p && typeof p === 'object' && p.missing)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
// Vérifier count
|
|
128
|
+
if (argumentsPattern.count !== undefined) {
|
|
129
|
+
if (args.length !== argumentsPattern.count) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Vérifier secondArgument
|
|
134
|
+
if (argumentsPattern.secondArgument) {
|
|
135
|
+
if (argumentsPattern.secondArgument.missing) {
|
|
136
|
+
if (args.length >= 2) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Vérifier missing (vérifier que l'argument nommé n'est PAS présent)
|
|
142
|
+
if (argumentsPattern.missing) {
|
|
143
|
+
const missingName = argumentsPattern.missing;
|
|
144
|
+
// Vérifier si un argument est un CallExpression avec ce nom de fonction
|
|
145
|
+
const hasMatchingCall = args.some(arg => {
|
|
146
|
+
if (ts_morph_1.Node.isCallExpression(arg)) {
|
|
147
|
+
const funcName = arg.getExpression().getText().split('.').pop() || '';
|
|
148
|
+
return funcName === missingName;
|
|
149
|
+
}
|
|
150
|
+
// Vérifier aussi si c'est un Identifier simple
|
|
151
|
+
if (ts_morph_1.Node.isIdentifier(arg)) {
|
|
152
|
+
return arg.getText() === missingName;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
});
|
|
156
|
+
// Si on trouve l'argument "missing", le pattern ne matche PAS
|
|
157
|
+
if (hasMatchingCall) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Vérifie la partie gauche d'une BinaryExpression
|
|
165
|
+
*/
|
|
166
|
+
function matchesLeft(node, leftPattern, matchesAstPatternFn) {
|
|
167
|
+
if (!ts_morph_1.Node.isBinaryExpression(node)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const left = node.getLeft();
|
|
171
|
+
return matchesAstPatternFn(left, leftPattern);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Vérifie la partie droite d'une BinaryExpression
|
|
175
|
+
*/
|
|
176
|
+
function matchesRight(node, rightPattern) {
|
|
177
|
+
if (!ts_morph_1.Node.isBinaryExpression(node)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const right = node.getRight();
|
|
181
|
+
// Vérifier value
|
|
182
|
+
if (rightPattern.value !== undefined) {
|
|
183
|
+
const rightValue = right.getText();
|
|
184
|
+
const expectedValue = String(rightPattern.value);
|
|
185
|
+
return rightValue === expectedValue ||
|
|
186
|
+
rightValue === `'${expectedValue}'` ||
|
|
187
|
+
rightValue === `"${expectedValue}"`;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Vérifie une chaîne de propriétés (ex: a.b.c.pipe().subscribe())
|
|
193
|
+
*/
|
|
194
|
+
function matchesChainContains(node, chainPatterns, matchesAstPatternFn) {
|
|
195
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
let current = node;
|
|
199
|
+
let patternIndex = chainPatterns.length - 1;
|
|
200
|
+
while (patternIndex >= 0 && ts_morph_1.Node.isPropertyAccessExpression(current)) {
|
|
201
|
+
if (!matchesAstPatternFn(current, chainPatterns[patternIndex])) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
patternIndex--;
|
|
205
|
+
current = current.getExpression();
|
|
206
|
+
}
|
|
207
|
+
return patternIndex < 0;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Vérifie le corps d'une fonction
|
|
211
|
+
*/
|
|
212
|
+
function matchesBody(node, bodyPattern, matchesAstPatternFn) {
|
|
213
|
+
let body;
|
|
214
|
+
if (ts_morph_1.Node.isFunctionDeclaration(node)) {
|
|
215
|
+
body = node.getBody();
|
|
216
|
+
}
|
|
217
|
+
else if (ts_morph_1.Node.isMethodDeclaration(node)) {
|
|
218
|
+
body = node.getBody();
|
|
219
|
+
}
|
|
220
|
+
else if (ts_morph_1.Node.isArrowFunction(node)) {
|
|
221
|
+
body = node.getBody();
|
|
222
|
+
}
|
|
223
|
+
if (!body) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
// Vérifier contains
|
|
227
|
+
if (bodyPattern.contains) {
|
|
228
|
+
let hasMatch = false;
|
|
229
|
+
body.forEachDescendant((child) => {
|
|
230
|
+
if (matchesAstPatternFn(child, bodyPattern.contains)) {
|
|
231
|
+
hasMatch = true;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return hasMatch;
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesInFilePattern = matchesInFilePattern;
|
|
4
|
+
exports.matchesTestSetup = matchesTestSetup;
|
|
5
|
+
const ts_morph_1 = require("ts-morph");
|
|
6
|
+
/**
|
|
7
|
+
* Vérifie si un fichier correspond au pattern inFile
|
|
8
|
+
* @param filePath Chemin relatif du fichier
|
|
9
|
+
* @param inFilePattern Pattern de fichier (string, objet tsconfig, objet avec exclude, etc.)
|
|
10
|
+
*/
|
|
11
|
+
function matchesInFilePattern(filePath, inFilePattern) {
|
|
12
|
+
// Si c'est un pattern de type "*.spec.ts" ou "angular.json"
|
|
13
|
+
if (typeof inFilePattern === 'string') {
|
|
14
|
+
// Normaliser le chemin (remplacer \ par /)
|
|
15
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
16
|
+
// Convertir le pattern glob en regex
|
|
17
|
+
const regexPattern = inFilePattern
|
|
18
|
+
.replace(/\./g, '\\.')
|
|
19
|
+
.replace(/\*/g, '.*');
|
|
20
|
+
const regex = new RegExp(regexPattern + '$');
|
|
21
|
+
// Tester le chemin complet ET juste le nom de fichier
|
|
22
|
+
const fileName = normalizedPath.split('/').pop() || '';
|
|
23
|
+
return regex.test(normalizedPath) || regex.test(fileName);
|
|
24
|
+
}
|
|
25
|
+
// Si c'est un objet avec exclude (ex: { exclude: "*.spec.ts" })
|
|
26
|
+
if (typeof inFilePattern === 'object' && inFilePattern.exclude) {
|
|
27
|
+
// Normaliser le chemin (remplacer \ par /)
|
|
28
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
29
|
+
// Convertir le pattern glob en regex
|
|
30
|
+
const excludePattern = inFilePattern.exclude;
|
|
31
|
+
const regexPattern = excludePattern
|
|
32
|
+
.replace(/\./g, '\\.')
|
|
33
|
+
.replace(/\*/g, '.*');
|
|
34
|
+
const regex = new RegExp(regexPattern + '$');
|
|
35
|
+
// Tester le chemin complet ET juste le nom de fichier
|
|
36
|
+
const fileName = normalizedPath.split('/').pop() || '';
|
|
37
|
+
const isExcluded = regex.test(normalizedPath) || regex.test(fileName);
|
|
38
|
+
// Retourner l'inverse : si exclu, retourner false
|
|
39
|
+
return !isExcluded;
|
|
40
|
+
}
|
|
41
|
+
// Si c'est un objet de configuration (ex: { tsconfig: {...} })
|
|
42
|
+
if (typeof inFilePattern === 'object' && inFilePattern.tsconfig) {
|
|
43
|
+
// Pour les patterns tsconfig complexes (ex: host_bindings_type_checking)
|
|
44
|
+
// On vérifie si le fichier devrait être concerné
|
|
45
|
+
// Cette vérification nécessite de lire tsconfig.json, ce qui est coûteux
|
|
46
|
+
// Règle spécifique: host_bindings_type_checking concerne tous les fichiers .ts
|
|
47
|
+
if (inFilePattern.tsconfig.angularCompilerOptions) {
|
|
48
|
+
return filePath.endsWith('.ts') && !filePath.endsWith('.spec.ts');
|
|
49
|
+
}
|
|
50
|
+
// Par défaut, matcher tous les fichiers pour ne pas bloquer l'analyse
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vérifie si le setup de test correspond au pattern testSetup
|
|
57
|
+
* @param sourceFile Fichier source TypeScript
|
|
58
|
+
* @param testSetupPattern Pattern de setup de test (providers, etc.)
|
|
59
|
+
*/
|
|
60
|
+
function matchesTestSetup(sourceFile, testSetupPattern) {
|
|
61
|
+
// Vérifier si ErrorHandler est manquant dans les providers du TestBed
|
|
62
|
+
if (testSetupPattern.providers && testSetupPattern.providers.missing) {
|
|
63
|
+
const missingProvider = testSetupPattern.providers.missing;
|
|
64
|
+
let hasProvider = false;
|
|
65
|
+
// Chercher tous les appels à TestBed.configureTestingModule
|
|
66
|
+
sourceFile.forEachDescendant((node) => {
|
|
67
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
68
|
+
const expression = node.getExpression();
|
|
69
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(expression)) {
|
|
70
|
+
const objectText = expression.getExpression().getText();
|
|
71
|
+
const propertyText = expression.getName();
|
|
72
|
+
if (objectText === 'TestBed' && propertyText === 'configureTestingModule') {
|
|
73
|
+
// Analyser les arguments pour trouver providers
|
|
74
|
+
const args = node.getArguments();
|
|
75
|
+
if (args.length > 0 && ts_morph_1.Node.isObjectLiteralExpression(args[0])) {
|
|
76
|
+
const configObj = args[0];
|
|
77
|
+
const providersProperty = configObj.getProperty('providers');
|
|
78
|
+
if (providersProperty && ts_morph_1.Node.isPropertyAssignment(providersProperty)) {
|
|
79
|
+
const providersValue = providersProperty.getInitializer();
|
|
80
|
+
if (providersValue) {
|
|
81
|
+
const providersText = providersValue.getText();
|
|
82
|
+
if (providersText.includes(missingProvider)) {
|
|
83
|
+
hasProvider = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// Si le pattern demande "missing: ErrorHandler" et qu'on l'a trouvé, retourner false
|
|
93
|
+
// Sinon, retourner true (le provider est bien manquant)
|
|
94
|
+
return !hasProvider;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesParent = matchesParent;
|
|
4
|
+
exports.matchesInClass = matchesInClass;
|
|
5
|
+
exports.matchesAncestor = matchesAncestor;
|
|
6
|
+
exports.matchesImplements = matchesImplements;
|
|
7
|
+
exports.matchesImplementsGeneric = matchesImplementsGeneric;
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
const matcher_helpers_1 = require("../utils/matcher-helpers");
|
|
10
|
+
/**
|
|
11
|
+
* Vérifie le parent d'un nœud
|
|
12
|
+
* Note: matchesAstPattern sera importé depuis index.ts pour éviter les dépendances circulaires
|
|
13
|
+
*/
|
|
14
|
+
function matchesParent(node, parentPattern, matchesAstPatternFn) {
|
|
15
|
+
const parent = node.getParent();
|
|
16
|
+
if (!parent) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
// Vérifier notNodeType et notFunctionName : chercher dans TOUS les ancêtres (pas juste parent immédiat)
|
|
20
|
+
if (parentPattern.notNodeType || parentPattern.notFunctionName) {
|
|
21
|
+
let current = node.getParent();
|
|
22
|
+
while (current) {
|
|
23
|
+
// Vérifier notNodeType
|
|
24
|
+
if (parentPattern.notNodeType && current.getKindName() === parentPattern.notNodeType) {
|
|
25
|
+
// Vérifier notFunctionName si spécifié
|
|
26
|
+
if (parentPattern.notFunctionName && ts_morph_1.Node.isCallExpression(current)) {
|
|
27
|
+
const funcName = current.getExpression().getText().split('.').pop() || '';
|
|
28
|
+
if (funcName === parentPattern.notFunctionName) {
|
|
29
|
+
return false; // Trouvé le pattern à exclure
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (!parentPattern.notFunctionName) {
|
|
33
|
+
return false; // notNodeType seul trouvé
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
current = current.getParent();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// D'abord vérifier le nodeType du parent
|
|
40
|
+
if (parentPattern.nodeType) {
|
|
41
|
+
const parentNodeTypes = (0, matcher_helpers_1.ensureArray)(parentPattern.nodeType);
|
|
42
|
+
// Si name est spécifié, chercher dans les ancêtres une VariableDeclaration qui matche
|
|
43
|
+
if (parentPattern.name) {
|
|
44
|
+
let current = node.getParent();
|
|
45
|
+
while (current) {
|
|
46
|
+
const currentKind = current.getKindName();
|
|
47
|
+
if (parentNodeTypes.includes(currentKind)) {
|
|
48
|
+
// Vérifier si ce nœud a le nom recherché
|
|
49
|
+
if (ts_morph_1.Node.isVariableDeclaration(current)) {
|
|
50
|
+
const varName = current.getName();
|
|
51
|
+
const expectedNames = (0, matcher_helpers_1.ensureArray)(parentPattern.name);
|
|
52
|
+
if (expectedNames.includes(varName)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (ts_morph_1.Node.isVariableStatement(current)) {
|
|
57
|
+
// Pour VariableStatement, vérifier les déclarations
|
|
58
|
+
const declarations = current.getDeclarations();
|
|
59
|
+
const expectedNames = (0, matcher_helpers_1.ensureArray)(parentPattern.name);
|
|
60
|
+
const hasMatchingName = declarations.some(decl => expectedNames.includes(decl.getName()));
|
|
61
|
+
if (hasMatchingName) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
current = current.getParent();
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Si pas de name, vérifier seulement le nodeType du parent immédiat
|
|
71
|
+
const parentKind = parent.getKindName();
|
|
72
|
+
if (!parentNodeTypes.includes(parentKind)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Vérifier initializer (pour PropertyDeclaration) APRÈS avoir vérifié le nodeType
|
|
77
|
+
if (parentPattern.initializer && ts_morph_1.Node.isPropertyDeclaration(parent)) {
|
|
78
|
+
const initializer = parent.getInitializer();
|
|
79
|
+
if (!initializer) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// Vérifier récursivement l'initializer
|
|
83
|
+
if (!matchesAstPatternFn(initializer, parentPattern.initializer)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
// Si l'initializer matche, on retourne true (pas besoin de vérifier tout le parent)
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return matchesAstPatternFn(parent, parentPattern);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Vérifie si le nœud est dans une classe avec les décorateurs spécifiés
|
|
93
|
+
*/
|
|
94
|
+
function matchesInClass(node, classPattern, matchesAstPatternFn) {
|
|
95
|
+
let current = node.getParent();
|
|
96
|
+
while (current) {
|
|
97
|
+
if (ts_morph_1.Node.isClassDeclaration(current)) {
|
|
98
|
+
// Vérifier hasDecorator
|
|
99
|
+
if (classPattern.hasDecorator) {
|
|
100
|
+
const decorators = current.getDecorators();
|
|
101
|
+
const decoratorNames = decorators.map(d => {
|
|
102
|
+
const expr = d.getExpression();
|
|
103
|
+
if (ts_morph_1.Node.isCallExpression(expr)) {
|
|
104
|
+
return expr.getExpression().getText();
|
|
105
|
+
}
|
|
106
|
+
return expr.getText();
|
|
107
|
+
});
|
|
108
|
+
const hasRequiredDecorator = classPattern.hasDecorator.some((name) => decoratorNames.includes(name));
|
|
109
|
+
if (!hasRequiredDecorator) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return matchesAstPatternFn(current, classPattern);
|
|
114
|
+
}
|
|
115
|
+
current = current.getParent();
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Vérifie un ancêtre dans l'arbre AST
|
|
121
|
+
*/
|
|
122
|
+
function matchesAncestor(node, ancestorPattern, matchesAstPatternFn) {
|
|
123
|
+
let current = node.getParent();
|
|
124
|
+
while (current) {
|
|
125
|
+
if (matchesAstPatternFn(current, ancestorPattern)) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
current = current.getParent();
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Vérifie si le nœud implémente une interface
|
|
134
|
+
*/
|
|
135
|
+
function matchesImplements(node, interfaceName) {
|
|
136
|
+
if (!ts_morph_1.Node.isClassDeclaration(node)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const implementsClauses = node.getImplements();
|
|
140
|
+
return implementsClauses.some(clause => {
|
|
141
|
+
const text = clause.getText();
|
|
142
|
+
// Matcher l'interface avec ou sans génériques (ex: "Resolve" matche "Resolve<User>")
|
|
143
|
+
return text === interfaceName || text.startsWith(interfaceName + '<');
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Vérifie si le nœud implémente une interface générique manquante
|
|
148
|
+
*/
|
|
149
|
+
function matchesImplementsGeneric(node, implementsGenericPattern) {
|
|
150
|
+
if (!ts_morph_1.Node.isClassDeclaration(node)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const implementsClauses = node.getImplements();
|
|
154
|
+
if (implementsGenericPattern.missing) {
|
|
155
|
+
const missingType = implementsGenericPattern.missing;
|
|
156
|
+
// Vérifier qu'aucune clause implements ne contient le type manquant dans ses génériques
|
|
157
|
+
const hasGeneric = implementsClauses.some(clause => {
|
|
158
|
+
const text = clause.getText();
|
|
159
|
+
// Vérifier si le type manquant est présent dans la partie générique (entre < et >)
|
|
160
|
+
const genericMatch = text.match(/<([^>]+)>/);
|
|
161
|
+
if (genericMatch) {
|
|
162
|
+
const genericsText = genericMatch[1];
|
|
163
|
+
// Vérifier si le type manquant apparaît dans les génériques (avec ou sans espaces)
|
|
164
|
+
return genericsText.includes(missingType);
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
});
|
|
168
|
+
// On veut que le type soit absent, donc on retourne true si hasGeneric est false
|
|
169
|
+
return !hasGeneric;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesModuleSpecifier = matchesModuleSpecifier;
|
|
4
|
+
exports.matchesNamedImports = matchesNamedImports;
|
|
5
|
+
const ts_morph_1 = require("ts-morph");
|
|
6
|
+
/**
|
|
7
|
+
* Vérifie le module specifier d'une ImportDeclaration
|
|
8
|
+
*/
|
|
9
|
+
function matchesModuleSpecifier(node, moduleSpecifier) {
|
|
10
|
+
if (!ts_morph_1.Node.isImportDeclaration(node)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const moduleSpec = node.getModuleSpecifierValue();
|
|
14
|
+
return moduleSpec === moduleSpecifier;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Vérifie les named imports (contains, containsAny)
|
|
18
|
+
*/
|
|
19
|
+
function matchesNamedImports(node, namedImports) {
|
|
20
|
+
if (!ts_morph_1.Node.isImportDeclaration(node)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const importClause = node.getImportClause();
|
|
24
|
+
if (!importClause) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const namedBindings = importClause.getNamedBindings();
|
|
28
|
+
if (!namedBindings || !ts_morph_1.Node.isNamedImports(namedBindings)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const importNames = namedBindings.getElements().map(el => el.getName());
|
|
32
|
+
if (namedImports.contains) {
|
|
33
|
+
return importNames.includes(namedImports.contains);
|
|
34
|
+
}
|
|
35
|
+
if (namedImports.containsAny) {
|
|
36
|
+
return namedImports.containsAny.some(name => importNames.includes(name));
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Index centralisé des matchers TypeScript AST
|
|
4
|
+
* Regroupe tous les matchers pour analyse de code TypeScript
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.clearContextCaches = exports.FileMatcher = exports.CollectionMatcher = exports.TypeMatcher = exports.ExpressionMatcher = exports.HierarchyMatcher = exports.DecoratorMatcher = exports.ImportMatcher = exports.ContextMatcher = exports.NodeMatcher = void 0;
|
|
41
|
+
// Export all TypeScript matchers
|
|
42
|
+
exports.NodeMatcher = __importStar(require("./node-matcher"));
|
|
43
|
+
exports.ContextMatcher = __importStar(require("./context-matcher"));
|
|
44
|
+
exports.ImportMatcher = __importStar(require("./import-matcher"));
|
|
45
|
+
exports.DecoratorMatcher = __importStar(require("./decorator-matcher"));
|
|
46
|
+
exports.HierarchyMatcher = __importStar(require("./hierarchy-matcher"));
|
|
47
|
+
exports.ExpressionMatcher = __importStar(require("./expression-matcher"));
|
|
48
|
+
exports.TypeMatcher = __importStar(require("./type-matcher"));
|
|
49
|
+
exports.CollectionMatcher = __importStar(require("./collection-matcher"));
|
|
50
|
+
exports.FileMatcher = __importStar(require("./file-matcher"));
|
|
51
|
+
// Export clearContextCaches pour gestion mémoire
|
|
52
|
+
var context_matcher_1 = require("./context-matcher");
|
|
53
|
+
Object.defineProperty(exports, "clearContextCaches", { enumerable: true, get: function () { return context_matcher_1.clearContextCaches; } });
|