@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,377 @@
|
|
|
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.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.matchesAstPattern = matchesAstPattern;
|
|
38
|
+
const ts_morph_1 = require("ts-morph");
|
|
39
|
+
// Import matchers TypeScript (depuis ts/)
|
|
40
|
+
const NodeMatcher = __importStar(require("./ts/node-matcher"));
|
|
41
|
+
exports.NodeMatcher = NodeMatcher;
|
|
42
|
+
const ContextMatcher = __importStar(require("./ts/context-matcher"));
|
|
43
|
+
exports.ContextMatcher = ContextMatcher;
|
|
44
|
+
const ImportMatcher = __importStar(require("./ts/import-matcher"));
|
|
45
|
+
exports.ImportMatcher = ImportMatcher;
|
|
46
|
+
const DecoratorMatcher = __importStar(require("./ts/decorator-matcher"));
|
|
47
|
+
exports.DecoratorMatcher = DecoratorMatcher;
|
|
48
|
+
const HierarchyMatcher = __importStar(require("./ts/hierarchy-matcher"));
|
|
49
|
+
exports.HierarchyMatcher = HierarchyMatcher;
|
|
50
|
+
const ExpressionMatcher = __importStar(require("./ts/expression-matcher"));
|
|
51
|
+
exports.ExpressionMatcher = ExpressionMatcher;
|
|
52
|
+
const TypeMatcher = __importStar(require("./ts/type-matcher"));
|
|
53
|
+
exports.TypeMatcher = TypeMatcher;
|
|
54
|
+
const CollectionMatcher = __importStar(require("./ts/collection-matcher"));
|
|
55
|
+
exports.CollectionMatcher = CollectionMatcher;
|
|
56
|
+
const FileMatcher = __importStar(require("./ts/file-matcher"));
|
|
57
|
+
exports.FileMatcher = FileMatcher;
|
|
58
|
+
const SymbolMatcher = __importStar(require("./ts/symbol-matcher"));
|
|
59
|
+
exports.SymbolMatcher = SymbolMatcher;
|
|
60
|
+
// Import matchers HTML (depuis html/)
|
|
61
|
+
const HtmlMatcher = __importStar(require("./html"));
|
|
62
|
+
exports.HtmlMatcher = HtmlMatcher;
|
|
63
|
+
/**
|
|
64
|
+
* Fonction principale : vérifie si un nœud correspond à un pattern AST
|
|
65
|
+
* OPTIMISÉ : excludeContext en premier (88% des règles l'utilisent)
|
|
66
|
+
*/
|
|
67
|
+
function matchesAstPattern(node, pattern) {
|
|
68
|
+
// Gestion spéciale des patterns HTML purs (Attribute, PipeExpression)
|
|
69
|
+
if (HtmlMatcher.isHtmlPattern(pattern)) {
|
|
70
|
+
return HtmlMatcher.matchesHtmlPatternInComponent(node, pattern);
|
|
71
|
+
}
|
|
72
|
+
// 1. excludeContext EN PREMIER (88% des règles)
|
|
73
|
+
if (pattern.excludeContext) {
|
|
74
|
+
if (ContextMatcher.isInExcludedContext(node, pattern.excludeContext)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 2. nodeType (rapide, mais souvent déjà filtré par getDescendantsOfKind)
|
|
79
|
+
if (pattern.nodeType) {
|
|
80
|
+
if (!NodeMatcher.matchesNodeType(node, pattern.nodeType)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// 3. Critères légers et fréquents
|
|
85
|
+
if (pattern.name !== undefined) {
|
|
86
|
+
if (!NodeMatcher.matchesName(node, pattern.name)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (pattern.propertyName !== undefined) {
|
|
91
|
+
if (!NodeMatcher.matchesPropertyName(node, pattern.propertyName)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (pattern.functionName !== undefined) {
|
|
96
|
+
if (!NodeMatcher.matchesFunctionName(node, pattern.functionName)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 4. context requis (après excludeContext)
|
|
101
|
+
if (pattern.context) {
|
|
102
|
+
if (!ContextMatcher.matchesContext(node, pattern.context)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Vérifier moduleSpecifier (pour ImportDeclaration)
|
|
107
|
+
if (pattern.moduleSpecifier !== undefined) {
|
|
108
|
+
if (!ImportMatcher.matchesModuleSpecifier(node, pattern.moduleSpecifier)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Vérifier namedImports
|
|
113
|
+
if (pattern.namedImports !== undefined) {
|
|
114
|
+
if (!ImportMatcher.matchesNamedImports(node, pattern.namedImports)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Vérifier les propriétés (pour Decorator, ObjectLiteral)
|
|
119
|
+
if (pattern.properties !== undefined) {
|
|
120
|
+
if (!DecoratorMatcher.matchesProperties(node, pattern.properties, matchesAstPattern)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Vérifier template (pour @Component)
|
|
125
|
+
if (pattern.template !== undefined) {
|
|
126
|
+
if (!DecoratorMatcher.matchesTemplate(node, pattern.template)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Vérifier arguments (pour CallExpression)
|
|
131
|
+
if (pattern.arguments !== undefined) {
|
|
132
|
+
if (!ExpressionMatcher.matchesArguments(node, pattern.arguments, matchesAstPattern)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Vérifier initializer (valeur initiale)
|
|
137
|
+
if (pattern.initializer !== undefined) {
|
|
138
|
+
if (!TypeMatcher.matchesInitializer(node, pattern.initializer, matchesAstPattern)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Vérifier parent
|
|
143
|
+
if (pattern.parent !== undefined) {
|
|
144
|
+
if (!HierarchyMatcher.matchesParent(node, pattern.parent, matchesAstPattern)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Vérifier inClass
|
|
149
|
+
if (pattern.inClass !== undefined) {
|
|
150
|
+
if (!HierarchyMatcher.matchesInClass(node, pattern.inClass, matchesAstPattern)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Vérifier notInClass
|
|
155
|
+
if (pattern.notInClass !== undefined) {
|
|
156
|
+
if (HierarchyMatcher.matchesInClass(node, pattern.notInClass, matchesAstPattern)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Vérifier expression
|
|
161
|
+
if (pattern.expression !== undefined) {
|
|
162
|
+
if (!ExpressionMatcher.matchesExpression(node, pattern.expression, matchesAstPattern)) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Vérifier left (BinaryExpression)
|
|
167
|
+
if (pattern.left !== undefined) {
|
|
168
|
+
if (!ExpressionMatcher.matchesLeft(node, pattern.left, matchesAstPattern)) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Vérifier right (BinaryExpression)
|
|
173
|
+
if (pattern.right !== undefined) {
|
|
174
|
+
if (!ExpressionMatcher.matchesRight(node, pattern.right)) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Vérifier operatorKind
|
|
179
|
+
if (pattern.operatorKind !== undefined) {
|
|
180
|
+
if (!NodeMatcher.matchesOperatorKind(node, pattern.operatorKind)) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Vérifier type
|
|
185
|
+
if (pattern.type !== undefined) {
|
|
186
|
+
if (!TypeMatcher.matchesType(node, pattern.type)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Vérifier returnType
|
|
191
|
+
if (pattern.returnType !== undefined) {
|
|
192
|
+
if (!TypeMatcher.matchesReturnType(node, pattern.returnType)) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Vérifier body
|
|
197
|
+
if (pattern.body !== undefined) {
|
|
198
|
+
if (!ExpressionMatcher.matchesBody(node, pattern.body, matchesAstPattern)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Vérifier modifiers
|
|
203
|
+
if (pattern.modifiers !== undefined) {
|
|
204
|
+
if (!NodeMatcher.matchesModifiers(node, pattern.modifiers)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Vérifier exported
|
|
209
|
+
if (pattern.exported !== undefined) {
|
|
210
|
+
if (!NodeMatcher.matchesExported(node, pattern.exported)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Vérifier parameters
|
|
215
|
+
if (pattern.parameters !== undefined) {
|
|
216
|
+
if (!DecoratorMatcher.matchesParameters(node, pattern.parameters)) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Vérifier elements (ArrayLiteralExpression)
|
|
221
|
+
if (pattern.elements !== undefined) {
|
|
222
|
+
// Cas spécial: referTo dans elements (résolution de symboles cross-file)
|
|
223
|
+
if (typeof pattern.elements === 'object' && 'referTo' in pattern.elements) {
|
|
224
|
+
if (!ts_morph_1.Node.isArrayLiteralExpression(node)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const elements = node.getElements();
|
|
228
|
+
const elementsPattern = pattern.elements;
|
|
229
|
+
const hasMatch = elements.some(element => {
|
|
230
|
+
// Vérifier nodeType si présent dans le pattern elements
|
|
231
|
+
if (elementsPattern.nodeType) {
|
|
232
|
+
if (!NodeMatcher.matchesNodeType(element, elementsPattern.nodeType)) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Vérifier referTo
|
|
237
|
+
return SymbolMatcher.matchesReferTo(element, elementsPattern.referTo);
|
|
238
|
+
});
|
|
239
|
+
if (!hasMatch) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// Logique normale (notEmpty, contains, etc.)
|
|
245
|
+
if (!CollectionMatcher.matchesElements(node, pattern.elements, matchesAstPattern)) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Vérifier ancestor
|
|
251
|
+
if (pattern.ancestor !== undefined) {
|
|
252
|
+
if (!HierarchyMatcher.matchesAncestor(node, pattern.ancestor, matchesAstPattern)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Vérifier chainContains
|
|
257
|
+
if (pattern.chainContains !== undefined) {
|
|
258
|
+
if (!ExpressionMatcher.matchesChainContains(node, pattern.chainContains, matchesAstPattern)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Vérifier containsAny
|
|
263
|
+
if (pattern.containsAny !== undefined) {
|
|
264
|
+
if (!CollectionMatcher.matchesContainsAny(node, pattern.containsAny, matchesAstPattern)) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Vérifier containsAll
|
|
269
|
+
if (pattern.containsAll !== undefined) {
|
|
270
|
+
if (!CollectionMatcher.matchesContainsAll(node, pattern.containsAll, matchesAstPattern)) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Vérifier value
|
|
275
|
+
if (pattern.value !== undefined) {
|
|
276
|
+
if (!NodeMatcher.matchesValue(node, pattern.value)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Vérifier valueMatches
|
|
281
|
+
if (pattern.valueMatches !== undefined) {
|
|
282
|
+
if (!NodeMatcher.matchesValuePattern(node, pattern.valueMatches)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Vérifier text
|
|
287
|
+
if (pattern.text !== undefined) {
|
|
288
|
+
if (!NodeMatcher.matchesText(node, pattern.text)) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Vérifier nextStatement
|
|
293
|
+
if (pattern.nextStatement !== undefined) {
|
|
294
|
+
if (!ContextMatcher.matchesNextStatement(node, pattern.nextStatement)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Vérifier nextProvider
|
|
299
|
+
if (pattern.nextProvider !== undefined) {
|
|
300
|
+
if (!ContextMatcher.matchesNextProvider(node, pattern.nextProvider)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Vérifier implements
|
|
305
|
+
if (pattern.implements !== undefined) {
|
|
306
|
+
if (!HierarchyMatcher.matchesImplements(node, pattern.implements)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Vérifier implementsGeneric
|
|
311
|
+
if (pattern.implementsGeneric !== undefined) {
|
|
312
|
+
if (!HierarchyMatcher.matchesImplementsGeneric(node, pattern.implementsGeneric)) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Vérifier object
|
|
317
|
+
if (pattern.object !== undefined) {
|
|
318
|
+
if (!TypeMatcher.matchesObject(node, pattern.object)) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Vérifier property
|
|
323
|
+
if (pattern.property !== undefined) {
|
|
324
|
+
if (!TypeMatcher.matchesProperty(node, pattern.property)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Vérifier declarations
|
|
329
|
+
if (pattern.declarations !== undefined) {
|
|
330
|
+
if (!DecoratorMatcher.matchesDeclarations(node, pattern.declarations, matchesAstPattern)) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Vérifier pipeName
|
|
335
|
+
if (pattern.pipeName !== undefined) {
|
|
336
|
+
if (!DecoratorMatcher.matchesPipeName(node, pattern.pipeName)) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Vérifier optionalChaining
|
|
341
|
+
if (pattern.optionalChaining !== undefined) {
|
|
342
|
+
if (!DecoratorMatcher.matchesOptionalChaining(node, pattern.optionalChaining)) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Vérifier nonNullAssertion
|
|
347
|
+
if (pattern.nonNullAssertion !== undefined) {
|
|
348
|
+
if (!DecoratorMatcher.matchesNonNullAssertion(node, pattern.nonNullAssertion)) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Vérifier inFile (nom de fichier)
|
|
353
|
+
if (pattern.inFile !== undefined) {
|
|
354
|
+
const sourceFile = node.getSourceFile();
|
|
355
|
+
const filePath = sourceFile.getFilePath();
|
|
356
|
+
if (!FileMatcher.matchesInFilePattern(filePath, pattern.inFile)) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Vérifier testSetup
|
|
361
|
+
if (pattern.testSetup !== undefined) {
|
|
362
|
+
const sourceFile = node.getSourceFile();
|
|
363
|
+
if (!FileMatcher.matchesTestSetup(sourceFile, pattern.testSetup)) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Vérifier referTo (résolution de symboles cross-file au niveau racine)
|
|
368
|
+
if (pattern.referTo !== undefined) {
|
|
369
|
+
if (!SymbolMatcher.matchesReferTo(node, pattern.referTo)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
// Export des fonctions utilitaires de cache
|
|
376
|
+
var decorator_matcher_1 = require("./ts/decorator-matcher");
|
|
377
|
+
Object.defineProperty(exports, "clearTemplateCache", { enumerable: true, get: function () { return decorator_matcher_1.clearTemplateCache; } });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesElements = matchesElements;
|
|
4
|
+
exports.matchesContainsAny = matchesContainsAny;
|
|
5
|
+
exports.matchesContainsAll = matchesContainsAll;
|
|
6
|
+
const ts_morph_1 = require("ts-morph");
|
|
7
|
+
/**
|
|
8
|
+
* Vérifie les éléments d'un tableau (ArrayLiteralExpression)
|
|
9
|
+
*/
|
|
10
|
+
function matchesElements(node, elementsPattern, matchesAstPatternFn) {
|
|
11
|
+
if (!ts_morph_1.Node.isArrayLiteralExpression(node)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const elements = node.getElements();
|
|
15
|
+
// Vérifier notEmpty (tableau doit avoir au moins un élément)
|
|
16
|
+
if (typeof elementsPattern === 'object' && elementsPattern.notEmpty === true) {
|
|
17
|
+
return elements.length > 0;
|
|
18
|
+
}
|
|
19
|
+
// Vérifier contains (au moins un élément matche le pattern)
|
|
20
|
+
if (elementsPattern.contains) {
|
|
21
|
+
return elements.some(element => matchesAstPatternFn(element, elementsPattern.contains));
|
|
22
|
+
}
|
|
23
|
+
// Cas où elementsPattern est directement un pattern AST
|
|
24
|
+
return elements.some(element => matchesAstPatternFn(element, elementsPattern));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Vérifie qu'au moins un sous-pattern matche (containsAny)
|
|
28
|
+
*/
|
|
29
|
+
function matchesContainsAny(node, patterns, matchesAstPatternFn) {
|
|
30
|
+
let hasMatch = false;
|
|
31
|
+
node.forEachDescendant((child) => {
|
|
32
|
+
if (patterns.some(pattern => matchesAstPatternFn(child, pattern))) {
|
|
33
|
+
hasMatch = true;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return hasMatch;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Vérifie que tous les sous-patterns matchent (containsAll)
|
|
40
|
+
*/
|
|
41
|
+
function matchesContainsAll(node, patterns, matchesAstPatternFn) {
|
|
42
|
+
const matches = new Set();
|
|
43
|
+
node.forEachDescendant((child) => {
|
|
44
|
+
patterns.forEach((pattern, index) => {
|
|
45
|
+
if (matchesAstPatternFn(child, pattern)) {
|
|
46
|
+
matches.add(index);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return matches.size === patterns.length;
|
|
51
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clearContextCaches = clearContextCaches;
|
|
4
|
+
exports.isInExcludedContext = isInExcludedContext;
|
|
5
|
+
exports.matchesNextStatement = matchesNextStatement;
|
|
6
|
+
exports.matchesNextProvider = matchesNextProvider;
|
|
7
|
+
exports.matchesContext = matchesContext;
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
/**
|
|
10
|
+
* Cache pour les résultats de vérification excludeContext
|
|
11
|
+
* Évite de refaire les mêmes traversals coûteux
|
|
12
|
+
*/
|
|
13
|
+
let excludeContextCache = new WeakMap();
|
|
14
|
+
/**
|
|
15
|
+
* Cache des comment ranges par fichier (calculé une seule fois)
|
|
16
|
+
*/
|
|
17
|
+
let commentRangesCache = new WeakMap();
|
|
18
|
+
/**
|
|
19
|
+
* Vide les caches pour libérer la mémoire entre les batches
|
|
20
|
+
* CRITIQUE : Appeler après removeSourceFile() pour forcer GC immédiat
|
|
21
|
+
*/
|
|
22
|
+
function clearContextCaches() {
|
|
23
|
+
excludeContextCache = new WeakMap();
|
|
24
|
+
commentRangesCache = new WeakMap();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Récupère les comment ranges d'un fichier (avec cache)
|
|
28
|
+
*/
|
|
29
|
+
function getCommentRanges(sourceFile) {
|
|
30
|
+
let cached = commentRangesCache.get(sourceFile);
|
|
31
|
+
if (cached) {
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
const leadingComments = sourceFile.getLeadingCommentRanges() || [];
|
|
35
|
+
const trailingComments = sourceFile.getTrailingCommentRanges() || [];
|
|
36
|
+
cached = [...leadingComments, ...trailingComments].map(range => ({
|
|
37
|
+
pos: range.getPos(),
|
|
38
|
+
end: range.getEnd()
|
|
39
|
+
}));
|
|
40
|
+
commentRangesCache.set(sourceFile, cached);
|
|
41
|
+
return cached;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Vérifie si le nœud est dans un commentaire (optimisé avec cache)
|
|
45
|
+
*/
|
|
46
|
+
function isNodeInComment(node) {
|
|
47
|
+
const sourceFile = node.getSourceFile();
|
|
48
|
+
const start = node.getStart();
|
|
49
|
+
const commentRanges = getCommentRanges(sourceFile);
|
|
50
|
+
return commentRanges.some(range => start >= range.pos && start <= range.end);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Vérifie si le nœud est dans un StringLiteral (optimisé, limite profondeur)
|
|
54
|
+
*/
|
|
55
|
+
function isNodeInStringLiteral(node) {
|
|
56
|
+
let parent = node.getParent();
|
|
57
|
+
let depth = 0;
|
|
58
|
+
// Augmenté à 20 pour gérer les objets profondément imbriqués (Nx, configs complexes)
|
|
59
|
+
// Exemple: const config = { a: { b: { c: { d: "string" } } } } → profondeur 12+
|
|
60
|
+
const MAX_DEPTH = 20;
|
|
61
|
+
while (parent && depth < MAX_DEPTH) {
|
|
62
|
+
if (ts_morph_1.Node.isStringLiteral(parent)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
// Optimisation: arrêter si on atteint un SourceFile
|
|
66
|
+
if (ts_morph_1.Node.isSourceFile(parent)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
parent = parent.getParent();
|
|
70
|
+
depth++;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Vérifie si le nœud est dans un ImportSpecifier (optimisé, limite profondeur)
|
|
76
|
+
*/
|
|
77
|
+
function isNodeInImportSpecifier(node) {
|
|
78
|
+
let parent = node.getParent();
|
|
79
|
+
let depth = 0;
|
|
80
|
+
// Augmenté à 10 pour cohérence (ImportSpecifier est généralement à profondeur 3-4)
|
|
81
|
+
const MAX_DEPTH = 10;
|
|
82
|
+
while (parent && depth < MAX_DEPTH) {
|
|
83
|
+
if (ts_morph_1.Node.isImportSpecifier(parent)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (ts_morph_1.Node.isSourceFile(parent)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
parent = parent.getParent();
|
|
90
|
+
depth++;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Vérifie si le nœud est dans un contexte exclu (StringLiteral, Comment)
|
|
96
|
+
* OPTIMISÉ avec cache et early exit
|
|
97
|
+
*/
|
|
98
|
+
function isInExcludedContext(node, excludeContext) {
|
|
99
|
+
// Vérifier le cache
|
|
100
|
+
const cached = excludeContextCache.get(node);
|
|
101
|
+
if (cached) {
|
|
102
|
+
return excludeContext.some(ctx => cached.has(ctx));
|
|
103
|
+
}
|
|
104
|
+
// Calculer les contextes (seulement ceux demandés)
|
|
105
|
+
const contexts = new Set();
|
|
106
|
+
// Vérifier seulement les contextes demandés (optimisation)
|
|
107
|
+
if (excludeContext.includes('StringLiteral')) {
|
|
108
|
+
if (isNodeInStringLiteral(node)) {
|
|
109
|
+
contexts.add('StringLiteral');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (excludeContext.includes('Comment')) {
|
|
113
|
+
if (isNodeInComment(node)) {
|
|
114
|
+
contexts.add('Comment');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (excludeContext.includes('ImportSpecifier')) {
|
|
118
|
+
if (isNodeInImportSpecifier(node)) {
|
|
119
|
+
contexts.add('ImportSpecifier');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Mettre en cache
|
|
123
|
+
excludeContextCache.set(node, contexts);
|
|
124
|
+
// Vérifier si au moins un contexte exclu est présent
|
|
125
|
+
return excludeContext.some(ctx => contexts.has(ctx));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Vérifie la prochaine instruction (nextStatement)
|
|
129
|
+
*/
|
|
130
|
+
function matchesNextStatement(node, nextStatementPattern) {
|
|
131
|
+
// Trouver le statement parent
|
|
132
|
+
let currentStatement = node;
|
|
133
|
+
while (currentStatement && !ts_morph_1.Node.isStatement(currentStatement)) {
|
|
134
|
+
currentStatement = currentStatement.getParent();
|
|
135
|
+
}
|
|
136
|
+
if (!currentStatement) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const parent = currentStatement.getParent();
|
|
140
|
+
if (!parent) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
let statements = [];
|
|
144
|
+
// Récupérer les statements selon le type de parent
|
|
145
|
+
if (ts_morph_1.Node.isBlock(parent)) {
|
|
146
|
+
statements = parent.getStatements();
|
|
147
|
+
}
|
|
148
|
+
else if (ts_morph_1.Node.isSourceFile(parent)) {
|
|
149
|
+
statements = parent.getStatements();
|
|
150
|
+
}
|
|
151
|
+
else if (ts_morph_1.Node.isCaseClause(parent) || ts_morph_1.Node.isDefaultClause(parent)) {
|
|
152
|
+
statements = parent.getStatements();
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const currentIndex = statements.indexOf(currentStatement);
|
|
158
|
+
if (currentIndex === -1) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
// Vérifier notContains : matcher si le pattern est absent du statement suivant
|
|
162
|
+
if (nextStatementPattern.notContains) {
|
|
163
|
+
// Si pas de statement suivant, le pattern est bien absent → match
|
|
164
|
+
if (currentIndex === statements.length - 1) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
const nextStatement = statements[currentIndex + 1];
|
|
168
|
+
const nextText = nextStatement.getText();
|
|
169
|
+
// Vérifier si au moins un des patterns est présent dans le statement suivant
|
|
170
|
+
const hasAny = nextStatementPattern.notContains.some((pattern) => nextText.includes(pattern));
|
|
171
|
+
// Matcher si AUCUN pattern n'est présent
|
|
172
|
+
return !hasAny;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Vérifie le provider suivant (nextProvider)
|
|
178
|
+
*/
|
|
179
|
+
function matchesNextProvider(node, nextProviderPattern) {
|
|
180
|
+
// Trouver le tableau de providers parent
|
|
181
|
+
let parent = node.getParent();
|
|
182
|
+
while (parent && !ts_morph_1.Node.isArrayLiteralExpression(parent)) {
|
|
183
|
+
parent = parent.getParent();
|
|
184
|
+
}
|
|
185
|
+
if (!parent || !ts_morph_1.Node.isArrayLiteralExpression(parent)) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const elements = parent.getElements();
|
|
189
|
+
const currentIndex = elements.indexOf(node);
|
|
190
|
+
if (currentIndex === -1) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
// Vérifier missing : le provider ne doit PAS être présent dans les éléments restants
|
|
194
|
+
if (nextProviderPattern.missing) {
|
|
195
|
+
const missingProviderName = nextProviderPattern.missing;
|
|
196
|
+
// Vérifier tous les providers après le nœud courant
|
|
197
|
+
for (let i = currentIndex + 1; i < elements.length; i++) {
|
|
198
|
+
const providerElement = elements[i];
|
|
199
|
+
const providerText = providerElement.getText();
|
|
200
|
+
// Vérifier si le provider manquant est présent
|
|
201
|
+
if (providerText.includes(missingProviderName)) {
|
|
202
|
+
// Le provider est présent, donc le pattern ne matche PAS
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Le provider est bien absent de tous les providers restants
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Vérifie le contexte requis (context)
|
|
213
|
+
*/
|
|
214
|
+
function matchesContext(node, context) {
|
|
215
|
+
// Convertir en tableau si c'est une chaîne unique
|
|
216
|
+
const contexts = Array.isArray(context) ? context : [context];
|
|
217
|
+
// Contextes spéciaux personnalisés
|
|
218
|
+
if (contexts.includes('ProvidersArray')) {
|
|
219
|
+
if (isInProvidersArray(node)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Contextes AST standards
|
|
224
|
+
let parent = node.getParent();
|
|
225
|
+
while (parent) {
|
|
226
|
+
const parentKind = parent.getKindName();
|
|
227
|
+
if (contexts.includes(parentKind)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
parent = parent.getParent();
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Vérifie si le nœud est dans un tableau providers d'un @NgModule ou @Component
|
|
236
|
+
*/
|
|
237
|
+
function isInProvidersArray(node) {
|
|
238
|
+
// Vérifier si le nœud est dans un ArrayLiteralExpression
|
|
239
|
+
let parent = node.getParent();
|
|
240
|
+
while (parent) {
|
|
241
|
+
// Si on trouve un ArrayLiteralExpression, vérifier si c'est un tableau providers
|
|
242
|
+
if (ts_morph_1.Node.isArrayLiteralExpression(parent)) {
|
|
243
|
+
const propAssignment = parent.getParent();
|
|
244
|
+
// Vérifier si c'est une PropertyAssignment avec le nom "providers"
|
|
245
|
+
if (propAssignment && ts_morph_1.Node.isPropertyAssignment(propAssignment)) {
|
|
246
|
+
const propName = propAssignment.getName();
|
|
247
|
+
if (propName === 'providers') {
|
|
248
|
+
// Vérifier si la PropertyAssignment est dans un ObjectLiteralExpression
|
|
249
|
+
const objLiteral = propAssignment.getParent();
|
|
250
|
+
if (objLiteral && ts_morph_1.Node.isObjectLiteralExpression(objLiteral)) {
|
|
251
|
+
// Vérifier si l'ObjectLiteralExpression est dans un CallExpression (argument du décorateur)
|
|
252
|
+
const callExpr = objLiteral.getParent();
|
|
253
|
+
if (callExpr && ts_morph_1.Node.isCallExpression(callExpr)) {
|
|
254
|
+
// Vérifier si le CallExpression est dans un Decorator
|
|
255
|
+
const decorator = callExpr.getParent();
|
|
256
|
+
if (decorator && ts_morph_1.Node.isDecorator(decorator)) {
|
|
257
|
+
// Vérifier si le décorateur est @NgModule ou @Component
|
|
258
|
+
const expr = decorator.getExpression();
|
|
259
|
+
if (ts_morph_1.Node.isCallExpression(expr)) {
|
|
260
|
+
const decoratorName = expr.getExpression().getText();
|
|
261
|
+
return decoratorName === 'NgModule' || decoratorName === 'Component';
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// On a trouvé un ArrayLiteralExpression mais ce n'est pas providers
|
|
269
|
+
// Ne pas continuer la recherche plus haut
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
parent = parent.getParent();
|
|
273
|
+
}
|
|
274
|
+
return false;
|
|
275
|
+
}
|