@silvestv/migration-planificator 6.0.1 → 6.0.3
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/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 +68 -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 +485 -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 +66 -0
- package/dist/src/core/ast/matchers/utils/template-cache.js +50 -0
- package/dist/src/core/ast/scanner-ast.js +8 -4
- 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-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
|
@@ -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
|
/**
|
|
@@ -34,6 +34,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.HtmlMatcher = exports.SymbolMatcher = exports.FileMatcher = exports.CollectionMatcher = exports.TypeMatcher = exports.ExpressionMatcher = exports.HierarchyMatcher = exports.DecoratorMatcher = exports.ImportMatcher = exports.ContextMatcher = exports.NodeMatcher = exports.clearTemplateCache = void 0;
|
|
37
|
+
exports.setOverrideNode = setOverrideNode;
|
|
38
|
+
exports.getOverrideNode = getOverrideNode;
|
|
39
|
+
exports.clearOverrideNode = clearOverrideNode;
|
|
37
40
|
exports.matchesAstPattern = matchesAstPattern;
|
|
38
41
|
const ts_morph_1 = require("ts-morph");
|
|
39
42
|
// Import matchers TypeScript (depuis ts/)
|
|
@@ -61,6 +64,26 @@ exports.SymbolMatcher = SymbolMatcher;
|
|
|
61
64
|
const HtmlMatcher = __importStar(require("./html"));
|
|
62
65
|
exports.HtmlMatcher = HtmlMatcher;
|
|
63
66
|
const html_pipe_variable_matcher_1 = require("./html/html-pipe-variable-matcher");
|
|
67
|
+
const host_binding_property_matcher_1 = require("./ts/host-binding-property-matcher");
|
|
68
|
+
const mutation_matcher_1 = require("./ts/mutation-matcher");
|
|
69
|
+
/**
|
|
70
|
+
* Mécanisme "Override Node" pour localisation précise
|
|
71
|
+
* Permet aux matchers de retourner un nœud différent pour l'affichage
|
|
72
|
+
* Exemple: hostBindingProperty retourne le PropertyAssignment au lieu du @Component
|
|
73
|
+
*/
|
|
74
|
+
let currentOverrideNode = null;
|
|
75
|
+
/** Définit le nœud à utiliser pour la localisation (ligne, texte) */
|
|
76
|
+
function setOverrideNode(node) {
|
|
77
|
+
currentOverrideNode = node;
|
|
78
|
+
}
|
|
79
|
+
/** Récupère le nœud override ou null si non défini */
|
|
80
|
+
function getOverrideNode() {
|
|
81
|
+
return currentOverrideNode;
|
|
82
|
+
}
|
|
83
|
+
/** Nettoie le nœud override (à appeler après utilisation) */
|
|
84
|
+
function clearOverrideNode() {
|
|
85
|
+
currentOverrideNode = null;
|
|
86
|
+
}
|
|
64
87
|
/**
|
|
65
88
|
* Fonction principale : vérifie si un nœud correspond à un pattern AST
|
|
66
89
|
* OPTIMISÉ : excludeContext en premier (88% des règles l'utilisent)
|
|
@@ -206,12 +229,25 @@ function matchesAstPattern(node, pattern) {
|
|
|
206
229
|
return false;
|
|
207
230
|
}
|
|
208
231
|
}
|
|
232
|
+
// Vérifier excludeModifiers
|
|
233
|
+
if (pattern.excludeModifiers !== undefined) {
|
|
234
|
+
if (!NodeMatcher.excludesModifiers(node, pattern.excludeModifiers)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
209
238
|
// Vérifier exported
|
|
210
239
|
if (pattern.exported !== undefined) {
|
|
211
240
|
if (!NodeMatcher.matchesExported(node, pattern.exported)) {
|
|
212
241
|
return false;
|
|
213
242
|
}
|
|
214
243
|
}
|
|
244
|
+
// Vérifier isMutatedInClass (pour PropertyDeclaration)
|
|
245
|
+
if (pattern.isMutatedInClass !== undefined) {
|
|
246
|
+
const mutated = (0, mutation_matcher_1.isMutatedInClass)(node);
|
|
247
|
+
if (mutated !== pattern.isMutatedInClass) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
215
251
|
// Vérifier parameters
|
|
216
252
|
if (pattern.parameters !== undefined) {
|
|
217
253
|
if (!DecoratorMatcher.matchesParameters(node, pattern.parameters)) {
|
|
@@ -377,6 +413,38 @@ function matchesAstPattern(node, pattern) {
|
|
|
377
413
|
return false;
|
|
378
414
|
}
|
|
379
415
|
}
|
|
416
|
+
// Vérifier hostBindingProperty (résolution host binding → TS property → vérification wrappers)
|
|
417
|
+
// Note: matchesHostBindingProperty retourne Node | null pour localisation précise
|
|
418
|
+
if (pattern.hostBindingProperty !== undefined) {
|
|
419
|
+
const problematicNode = (0, host_binding_property_matcher_1.matchesHostBindingProperty)(node, pattern.hostBindingProperty);
|
|
420
|
+
if (!problematicNode) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
setOverrideNode(problematicNode);
|
|
424
|
+
}
|
|
425
|
+
// Vérifier fileMissing (identifiant absent de tout le fichier)
|
|
426
|
+
if (pattern.fileMissing !== undefined) {
|
|
427
|
+
const sourceFile = node.getSourceFile();
|
|
428
|
+
if (!FileMatcher.matchesFileMissing(sourceFile, pattern.fileMissing)) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Vérifier projectFileContains (cross-file avec fichiers config)
|
|
433
|
+
if (pattern.projectFileContains !== undefined) {
|
|
434
|
+
const sourceFile = node.getSourceFile();
|
|
435
|
+
if (!FileMatcher.matchesProjectFileContains(sourceFile, pattern.projectFileContains)) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Vérifier hasHostBindingDecorators (@HostBinding, @HostListener, ou host object literal dynamique)
|
|
440
|
+
// Utilise hasAnyHostBindings() de host-binding-property-matcher.ts (DRY)
|
|
441
|
+
if (pattern.hasHostBindingDecorators !== undefined) {
|
|
442
|
+
if (pattern.hasHostBindingDecorators === true) {
|
|
443
|
+
if (!(0, host_binding_property_matcher_1.hasAnyHostBindings)(node)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
380
448
|
return true;
|
|
381
449
|
}
|
|
382
450
|
// 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;
|