@silvestv/migration-planificator 6.0.0 → 6.0.2
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/dist/src/core/app-analyzer.js +10 -2
- package/dist/src/core/ast/matchers/html/html-template-ref-collector.js +95 -0
- package/dist/src/core/ast/matchers/html/html-text-matcher.js +85 -32
- package/dist/src/core/ast/matchers/html/index.js +16 -10
- package/dist/src/core/ast/matchers/index.js +44 -0
- package/dist/src/core/ast/matchers/ts/collection-matcher.js +4 -0
- package/dist/src/core/ast/matchers/ts/context-matcher.js +7 -4
- package/dist/src/core/ast/matchers/ts/decorator-matcher.js +145 -144
- package/dist/src/core/ast/matchers/ts/file-matcher.js +199 -8
- package/dist/src/core/ast/matchers/ts/host-binding-property-matcher.js +327 -0
- package/dist/src/core/ast/matchers/ts/mutation-matcher.js +248 -0
- package/dist/src/core/ast/matchers/ts/node-matcher.js +17 -1
- package/dist/src/core/ast/matchers/ts/symbol-matcher.js +98 -2
- package/dist/src/core/ast/matchers/ts/type-matcher.js +5 -2
- package/dist/src/core/ast/matchers/utils/matcher-helpers.js +60 -0
- package/dist/src/core/ast/matchers/utils/template-cache.js +50 -0
- package/dist/src/core/project-detector.js +22 -10
- package/dist/src/core/project-strategy/nx-strategy.js +25 -22
- package/dist/src/data/angular-migration-rules.json +32 -57
- package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +30 -0
- package/dist/src/data/rules/to18/rules-18-obligatoire.json +8 -14
- package/dist/src/data/rules/to18/rules-18-optionnelle.json +0 -56
- package/dist/src/data/rules/to18/rules-18-recommande.json +32 -139
- package/dist/src/data/rules/to19/rules-19-obligatoire.json +4 -1
- package/dist/src/data/rules/to19/rules-19-optionnelle.json +3 -0
- package/dist/src/data/rules/to19/rules-19-recommande.json +30 -2
- package/dist/src/data/rules/to20/rules-20-optionnelle.json +0 -35
- package/dist/src/data/rules/to20/rules-20-recommande.json +44 -36
- package/dist/src/data/rules/to21/rules-21-obligatoire.json +23 -10
- package/package.json +1 -1
|
@@ -97,9 +97,17 @@ function extractPackageDeps(projectPath) {
|
|
|
97
97
|
* Résout le chemin d'un target (app ou lib) selon le type de projet - DRY
|
|
98
98
|
*/
|
|
99
99
|
function resolveTargetPath(targetName, projectPath, isNxMonorepo, nxFolder, angularJson) {
|
|
100
|
-
// Nx : apps/xxx ou libs/xxx
|
|
100
|
+
// Nx : apps/xxx ou libs/xxx (ou src/ pour single-app)
|
|
101
101
|
if (isNxMonorepo) {
|
|
102
|
-
|
|
102
|
+
const standardPath = path.join(projectPath, nxFolder, targetName);
|
|
103
|
+
// Nx single-app : si apps/xxx n'existe pas, utiliser src/
|
|
104
|
+
if (!fs.existsSync(standardPath) && nxFolder === 'apps') {
|
|
105
|
+
const srcPath = path.join(projectPath, 'src');
|
|
106
|
+
if (fs.existsSync(srcPath)) {
|
|
107
|
+
return srcPath;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return standardPath;
|
|
103
111
|
}
|
|
104
112
|
// Angular CLI : lire depuis angular.json si disponible
|
|
105
113
|
const projectConfig = angularJson?.projects?.[targetName];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectTemplateReferences = collectTemplateReferences;
|
|
4
|
+
const DEFER_SUB_BLOCKS = ['mainBlock', 'placeholder', 'loading', 'error'];
|
|
5
|
+
/**
|
|
6
|
+
* Collecte toutes les template reference variables (#xxx) d'un template HTML
|
|
7
|
+
* Parcourt récursivement l'AST pour extraire les noms de références
|
|
8
|
+
*/
|
|
9
|
+
function collectTemplateReferences(nodes) {
|
|
10
|
+
const references = new Set();
|
|
11
|
+
traverse(nodes, references);
|
|
12
|
+
return references;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parcourt récursivement les noeuds HTML pour collecter les références
|
|
16
|
+
*/
|
|
17
|
+
function traverse(nodeList, references) {
|
|
18
|
+
for (const node of nodeList) {
|
|
19
|
+
if (!node || typeof node !== 'object')
|
|
20
|
+
continue;
|
|
21
|
+
const anyNode = node;
|
|
22
|
+
// Element nodes ont un tableau references contenant les #xxx
|
|
23
|
+
extractReferencesFromNode(anyNode, references);
|
|
24
|
+
// Parcourir récursivement les enfants
|
|
25
|
+
traverseChildren(anyNode, references);
|
|
26
|
+
traverseBranches(anyNode, references);
|
|
27
|
+
traverseCases(anyNode, references);
|
|
28
|
+
traverseEmpty(anyNode, references);
|
|
29
|
+
traverseDeferBlocks(anyNode, references);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extrait les références (#xxx) d'un noeud Element
|
|
34
|
+
*/
|
|
35
|
+
function extractReferencesFromNode(node, references) {
|
|
36
|
+
if (!Array.isArray(node.references))
|
|
37
|
+
return;
|
|
38
|
+
for (const ref of node.references) {
|
|
39
|
+
if (ref && typeof ref === 'object' && 'name' in ref && typeof ref.name === 'string') {
|
|
40
|
+
references.add(ref.name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parcourt les enfants directs d'un noeud
|
|
46
|
+
*/
|
|
47
|
+
function traverseChildren(node, references) {
|
|
48
|
+
if (Array.isArray(node.children)) {
|
|
49
|
+
traverse(node.children, references);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parcourt les branches (@if, @else if, @else)
|
|
54
|
+
*/
|
|
55
|
+
function traverseBranches(node, references) {
|
|
56
|
+
if (!Array.isArray(node.branches))
|
|
57
|
+
return;
|
|
58
|
+
for (const branch of node.branches) {
|
|
59
|
+
if (branch && typeof branch === 'object' && Array.isArray(branch.children)) {
|
|
60
|
+
traverse(branch.children, references);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parcourt les cases (@switch, @case, @default)
|
|
66
|
+
*/
|
|
67
|
+
function traverseCases(node, references) {
|
|
68
|
+
if (!Array.isArray(node.cases))
|
|
69
|
+
return;
|
|
70
|
+
for (const caseNode of node.cases) {
|
|
71
|
+
if (caseNode && typeof caseNode === 'object' && Array.isArray(caseNode.children)) {
|
|
72
|
+
traverse(caseNode.children, references);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Parcourt @for @empty
|
|
78
|
+
*/
|
|
79
|
+
function traverseEmpty(node, references) {
|
|
80
|
+
const empty = node.empty;
|
|
81
|
+
if (empty && Array.isArray(empty.children)) {
|
|
82
|
+
traverse(empty.children, references);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parcourt les sous-blocs @defer (mainBlock, placeholder, loading, error)
|
|
87
|
+
*/
|
|
88
|
+
function traverseDeferBlocks(node, references) {
|
|
89
|
+
for (const blockName of DEFER_SUB_BLOCKS) {
|
|
90
|
+
const block = node[blockName];
|
|
91
|
+
if (block && Array.isArray(block.children)) {
|
|
92
|
+
traverse(block.children, references);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -2,51 +2,104 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.matchesBoundTextPattern = matchesBoundTextPattern;
|
|
4
4
|
const html_parser_1 = require("./html-parser");
|
|
5
|
+
const DEFAULT_TEMPLATE_REF_EXTRACT_PATTERN = '\\bthis\\.(\\w+)';
|
|
5
6
|
/**
|
|
6
7
|
* Matche un pattern BoundText (interpolations {{ ... }})
|
|
8
|
+
* @param node Noeud HTML à vérifier
|
|
9
|
+
* @param pattern Pattern AST à matcher
|
|
10
|
+
* @param htmlContent Contenu HTML complet (pour line numbers)
|
|
11
|
+
* @param templateRefs Set des template references (#xxx) du template (optionnel)
|
|
7
12
|
*/
|
|
8
|
-
function matchesBoundTextPattern(node, pattern, htmlContent) {
|
|
13
|
+
function matchesBoundTextPattern(node, pattern, htmlContent, templateRefs) {
|
|
9
14
|
// Vérifier si c'est bien un BoundText (interpolation) et non un Text simple
|
|
10
|
-
if (!(
|
|
15
|
+
if (!isValidBoundTextNode(node)) {
|
|
11
16
|
return null;
|
|
12
17
|
}
|
|
13
18
|
// Vérifier textMatches si spécifié
|
|
14
19
|
if (!pattern.textMatches) {
|
|
15
20
|
return null;
|
|
16
21
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Pour LiteralPrimitive (mots-clés comme 'in')
|
|
24
|
-
if (expr.value !== undefined && typeof expr.value === 'string') {
|
|
25
|
-
if (textRegex.test(expr.value)) {
|
|
26
|
-
return {
|
|
27
|
-
lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
|
|
28
|
-
matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// Pour VoidExpression (Angular parse 'void' comme opérateur)
|
|
33
|
-
if (expr.constructor && expr.constructor.name === 'VoidExpression') {
|
|
34
|
-
if (textRegex.test('void')) {
|
|
35
|
-
return {
|
|
36
|
-
lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
|
|
37
|
-
matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
22
|
+
const nodeValue = node.value;
|
|
23
|
+
const source = nodeValue.source;
|
|
24
|
+
// Validation templateRefVariable : vérifier que this.xxx est une template ref
|
|
25
|
+
if (pattern.templateRefVariable) {
|
|
26
|
+
if (!isValidTemplateRefMatch(source, pattern, templateRefs)) {
|
|
27
|
+
return null;
|
|
41
28
|
}
|
|
42
29
|
}
|
|
30
|
+
const textRegex = new RegExp(pattern.textMatches);
|
|
31
|
+
// Tester via AST expressions
|
|
32
|
+
const astMatch = matchViaAstExpressions(nodeValue.ast, textRegex, node, htmlContent);
|
|
33
|
+
if (astMatch) {
|
|
34
|
+
return astMatch;
|
|
35
|
+
}
|
|
43
36
|
// Fallback: tester le source complet
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
if (source && textRegex.test(source)) {
|
|
38
|
+
return {
|
|
39
|
+
lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
|
|
40
|
+
matchedText: source
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Vérifie si le noeud est un BoundText valide avec une value object
|
|
47
|
+
*/
|
|
48
|
+
function isValidBoundTextNode(node) {
|
|
49
|
+
if (!node || typeof node !== 'object')
|
|
50
|
+
return false;
|
|
51
|
+
const nodeObj = node;
|
|
52
|
+
return 'value' in nodeObj && nodeObj.value !== null && typeof nodeObj.value === 'object';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Vérifie que this.xxx référence bien une template reference variable
|
|
56
|
+
* Retourne true si la validation passe (match autorisé), false sinon
|
|
57
|
+
*/
|
|
58
|
+
function isValidTemplateRefMatch(source, pattern, templateRefs) {
|
|
59
|
+
// Si pas de templateRefs fourni, on ne peut pas valider -> bloquer le match
|
|
60
|
+
if (!templateRefs || !source) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const extractPattern = pattern.templateRefVariable?.extractPattern || DEFAULT_TEMPLATE_REF_EXTRACT_PATTERN;
|
|
64
|
+
const extractRegex = new RegExp(extractPattern);
|
|
65
|
+
const match = extractRegex.exec(source);
|
|
66
|
+
// Si pas de this.xxx trouvé ou variable pas dans les template refs -> pas de match
|
|
67
|
+
if (!match || !match[1]) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const varName = match[1];
|
|
71
|
+
return templateRefs.has(varName);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Tente de matcher via les expressions AST (LiteralPrimitive, VoidExpression)
|
|
75
|
+
*/
|
|
76
|
+
function matchViaAstExpressions(ast, textRegex, node, htmlContent) {
|
|
77
|
+
if (!ast || typeof ast !== 'object')
|
|
78
|
+
return null;
|
|
79
|
+
const astObj = ast;
|
|
80
|
+
if (!astObj.expressions || !Array.isArray(astObj.expressions))
|
|
81
|
+
return null;
|
|
82
|
+
for (const expr of astObj.expressions) {
|
|
83
|
+
if (!expr || typeof expr !== 'object')
|
|
84
|
+
continue;
|
|
85
|
+
const exprObj = expr;
|
|
86
|
+
// Pour LiteralPrimitive (mots-clés comme 'in')
|
|
87
|
+
if (exprObj.value !== undefined && typeof exprObj.value === 'string') {
|
|
88
|
+
if (textRegex.test(exprObj.value)) {
|
|
89
|
+
return {
|
|
90
|
+
lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
|
|
91
|
+
matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Pour VoidExpression (Angular parse 'void' comme opérateur)
|
|
96
|
+
if (exprObj.constructor?.name === 'VoidExpression') {
|
|
97
|
+
if (textRegex.test('void')) {
|
|
98
|
+
return {
|
|
99
|
+
lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
|
|
100
|
+
matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
50
103
|
}
|
|
51
104
|
}
|
|
52
105
|
return null;
|
|
@@ -9,14 +9,15 @@ const html_attribute_matcher_1 = require("./html-attribute-matcher");
|
|
|
9
9
|
const html_element_matcher_1 = require("./html-element-matcher");
|
|
10
10
|
const html_text_matcher_1 = require("./html-text-matcher");
|
|
11
11
|
const html_pipe_matcher_1 = require("./html-pipe-matcher");
|
|
12
|
+
const html_template_ref_collector_1 = require("./html-template-ref-collector");
|
|
12
13
|
/**
|
|
13
14
|
* Vérifie si un nœud HTML correspond à un pattern AST HTML
|
|
14
15
|
* Supporte : Attribute, BoundAttribute, PipeExpression, BoundText, Element, DeferredBlock
|
|
15
16
|
*/
|
|
16
|
-
function matchesHtmlNode(node, pattern, htmlContent) {
|
|
17
|
+
function matchesHtmlNode(node, pattern, htmlContent, templateRefs) {
|
|
17
18
|
// Pattern: BoundText (interpolations {{ ... }})
|
|
18
19
|
if (pattern.nodeType === 'BoundText') {
|
|
19
|
-
return (0, html_text_matcher_1.matchesBoundTextPattern)(node, pattern, htmlContent);
|
|
20
|
+
return (0, html_text_matcher_1.matchesBoundTextPattern)(node, pattern, htmlContent, templateRefs);
|
|
20
21
|
}
|
|
21
22
|
// Pattern: Attribute (*ngIf, *ngFor, etc.) ou BoundAttribute ([ngClass], [ngStyle], etc.)
|
|
22
23
|
if ((pattern.nodeType === 'Attribute' || pattern.nodeType === 'BoundAttribute') && (0, html_parser_1.isElement)(node)) {
|
|
@@ -39,10 +40,10 @@ function matchesHtmlNode(node, pattern, htmlContent) {
|
|
|
39
40
|
/**
|
|
40
41
|
* Parcourt récursivement l'AST HTML et trouve les matches
|
|
41
42
|
*/
|
|
42
|
-
function traverseHtmlAst(nodes, pattern, htmlContent, matches) {
|
|
43
|
+
function traverseHtmlAst(nodes, pattern, htmlContent, matches, templateRefs) {
|
|
43
44
|
for (const node of nodes) {
|
|
44
45
|
// Vérifier si le nœud matche
|
|
45
|
-
const match = matchesHtmlNode(node, pattern, htmlContent);
|
|
46
|
+
const match = matchesHtmlNode(node, pattern, htmlContent, templateRefs);
|
|
46
47
|
if (match) {
|
|
47
48
|
matches.push(match);
|
|
48
49
|
}
|
|
@@ -79,13 +80,13 @@ function traverseHtmlAst(nodes, pattern, htmlContent, matches) {
|
|
|
79
80
|
}
|
|
80
81
|
// 1. Parcourir les enfants standards
|
|
81
82
|
if ('children' in node && Array.isArray(node.children)) {
|
|
82
|
-
traverseHtmlAst(node.children, pattern, htmlContent, matches);
|
|
83
|
+
traverseHtmlAst(node.children, pattern, htmlContent, matches, templateRefs);
|
|
83
84
|
}
|
|
84
85
|
// 2. Parcourir les branches (@if, @else if, @else)
|
|
85
86
|
if ('branches' in node && Array.isArray(node.branches)) {
|
|
86
87
|
for (const branch of node.branches) {
|
|
87
88
|
if (branch.children && Array.isArray(branch.children)) {
|
|
88
|
-
traverseHtmlAst(branch.children, pattern, htmlContent, matches);
|
|
89
|
+
traverseHtmlAst(branch.children, pattern, htmlContent, matches, templateRefs);
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -93,13 +94,13 @@ function traverseHtmlAst(nodes, pattern, htmlContent, matches) {
|
|
|
93
94
|
if ('cases' in node && Array.isArray(node.cases)) {
|
|
94
95
|
for (const caseNode of node.cases) {
|
|
95
96
|
if (caseNode.children && Array.isArray(caseNode.children)) {
|
|
96
|
-
traverseHtmlAst(caseNode.children, pattern, htmlContent, matches);
|
|
97
|
+
traverseHtmlAst(caseNode.children, pattern, htmlContent, matches, templateRefs);
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
// 4. Parcourir @for @empty
|
|
101
102
|
if ('empty' in node && node.empty?.children) {
|
|
102
|
-
traverseHtmlAst(node.empty.children, pattern, htmlContent, matches);
|
|
103
|
+
traverseHtmlAst(node.empty.children, pattern, htmlContent, matches, templateRefs);
|
|
103
104
|
}
|
|
104
105
|
// 4b. Parcourir @for expression (for PipeExpression like | keyvalue)
|
|
105
106
|
if (pattern.nodeType === 'PipeExpression' && 'expression' in node && node.expression) {
|
|
@@ -116,7 +117,7 @@ function traverseHtmlAst(nodes, pattern, htmlContent, matches) {
|
|
|
116
117
|
const deferSubBlocks = ['mainBlock', 'placeholder', 'loading', 'error'];
|
|
117
118
|
for (const blockName of deferSubBlocks) {
|
|
118
119
|
if (blockName in node && node[blockName]?.children) {
|
|
119
|
-
traverseHtmlAst(node[blockName].children, pattern, htmlContent, matches);
|
|
120
|
+
traverseHtmlAst(node[blockName].children, pattern, htmlContent, matches, templateRefs);
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
}
|
|
@@ -131,8 +132,13 @@ function findHtmlPatternMatches(htmlContent, pattern) {
|
|
|
131
132
|
const matches = [];
|
|
132
133
|
// Parser le template HTML
|
|
133
134
|
const ast = (0, html_parser_1.parseHtmlTemplate)(htmlContent);
|
|
135
|
+
// Collecter les template refs si le pattern utilise templateRefVariable
|
|
136
|
+
let templateRefs;
|
|
137
|
+
if (pattern.templateRefVariable) {
|
|
138
|
+
templateRefs = (0, html_template_ref_collector_1.collectTemplateReferences)(ast);
|
|
139
|
+
}
|
|
134
140
|
// Parcourir l'AST et trouver les matches
|
|
135
|
-
traverseHtmlAst(ast, pattern, htmlContent, matches);
|
|
141
|
+
traverseHtmlAst(ast, pattern, htmlContent, matches, templateRefs);
|
|
136
142
|
return matches;
|
|
137
143
|
}
|
|
138
144
|
/**
|
|
@@ -61,6 +61,8 @@ exports.SymbolMatcher = SymbolMatcher;
|
|
|
61
61
|
const HtmlMatcher = __importStar(require("./html"));
|
|
62
62
|
exports.HtmlMatcher = HtmlMatcher;
|
|
63
63
|
const html_pipe_variable_matcher_1 = require("./html/html-pipe-variable-matcher");
|
|
64
|
+
const host_binding_property_matcher_1 = require("./ts/host-binding-property-matcher");
|
|
65
|
+
const mutation_matcher_1 = require("./ts/mutation-matcher");
|
|
64
66
|
/**
|
|
65
67
|
* Fonction principale : vérifie si un nœud correspond à un pattern AST
|
|
66
68
|
* OPTIMISÉ : excludeContext en premier (88% des règles l'utilisent)
|
|
@@ -206,12 +208,25 @@ function matchesAstPattern(node, pattern) {
|
|
|
206
208
|
return false;
|
|
207
209
|
}
|
|
208
210
|
}
|
|
211
|
+
// Vérifier excludeModifiers
|
|
212
|
+
if (pattern.excludeModifiers !== undefined) {
|
|
213
|
+
if (!NodeMatcher.excludesModifiers(node, pattern.excludeModifiers)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
209
217
|
// Vérifier exported
|
|
210
218
|
if (pattern.exported !== undefined) {
|
|
211
219
|
if (!NodeMatcher.matchesExported(node, pattern.exported)) {
|
|
212
220
|
return false;
|
|
213
221
|
}
|
|
214
222
|
}
|
|
223
|
+
// Vérifier isMutatedInClass (pour PropertyDeclaration)
|
|
224
|
+
if (pattern.isMutatedInClass !== undefined) {
|
|
225
|
+
const mutated = (0, mutation_matcher_1.isMutatedInClass)(node);
|
|
226
|
+
if (mutated !== pattern.isMutatedInClass) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
215
230
|
// Vérifier parameters
|
|
216
231
|
if (pattern.parameters !== undefined) {
|
|
217
232
|
if (!DecoratorMatcher.matchesParameters(node, pattern.parameters)) {
|
|
@@ -377,6 +392,35 @@ function matchesAstPattern(node, pattern) {
|
|
|
377
392
|
return false;
|
|
378
393
|
}
|
|
379
394
|
}
|
|
395
|
+
// Vérifier hostBindingProperty (résolution host binding → TS property → vérification wrappers)
|
|
396
|
+
if (pattern.hostBindingProperty !== undefined) {
|
|
397
|
+
if (!(0, host_binding_property_matcher_1.matchesHostBindingProperty)(node, pattern.hostBindingProperty)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Vérifier fileMissing (identifiant absent de tout le fichier)
|
|
402
|
+
if (pattern.fileMissing !== undefined) {
|
|
403
|
+
const sourceFile = node.getSourceFile();
|
|
404
|
+
if (!FileMatcher.matchesFileMissing(sourceFile, pattern.fileMissing)) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Vérifier projectFileContains (cross-file avec fichiers config)
|
|
409
|
+
if (pattern.projectFileContains !== undefined) {
|
|
410
|
+
const sourceFile = node.getSourceFile();
|
|
411
|
+
if (!FileMatcher.matchesProjectFileContains(sourceFile, pattern.projectFileContains)) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Vérifier hasHostBindingDecorators (@HostBinding, @HostListener, ou host object literal dynamique)
|
|
416
|
+
// Utilise hasAnyHostBindings() de host-binding-property-matcher.ts (DRY)
|
|
417
|
+
if (pattern.hasHostBindingDecorators !== undefined) {
|
|
418
|
+
if (pattern.hasHostBindingDecorators === true) {
|
|
419
|
+
if (!(0, host_binding_property_matcher_1.hasAnyHostBindings)(node)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
380
424
|
return true;
|
|
381
425
|
}
|
|
382
426
|
// Export des fonctions utilitaires de cache
|
|
@@ -38,6 +38,10 @@ function matchesElements(node, elementsPattern, matchesAstPatternFn) {
|
|
|
38
38
|
if (elementsPattern.contains) {
|
|
39
39
|
return elements.some(element => matchesAstPatternFn(element, elementsPattern.contains));
|
|
40
40
|
}
|
|
41
|
+
// Vérifier containsAny (au moins un élément matche l'un des patterns)
|
|
42
|
+
if (elementsPattern.containsAny && Array.isArray(elementsPattern.containsAny)) {
|
|
43
|
+
return elements.some(element => elementsPattern.containsAny.some((pattern) => matchesAstPatternFn(element, pattern)));
|
|
44
|
+
}
|
|
41
45
|
// Cas où elementsPattern est directement un pattern AST
|
|
42
46
|
return elements.some(element => matchesAstPatternFn(element, elementsPattern));
|
|
43
47
|
}
|
|
@@ -190,11 +190,14 @@ function matchesNextProvider(node, nextProviderPattern) {
|
|
|
190
190
|
if (currentIndex === -1) {
|
|
191
191
|
return false;
|
|
192
192
|
}
|
|
193
|
-
// Vérifier missing : le provider ne doit PAS être présent dans
|
|
193
|
+
// Vérifier missing : le provider ne doit PAS être présent dans le tableau entier
|
|
194
194
|
if (nextProviderPattern.missing) {
|
|
195
195
|
const missingProviderName = nextProviderPattern.missing;
|
|
196
|
-
// Vérifier tous les providers après le nœud courant
|
|
197
|
-
for (let i =
|
|
196
|
+
// Vérifier tous les providers du tableau (pas seulement ceux après le nœud courant)
|
|
197
|
+
for (let i = 0; i < elements.length; i++) {
|
|
198
|
+
// Ignorer le nœud courant lui-même
|
|
199
|
+
if (i === currentIndex)
|
|
200
|
+
continue;
|
|
198
201
|
const providerElement = elements[i];
|
|
199
202
|
const providerText = providerElement.getText();
|
|
200
203
|
// Vérifier si le provider manquant est présent
|
|
@@ -203,7 +206,7 @@ function matchesNextProvider(node, nextProviderPattern) {
|
|
|
203
206
|
return false;
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
|
-
// Le provider est bien absent de tous les providers
|
|
209
|
+
// Le provider est bien absent de tous les autres providers
|
|
207
210
|
return true;
|
|
208
211
|
}
|
|
209
212
|
return true;
|