@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,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesNodeType = matchesNodeType;
|
|
4
|
+
exports.matchesName = matchesName;
|
|
5
|
+
exports.matchesPropertyName = matchesPropertyName;
|
|
6
|
+
exports.matchesFunctionName = matchesFunctionName;
|
|
7
|
+
exports.matchesValue = matchesValue;
|
|
8
|
+
exports.matchesOperatorKind = matchesOperatorKind;
|
|
9
|
+
exports.matchesModifiers = matchesModifiers;
|
|
10
|
+
exports.matchesExported = matchesExported;
|
|
11
|
+
exports.matchesValuePattern = matchesValuePattern;
|
|
12
|
+
exports.matchesText = matchesText;
|
|
13
|
+
const ts_morph_1 = require("ts-morph");
|
|
14
|
+
const matcher_helpers_1 = require("../utils/matcher-helpers");
|
|
15
|
+
/**
|
|
16
|
+
* Vérifie si le nœud correspond au type demandé
|
|
17
|
+
*/
|
|
18
|
+
function matchesNodeType(node, nodeType) {
|
|
19
|
+
const nodeTypes = (0, matcher_helpers_1.ensureArray)(nodeType);
|
|
20
|
+
const nodeKindName = node.getKindName();
|
|
21
|
+
return nodeTypes.includes(nodeKindName);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Vérifie le nom du nœud (Identifier, Decorator, PropertyAccessExpression, PropertyAssignment, MethodDeclaration, etc.)
|
|
25
|
+
*/
|
|
26
|
+
function matchesName(node, name) {
|
|
27
|
+
const names = (0, matcher_helpers_1.ensureArray)(name);
|
|
28
|
+
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
29
|
+
return names.includes(node.getText());
|
|
30
|
+
}
|
|
31
|
+
if (ts_morph_1.Node.isDecorator(node)) {
|
|
32
|
+
const expression = node.getExpression();
|
|
33
|
+
if (ts_morph_1.Node.isCallExpression(expression)) {
|
|
34
|
+
const decoratorName = expression.getExpression().getText();
|
|
35
|
+
return names.includes(decoratorName);
|
|
36
|
+
}
|
|
37
|
+
return names.includes(expression.getText());
|
|
38
|
+
}
|
|
39
|
+
// Pour PropertyAccessExpression, vérifier le nom de la propriété
|
|
40
|
+
// Cela permet d'utiliser "name" comme alias de "propertyName"
|
|
41
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
42
|
+
return names.includes(node.getName());
|
|
43
|
+
}
|
|
44
|
+
// Pour PropertyAssignment (ex: { redirectTo: '/home' })
|
|
45
|
+
if (ts_morph_1.Node.isPropertyAssignment(node)) {
|
|
46
|
+
return names.includes(node.getName());
|
|
47
|
+
}
|
|
48
|
+
// Pour MethodDeclaration (ex: canActivate() { })
|
|
49
|
+
if (ts_morph_1.Node.isMethodDeclaration(node)) {
|
|
50
|
+
return names.includes(node.getName());
|
|
51
|
+
}
|
|
52
|
+
// Pour VariableDeclaration (ex: const serverRoutes: ServerRoute[] = [])
|
|
53
|
+
if (ts_morph_1.Node.isVariableDeclaration(node)) {
|
|
54
|
+
return names.includes(node.getName());
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Vérifie le nom de propriété (PropertyAccessExpression)
|
|
60
|
+
*/
|
|
61
|
+
function matchesPropertyName(node, propertyName) {
|
|
62
|
+
const propertyNames = (0, matcher_helpers_1.ensureArray)(propertyName);
|
|
63
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
64
|
+
const propName = node.getName();
|
|
65
|
+
return propertyNames.includes(propName);
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Vérifie le nom de fonction (CallExpression)
|
|
71
|
+
*/
|
|
72
|
+
function matchesFunctionName(node, functionName) {
|
|
73
|
+
const functionNames = (0, matcher_helpers_1.ensureArray)(functionName);
|
|
74
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
75
|
+
const expression = node.getExpression();
|
|
76
|
+
const funcName = expression.getText().split('.').pop() || '';
|
|
77
|
+
return functionNames.includes(funcName);
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Vérifie la valeur d'un nœud
|
|
83
|
+
*/
|
|
84
|
+
function matchesValue(node, value) {
|
|
85
|
+
const nodeText = node.getText();
|
|
86
|
+
const expectedValue = (0, matcher_helpers_1.normalizeValue)(value);
|
|
87
|
+
return (0, matcher_helpers_1.matchesTextValue)(nodeText, expectedValue);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Vérifie le type d'opérateur (BinaryExpression)
|
|
91
|
+
*/
|
|
92
|
+
function matchesOperatorKind(node, operatorKind) {
|
|
93
|
+
if (!ts_morph_1.Node.isBinaryExpression(node)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const operator = node.getOperatorToken();
|
|
97
|
+
return operator.getKindName() === operatorKind;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Vérifie les modificateurs (export, public, private, etc.)
|
|
101
|
+
*/
|
|
102
|
+
function matchesModifiers(node, modifiers) {
|
|
103
|
+
if (!('getModifiers' in node) || typeof node.getModifiers !== 'function') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const nodeModifiers = node.getModifiers().map((m) => m.getText());
|
|
107
|
+
return modifiers.every(mod => nodeModifiers.includes(mod));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Vérifie si le nœud est exporté
|
|
111
|
+
*/
|
|
112
|
+
function matchesExported(node, shouldBeExported) {
|
|
113
|
+
// Pour VariableDeclaration, vérifier si le parent VariableStatement est exporté
|
|
114
|
+
if (ts_morph_1.Node.isVariableDeclaration(node)) {
|
|
115
|
+
const parent = node.getParent();
|
|
116
|
+
if (parent && ts_morph_1.Node.isVariableDeclarationList(parent)) {
|
|
117
|
+
const grandParent = parent.getParent();
|
|
118
|
+
if (grandParent && ts_morph_1.Node.isVariableStatement(grandParent)) {
|
|
119
|
+
const isExported = grandParent.isExported();
|
|
120
|
+
return isExported === shouldBeExported;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (!('isExported' in node) || typeof node.isExported !== 'function') {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const isExported = node.isExported();
|
|
129
|
+
return isExported === shouldBeExported;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Vérifie un pattern de valeur avec regex
|
|
133
|
+
*/
|
|
134
|
+
function matchesValuePattern(node, valuePattern) {
|
|
135
|
+
const nodeText = node.getText();
|
|
136
|
+
const regex = new RegExp(valuePattern);
|
|
137
|
+
return regex.test(nodeText);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Vérifie le texte d'un StringLiteral
|
|
141
|
+
*/
|
|
142
|
+
function matchesText(node, textPattern) {
|
|
143
|
+
if (!ts_morph_1.Node.isStringLiteral(node)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const text = node.getLiteralValue();
|
|
147
|
+
// Si c'est une string simple, vérifier l'égalité exacte
|
|
148
|
+
if (typeof textPattern === 'string') {
|
|
149
|
+
return text === textPattern;
|
|
150
|
+
}
|
|
151
|
+
// Si c'est un objet avec startsWith
|
|
152
|
+
if (textPattern.startsWith) {
|
|
153
|
+
return text.startsWith(textPattern.startsWith);
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveModuleSourceFile = resolveModuleSourceFile;
|
|
4
|
+
exports.matchesReferTo = matchesReferTo;
|
|
5
|
+
const ts_morph_1 = require("ts-morph");
|
|
6
|
+
const ast_helpers_1 = require("../../../../utils/core/ast-helpers");
|
|
7
|
+
/**
|
|
8
|
+
* Matcher pour la résolution de symboles cross-file (referTo)
|
|
9
|
+
* Utilise ts-morph getDefinitionNodes() pour suivre les références
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Profondeur maximale pour suivre les références cross-file
|
|
13
|
+
* (ImportSpecifier → ExportSpecifier → ... → ClassDeclaration)
|
|
14
|
+
*
|
|
15
|
+
* Valeur recommandée: 10+ pour projets Nx complexes avec barrel exports multiples
|
|
16
|
+
* Exemple: libs/feature/index.ts → libs/feature/services/index.ts → service.ts
|
|
17
|
+
*/
|
|
18
|
+
const MAX_REFERENCE_DEPTH = 20;
|
|
19
|
+
/**
|
|
20
|
+
* Résout un module specifier (chemin relatif ou absolu) vers un SourceFile
|
|
21
|
+
* Gère les chemins relatifs profonds que ts-morph en mémoire a du mal à résoudre
|
|
22
|
+
* @param moduleSpecifier - Chemin du module (ex: '../../../../libs/shared/services/user.service')
|
|
23
|
+
* @param fromFile - Fichier source qui fait l'import
|
|
24
|
+
* @returns SourceFile résolu ou undefined
|
|
25
|
+
*/
|
|
26
|
+
function resolveModuleSourceFile(moduleSpecifier, fromFile) {
|
|
27
|
+
const project = fromFile.getProject();
|
|
28
|
+
// APPROCHE 1: Essayer getModuleSpecifierSourceFile (fonctionne pour chemins simples)
|
|
29
|
+
const directResolve = fromFile.getImportDeclarations()
|
|
30
|
+
.find(imp => imp.getModuleSpecifierValue() === moduleSpecifier)
|
|
31
|
+
?.getModuleSpecifierSourceFile();
|
|
32
|
+
if (directResolve) {
|
|
33
|
+
return directResolve;
|
|
34
|
+
}
|
|
35
|
+
// APPROCHE 2: Résolution manuelle pour chemins relatifs (en mémoire)
|
|
36
|
+
if (moduleSpecifier.startsWith('.')) {
|
|
37
|
+
const fromPath = fromFile.getFilePath();
|
|
38
|
+
const parts = fromPath.split('/').filter(p => p);
|
|
39
|
+
// Retirer le fichier pour avoir le directory
|
|
40
|
+
parts.pop();
|
|
41
|
+
// Parser le module specifier
|
|
42
|
+
const moduleParts = moduleSpecifier.split('/');
|
|
43
|
+
// Naviguer selon les ../ et ./
|
|
44
|
+
for (const part of moduleParts) {
|
|
45
|
+
if (part === '..') {
|
|
46
|
+
parts.pop();
|
|
47
|
+
}
|
|
48
|
+
else if (part !== '.') {
|
|
49
|
+
parts.push(part);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Reconstruire le chemin
|
|
53
|
+
const resolvedPath = '/' + parts.join('/');
|
|
54
|
+
// Essayer avec différentes extensions
|
|
55
|
+
const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx'];
|
|
56
|
+
for (const ext of extensions) {
|
|
57
|
+
const pathWithExt = resolvedPath + ext;
|
|
58
|
+
const sourceFile = project.getSourceFile(pathWithExt);
|
|
59
|
+
if (sourceFile) {
|
|
60
|
+
return sourceFile;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Dernière tentative: chercher par nom de fichier relatif
|
|
64
|
+
const allFiles = project.getSourceFiles();
|
|
65
|
+
const fileName = parts[parts.length - 1];
|
|
66
|
+
for (const file of allFiles) {
|
|
67
|
+
const filePath = file.getFilePath();
|
|
68
|
+
if (filePath.endsWith('/' + fileName + '.ts') ||
|
|
69
|
+
filePath.endsWith('/' + fileName + '.tsx') ||
|
|
70
|
+
filePath.endsWith('/' + fileName + '/index.ts')) {
|
|
71
|
+
// Vérifier que le chemin complet correspond
|
|
72
|
+
const filePathParts = filePath.split('/').filter(p => p);
|
|
73
|
+
let match = true;
|
|
74
|
+
for (let i = 0; i < parts.length && i < filePathParts.length; i++) {
|
|
75
|
+
if (parts[parts.length - 1 - i] !== filePathParts[filePathParts.length - 1 - i]) {
|
|
76
|
+
match = false;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (match) {
|
|
81
|
+
return file;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Résout récursivement un ImportSpecifier/ExportSpecifier vers sa déclaration finale
|
|
90
|
+
* Suit la chaîne: ImportSpecifier → ExportSpecifier → ... → ClassDeclaration
|
|
91
|
+
*
|
|
92
|
+
* @param node - Nœud de départ (ImportSpecifier ou ExportSpecifier)
|
|
93
|
+
* @param maxDepth - Profondeur maximale de récursion
|
|
94
|
+
* @returns Nœud final résolu ou le nœud d'origine si résolution impossible
|
|
95
|
+
*/
|
|
96
|
+
function resolveSpecifierRecursively(node, maxDepth = MAX_REFERENCE_DEPTH) {
|
|
97
|
+
let targetNode = node;
|
|
98
|
+
let depth = 0;
|
|
99
|
+
while ((0, ast_helpers_1.isImportOrExportSpecifier)(targetNode) && depth < maxDepth) {
|
|
100
|
+
const nextNode = resolveSpecifierOneLevel(targetNode);
|
|
101
|
+
if (!nextNode)
|
|
102
|
+
break;
|
|
103
|
+
targetNode = nextNode;
|
|
104
|
+
depth++;
|
|
105
|
+
}
|
|
106
|
+
return targetNode;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Résout un ImportSpecifier/ExportSpecifier d'un niveau
|
|
110
|
+
* Essaie via getSymbol() d'abord, puis via navigation manuelle du module
|
|
111
|
+
*
|
|
112
|
+
* @param specifier - ImportSpecifier ou ExportSpecifier
|
|
113
|
+
* @returns Prochain nœud dans la chaîne ou undefined
|
|
114
|
+
*/
|
|
115
|
+
function resolveSpecifierOneLevel(specifier) {
|
|
116
|
+
// APPROCHE 1: Via getSymbol() + getAliasedSymbol()
|
|
117
|
+
const symbol = specifier.getSymbol();
|
|
118
|
+
if (symbol) {
|
|
119
|
+
const aliasedSymbol = symbol.getAliasedSymbol() || symbol;
|
|
120
|
+
const declarations = aliasedSymbol.getDeclarations();
|
|
121
|
+
if (declarations.length > 0) {
|
|
122
|
+
return declarations[0];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// APPROCHE 2: Via navigation manuelle du module (barrel exports en mémoire)
|
|
126
|
+
const parentDecl = (0, ast_helpers_1.findImportOrExportDeclaration)(specifier);
|
|
127
|
+
if (!parentDecl)
|
|
128
|
+
return undefined;
|
|
129
|
+
const moduleInfo = (0, ast_helpers_1.extractModuleInfo)(parentDecl, specifier);
|
|
130
|
+
if (!moduleInfo)
|
|
131
|
+
return undefined;
|
|
132
|
+
const resolvedFile = resolveModuleSourceFile(moduleInfo.moduleSpecifier, moduleInfo.sourceFile);
|
|
133
|
+
if (!resolvedFile)
|
|
134
|
+
return undefined;
|
|
135
|
+
const exports = resolvedFile.getExportedDeclarations();
|
|
136
|
+
const exportedNodes = exports.get(moduleInfo.name);
|
|
137
|
+
return exportedNodes && exportedNodes.length > 0 ? exportedNodes[0] : undefined;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Vérifie si un nœud référence un symbole qui correspond au pattern referTo
|
|
141
|
+
* @param node Nœud à vérifier (généralement un Identifier dans un tableau providers)
|
|
142
|
+
* @param referToPattern Pattern de référence (decorator, propertyNotEqual, etc.)
|
|
143
|
+
* @returns true si le symbole référencé matche le pattern
|
|
144
|
+
*/
|
|
145
|
+
function matchesReferTo(node, referToPattern) {
|
|
146
|
+
const identifierNode = extractIdentifierNode(node, referToPattern);
|
|
147
|
+
if (!identifierNode || !ts_morph_1.Node.isIdentifier(identifierNode))
|
|
148
|
+
return false;
|
|
149
|
+
const definitions = identifierNode.getDefinitionNodes();
|
|
150
|
+
if (definitions.length === 0)
|
|
151
|
+
return false;
|
|
152
|
+
return definitions.some((def) => matchesDefinition(def, referToPattern));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extrait le nœud Identifier du nœud donné selon le pattern
|
|
156
|
+
* @param node - Nœud à analyser
|
|
157
|
+
* @param pattern - Pattern avec elementNodeType optionnel
|
|
158
|
+
* @returns Identifier ou undefined
|
|
159
|
+
*/
|
|
160
|
+
function extractIdentifierNode(node, pattern) {
|
|
161
|
+
// Vérifier elementNodeType si spécifié
|
|
162
|
+
if (pattern.elementNodeType) {
|
|
163
|
+
const nodeTypes = Array.isArray(pattern.elementNodeType)
|
|
164
|
+
? pattern.elementNodeType
|
|
165
|
+
: [pattern.elementNodeType];
|
|
166
|
+
const nodeKind = node.getKindName();
|
|
167
|
+
if (!nodeTypes.includes(nodeKind)) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Extraire l'identifier
|
|
172
|
+
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
173
|
+
return node;
|
|
174
|
+
}
|
|
175
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
176
|
+
const expression = node.getExpression();
|
|
177
|
+
if (ts_morph_1.Node.isIdentifier(expression)) {
|
|
178
|
+
return expression;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Vérifie si une définition matche le pattern referTo
|
|
185
|
+
* @param def - Définition à vérifier
|
|
186
|
+
* @param pattern - Pattern referTo
|
|
187
|
+
* @returns true si match
|
|
188
|
+
*/
|
|
189
|
+
function matchesDefinition(def, pattern) {
|
|
190
|
+
const targetNode = resolveToClassDeclaration(def);
|
|
191
|
+
if (!targetNode)
|
|
192
|
+
return false;
|
|
193
|
+
return checkDecoratorMatch(targetNode, pattern);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Résout une définition vers sa ClassDeclaration
|
|
197
|
+
* Gère les ImportSpecifier/ExportSpecifier avec résolution cross-file
|
|
198
|
+
*
|
|
199
|
+
* @param def - Définition (peut être ImportSpecifier, ExportSpecifier ou ClassDeclaration)
|
|
200
|
+
* @returns ClassDeclaration ou undefined
|
|
201
|
+
*/
|
|
202
|
+
function resolveToClassDeclaration(def) {
|
|
203
|
+
let targetNode = def;
|
|
204
|
+
// Si c'est un Import/ExportSpecifier, résoudre récursivement
|
|
205
|
+
if ((0, ast_helpers_1.isImportOrExportSpecifier)(def)) {
|
|
206
|
+
targetNode = resolveSpecifierRecursively(def);
|
|
207
|
+
}
|
|
208
|
+
return findParentClass(targetNode);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Vérifie si la ClassDeclaration a le bon décorateur avec les bonnes propriétés
|
|
212
|
+
* @param classDecl - ClassDeclaration à vérifier
|
|
213
|
+
* @param pattern - Pattern avec decorator et propertyNotEqual
|
|
214
|
+
* @returns true si match
|
|
215
|
+
*/
|
|
216
|
+
function checkDecoratorMatch(classDecl, pattern) {
|
|
217
|
+
const decorators = classDecl.getDecorators();
|
|
218
|
+
const targetDecorator = decorators.find(dec => getDecoratorName(dec) === pattern.decorator);
|
|
219
|
+
if (!targetDecorator)
|
|
220
|
+
return false;
|
|
221
|
+
if (pattern.propertyNotEqual) {
|
|
222
|
+
return checkPropertyNotEqual(targetDecorator, pattern.propertyNotEqual);
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Trouve la classe parente d'une définition
|
|
228
|
+
*/
|
|
229
|
+
function findParentClass(def) {
|
|
230
|
+
if (ts_morph_1.Node.isClassDeclaration(def)) {
|
|
231
|
+
return def;
|
|
232
|
+
}
|
|
233
|
+
let current = def.getParent();
|
|
234
|
+
while (current && !ts_morph_1.Node.isClassDeclaration(current)) {
|
|
235
|
+
current = current.getParent();
|
|
236
|
+
}
|
|
237
|
+
return current;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extrait le nom d'un décorateur
|
|
241
|
+
*/
|
|
242
|
+
function getDecoratorName(decorator) {
|
|
243
|
+
const expr = decorator.getExpression();
|
|
244
|
+
if (ts_morph_1.Node.isCallExpression(expr)) {
|
|
245
|
+
return expr.getExpression().getText();
|
|
246
|
+
}
|
|
247
|
+
if (ts_morph_1.Node.isIdentifier(expr)) {
|
|
248
|
+
return expr.getText();
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Vérifie si la propriété du décorateur ne correspond PAS à la valeur attendue
|
|
254
|
+
* @returns true si la propriété est absente OU différente de la valeur
|
|
255
|
+
*/
|
|
256
|
+
function checkPropertyNotEqual(decorator, propertyNotEqual) {
|
|
257
|
+
const { key, value } = propertyNotEqual;
|
|
258
|
+
const expr = decorator.getExpression();
|
|
259
|
+
// Si le décorateur n'a pas d'arguments, pas de config = match (car != value)
|
|
260
|
+
if (ts_morph_1.Node.isIdentifier(expr)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
if (!ts_morph_1.Node.isCallExpression(expr)) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const args = expr.getArguments();
|
|
267
|
+
if (args.length === 0 || !ts_morph_1.Node.isObjectLiteralExpression(args[0])) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
const configObj = args[0];
|
|
271
|
+
const targetProp = configObj.getProperty(key);
|
|
272
|
+
if (!targetProp || !ts_morph_1.Node.isPropertyAssignment(targetProp)) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
const propValue = targetProp.getInitializer();
|
|
276
|
+
if (!propValue) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
const actualValue = propValue.getText().replace(/['"]/g, '');
|
|
280
|
+
return actualValue !== value;
|
|
281
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesType = matchesType;
|
|
4
|
+
exports.matchesReturnType = matchesReturnType;
|
|
5
|
+
exports.matchesInitializer = matchesInitializer;
|
|
6
|
+
exports.matchesObject = matchesObject;
|
|
7
|
+
exports.matchesProperty = matchesProperty;
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
/**
|
|
10
|
+
* Vérifie le type TypeScript d'un nœud
|
|
11
|
+
*/
|
|
12
|
+
function matchesType(node, typePattern) {
|
|
13
|
+
let typeNode;
|
|
14
|
+
if (ts_morph_1.Node.isPropertyDeclaration(node) || ts_morph_1.Node.isParameterDeclaration(node)) {
|
|
15
|
+
typeNode = node.getTypeNode();
|
|
16
|
+
}
|
|
17
|
+
else if (ts_morph_1.Node.isVariableDeclaration(node)) {
|
|
18
|
+
typeNode = node.getTypeNode();
|
|
19
|
+
}
|
|
20
|
+
if (!typeNode) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const typeText = typeNode.getText();
|
|
24
|
+
// Vérifier string simple (comparaison exacte)
|
|
25
|
+
if (typeof typePattern === 'string') {
|
|
26
|
+
return typeText === typePattern;
|
|
27
|
+
}
|
|
28
|
+
// Vérifier containsAny
|
|
29
|
+
if (typeof typePattern === 'object' && typePattern.containsAny) {
|
|
30
|
+
return typePattern.containsAny.some((t) => typeText.includes(t));
|
|
31
|
+
}
|
|
32
|
+
// Vérifier union
|
|
33
|
+
if (typeof typePattern === 'object' && typePattern.union) {
|
|
34
|
+
if (ts_morph_1.Node.isUnionTypeNode(typeNode)) {
|
|
35
|
+
const types = typeNode.getTypeNodes().map(t => t.getText());
|
|
36
|
+
if (typePattern.union.contains) {
|
|
37
|
+
const hasAll = typePattern.union.contains.every((t) => types.includes(t));
|
|
38
|
+
if (!hasAll) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (typePattern.union.missing) {
|
|
43
|
+
const missingTypes = Array.isArray(typePattern.union.missing)
|
|
44
|
+
? typePattern.union.missing
|
|
45
|
+
: [typePattern.union.missing];
|
|
46
|
+
const hasAny = missingTypes.some((t) => types.includes(t));
|
|
47
|
+
if (hasAny) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
// Vérifier type simple
|
|
56
|
+
if (typeof typePattern === 'string') {
|
|
57
|
+
return typeText === typePattern;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Vérifie le type de retour d'une fonction
|
|
63
|
+
*/
|
|
64
|
+
function matchesReturnType(node, returnTypePattern) {
|
|
65
|
+
let returnTypeNode;
|
|
66
|
+
// Cas 1: FunctionDeclaration ou MethodDeclaration
|
|
67
|
+
if (ts_morph_1.Node.isFunctionDeclaration(node) || ts_morph_1.Node.isMethodDeclaration(node)) {
|
|
68
|
+
returnTypeNode = node.getReturnTypeNode();
|
|
69
|
+
}
|
|
70
|
+
// Cas 2: VariableDeclaration avec ArrowFunction initializer
|
|
71
|
+
else if (ts_morph_1.Node.isVariableDeclaration(node)) {
|
|
72
|
+
const initializer = node.getInitializer();
|
|
73
|
+
if (initializer && ts_morph_1.Node.isArrowFunction(initializer)) {
|
|
74
|
+
returnTypeNode = initializer.getReturnTypeNode();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Cas 3: ArrowFunction directement
|
|
78
|
+
else if (ts_morph_1.Node.isArrowFunction(node)) {
|
|
79
|
+
returnTypeNode = node.getReturnTypeNode();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// Si pas de returnType, vérifier si on attend 'missing'
|
|
85
|
+
if (!returnTypeNode) {
|
|
86
|
+
if (returnTypePattern.missing) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (returnTypePattern.union && returnTypePattern.union.missing) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const fullTypeText = returnTypeNode.getText();
|
|
95
|
+
// Vérifier union
|
|
96
|
+
if (returnTypePattern.union) {
|
|
97
|
+
// Cas simplifié: vérifier contains ET missing sur le texte complet
|
|
98
|
+
if (returnTypePattern.union.contains) {
|
|
99
|
+
const hasAll = returnTypePattern.union.contains.every((t) => fullTypeText.includes(t));
|
|
100
|
+
if (!hasAll) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (returnTypePattern.union.missing) {
|
|
105
|
+
const missingTypes = Array.isArray(returnTypePattern.union.missing)
|
|
106
|
+
? returnTypePattern.union.missing
|
|
107
|
+
: [returnTypePattern.union.missing];
|
|
108
|
+
const hasAny = missingTypes.some((t) => fullTypeText.includes(t));
|
|
109
|
+
if (hasAny) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
// Vérifier missing
|
|
116
|
+
if (returnTypePattern.missing) {
|
|
117
|
+
const missingTypes = Array.isArray(returnTypePattern.missing)
|
|
118
|
+
? returnTypePattern.missing
|
|
119
|
+
: [returnTypePattern.missing];
|
|
120
|
+
const hasAny = missingTypes.some((t) => fullTypeText.includes(t));
|
|
121
|
+
return !hasAny;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Vérifie l'initializer d'une propriété ou variable
|
|
127
|
+
*/
|
|
128
|
+
function matchesInitializer(node, initializerPattern, matchesAstPatternFn) {
|
|
129
|
+
let initializer;
|
|
130
|
+
if (ts_morph_1.Node.isPropertyDeclaration(node)) {
|
|
131
|
+
initializer = node.getInitializer();
|
|
132
|
+
}
|
|
133
|
+
else if (ts_morph_1.Node.isVariableDeclaration(node)) {
|
|
134
|
+
initializer = node.getInitializer();
|
|
135
|
+
}
|
|
136
|
+
else if (ts_morph_1.Node.isPropertyAssignment(node)) {
|
|
137
|
+
initializer = node.getInitializer();
|
|
138
|
+
}
|
|
139
|
+
if (!initializer) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
// Vérifier notWrappedIn
|
|
143
|
+
if (initializerPattern.notWrappedIn) {
|
|
144
|
+
if (ts_morph_1.Node.isCallExpression(initializer)) {
|
|
145
|
+
const funcName = initializer.getExpression().getText();
|
|
146
|
+
if (funcName === initializerPattern.notWrappedIn) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Vérifier le pattern de l'initializer
|
|
152
|
+
return matchesAstPatternFn(initializer, initializerPattern);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Vérifie si l'objet a une propriété spécifique
|
|
156
|
+
*/
|
|
157
|
+
function matchesObject(node, objectPattern) {
|
|
158
|
+
// Vérifier type simple
|
|
159
|
+
if (typeof objectPattern === 'string') {
|
|
160
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
161
|
+
const expressionText = node.getExpression().getText();
|
|
162
|
+
// Accepter soit "pendingTasks" soit "this.pendingTasks" soit "obj.pendingTasks"
|
|
163
|
+
return expressionText === objectPattern || expressionText.endsWith('.' + objectPattern);
|
|
164
|
+
}
|
|
165
|
+
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
166
|
+
return node.getText() === objectPattern;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
// Vérifier object avec type
|
|
171
|
+
if (objectPattern.type) {
|
|
172
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
173
|
+
const expr = node.getExpression();
|
|
174
|
+
if (ts_morph_1.Node.isIdentifier(expr)) {
|
|
175
|
+
// Vérifier le type via le type checker de ts-morph
|
|
176
|
+
try {
|
|
177
|
+
const type = expr.getType();
|
|
178
|
+
const typeText = type.getText();
|
|
179
|
+
if (typeText.includes(objectPattern.type)) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Si l'analyse de type échoue, fallback sur le nom de variable
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
// Vérifier properties (avec checkObjectProperties si nécessaire)
|
|
192
|
+
if (objectPattern.properties) {
|
|
193
|
+
// Pour l'instant, retourner true si l'objet existe
|
|
194
|
+
// Une vérification plus précise nécessiterait checkObjectProperties
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Vérifie la propriété d'un PropertyAccessExpression
|
|
201
|
+
*/
|
|
202
|
+
function matchesProperty(node, property) {
|
|
203
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return node.getName() === property;
|
|
207
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utilitaires partagés pour les matchers
|
|
4
|
+
* Évite la duplication de code (principe DRY)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ensureArray = ensureArray;
|
|
8
|
+
exports.normalizeValue = normalizeValue;
|
|
9
|
+
exports.matchesTextValue = matchesTextValue;
|
|
10
|
+
/**
|
|
11
|
+
* Normalise une valeur en tableau
|
|
12
|
+
* @param value Valeur simple ou tableau
|
|
13
|
+
* @returns Tableau
|
|
14
|
+
*/
|
|
15
|
+
function ensureArray(value) {
|
|
16
|
+
return Array.isArray(value) ? value : [value];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Normalise une valeur en tant que string
|
|
20
|
+
* @param value Valeur à normaliser
|
|
21
|
+
* @returns String normalisée
|
|
22
|
+
*/
|
|
23
|
+
function normalizeValue(value) {
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Vérifie si un texte correspond à une valeur (avec ou sans quotes)
|
|
28
|
+
* @param nodeText Texte du nœud
|
|
29
|
+
* @param expectedValue Valeur attendue
|
|
30
|
+
* @returns true si le texte correspond
|
|
31
|
+
*/
|
|
32
|
+
function matchesTextValue(nodeText, expectedValue) {
|
|
33
|
+
return nodeText === expectedValue ||
|
|
34
|
+
nodeText === `'${expectedValue}'` ||
|
|
35
|
+
nodeText === `"${expectedValue}"` ||
|
|
36
|
+
nodeText === `${expectedValue}`;
|
|
37
|
+
}
|