@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,298 @@
|
|
|
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.scanDirectory = scanDirectory;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const core_1 = require("../utils/core");
|
|
41
|
+
const core_2 = require("../utils/core");
|
|
42
|
+
/**
|
|
43
|
+
* Vérifie si le projet est un monorepo Nx
|
|
44
|
+
* @param projectRoot Racine du projet
|
|
45
|
+
* @returns true si le projet a un fichier nx.json
|
|
46
|
+
*/
|
|
47
|
+
function isNxProject(projectRoot) {
|
|
48
|
+
const nxJsonPath = path.join(projectRoot, 'nx.json');
|
|
49
|
+
return fs.existsSync(nxJsonPath);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Exécute une commande système et retourne sa sortie
|
|
53
|
+
* @param command Commande à exécuter (ex: "node -v")
|
|
54
|
+
* @returns Sortie de la commande ou null en cas d'erreur
|
|
55
|
+
*/
|
|
56
|
+
function executeCommand(command) {
|
|
57
|
+
try {
|
|
58
|
+
const output = (0, child_process_1.execSync)(command, {
|
|
59
|
+
encoding: 'utf-8',
|
|
60
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
61
|
+
});
|
|
62
|
+
return output.trim();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
core_2.logger.warning(`Échec exécution commande: ${command}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Scan les règles de routine basées sur commande système (routineCmd)
|
|
71
|
+
* @param rules Règles avec routineCmd défini
|
|
72
|
+
* @param projectRoot Racine du projet (pour chemins relatifs)
|
|
73
|
+
* @returns Liste des matches trouvés
|
|
74
|
+
*/
|
|
75
|
+
function scanRoutineCommands(rules, projectRoot) {
|
|
76
|
+
const matches = [];
|
|
77
|
+
for (const rule of rules) {
|
|
78
|
+
if (!rule.routineCmd || !rule.regex) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const commandOutput = executeCommand(rule.routineCmd);
|
|
82
|
+
if (commandOutput === null) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
// Tester la sortie avec la regex de la règle
|
|
86
|
+
const compiledRegex = new RegExp(rule.regex, 'g');
|
|
87
|
+
const match = compiledRegex.exec(commandOutput);
|
|
88
|
+
if (match) {
|
|
89
|
+
// Match trouvé = règle pertinente (action nécessaire)
|
|
90
|
+
matches.push({
|
|
91
|
+
ruleKey: rule.key,
|
|
92
|
+
ruleSummary: rule.summary,
|
|
93
|
+
filePath: '<system>',
|
|
94
|
+
lineNumber: 1,
|
|
95
|
+
matchedText: commandOutput
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return matches;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Scan un répertoire et applique les règles de migration
|
|
103
|
+
* @param targetDir Répertoire à scanner (app ou lib)
|
|
104
|
+
* @param rules Règles de migration à appliquer
|
|
105
|
+
* @param projectRoot Racine du projet (pour chemins relatifs)
|
|
106
|
+
* @returns Liste des occurrences trouvées
|
|
107
|
+
*/
|
|
108
|
+
function scanDirectory(targetDir, rules, projectRoot) {
|
|
109
|
+
const matches = [];
|
|
110
|
+
// Scanner les fichiers spécifiques (onFile: ex. package.json)
|
|
111
|
+
// PRIORITÉ 1 : Chercher onFile à la racine du projet
|
|
112
|
+
// PRIORITÉ 2 : Si pas trouvé ET onFileNx existe ET projet Nx → Chercher onFileNx
|
|
113
|
+
// PRIORITÉ 3 : Fallback targetDir (fichiers custom par app/lib)
|
|
114
|
+
for (const rule of rules.filter(r => r.onFile)) {
|
|
115
|
+
let targetFile = null;
|
|
116
|
+
// 1. Chercher onFile à la racine du projet
|
|
117
|
+
const rootFilePath = path.join(projectRoot, rule.onFile);
|
|
118
|
+
if (fs.existsSync(rootFilePath)) {
|
|
119
|
+
targetFile = rootFilePath;
|
|
120
|
+
}
|
|
121
|
+
// 2. Si pas trouvé ET onFileNx existe ET projet Nx → Chercher onFileNx
|
|
122
|
+
else if (rule.onFileNx && isNxProject(projectRoot)) {
|
|
123
|
+
const nxFilePath = path.join(projectRoot, rule.onFileNx);
|
|
124
|
+
if (fs.existsSync(nxFilePath)) {
|
|
125
|
+
targetFile = nxFilePath;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 3. Fallback : chercher dans targetDir (pour fichiers spécifiques à l'app/lib)
|
|
129
|
+
if (!targetFile) {
|
|
130
|
+
const targetFilePath = path.join(targetDir, rule.onFile);
|
|
131
|
+
if (fs.existsSync(targetFilePath)) {
|
|
132
|
+
targetFile = targetFilePath;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Scanner le fichier trouvé si règle a une regex
|
|
136
|
+
if (targetFile && rule.regex) {
|
|
137
|
+
matches.push(...applyRule(targetFile, rule, projectRoot));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Grouper les règles par fileType pattern (ex. *.ts → [règle1, règle2])
|
|
141
|
+
const fileTypePatternToRules = new Map();
|
|
142
|
+
const compiledFileTypePatterns = new Map();
|
|
143
|
+
for (const rule of rules.filter(r => !r.onFile)) {
|
|
144
|
+
for (const fileTypePattern of rule.fileTypes) {
|
|
145
|
+
// Grouper les règles
|
|
146
|
+
if (!fileTypePatternToRules.has(fileTypePattern)) {
|
|
147
|
+
fileTypePatternToRules.set(fileTypePattern, []);
|
|
148
|
+
// Compiler le pattern une seule fois
|
|
149
|
+
// IMPORTANT: Échapper les points AVANT de convertir les étoiles
|
|
150
|
+
const regexPattern = '^' + fileTypePattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$';
|
|
151
|
+
compiledFileTypePatterns.set(fileTypePattern, new RegExp(regexPattern));
|
|
152
|
+
}
|
|
153
|
+
fileTypePatternToRules.get(fileTypePattern).push(rule);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Scanner récursivement uniquement les fichiers qui matchent
|
|
157
|
+
if (fileTypePatternToRules.size > 0) {
|
|
158
|
+
scanRecursively(targetDir, fileTypePatternToRules, compiledFileTypePatterns, projectRoot, matches);
|
|
159
|
+
}
|
|
160
|
+
return matches;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Scan récursif des fichiers selon les patterns
|
|
164
|
+
* @param currentDir Répertoire en cours de scan
|
|
165
|
+
* @param fileTypePatternToRules Map des fileType patterns vers leurs règles
|
|
166
|
+
* @param compiledFileTypePatterns Map des patterns vers leurs regex compilées
|
|
167
|
+
* @param projectRoot Racine du projet
|
|
168
|
+
* @param matches Accumulateur de résultats (mutated)
|
|
169
|
+
*/
|
|
170
|
+
function scanRecursively(currentDir, fileTypePatternToRules, compiledFileTypePatterns, projectRoot, matches) {
|
|
171
|
+
try {
|
|
172
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
175
|
+
if (entry.isDirectory()) {
|
|
176
|
+
if (!(0, core_1.shouldIgnoreDir)(entry.name)) {
|
|
177
|
+
scanRecursively(fullPath, fileTypePatternToRules, compiledFileTypePatterns, projectRoot, matches);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else if (entry.isFile()) {
|
|
181
|
+
// Trouver les règles applicables pour ce fichier (dédupliquées)
|
|
182
|
+
const applicableRules = new Set();
|
|
183
|
+
for (const [fileTypePattern, rulesForPattern] of fileTypePatternToRules) {
|
|
184
|
+
const compiledPattern = compiledFileTypePatterns.get(fileTypePattern);
|
|
185
|
+
if (compiledPattern.test(entry.name)) {
|
|
186
|
+
rulesForPattern.forEach(r => applicableRules.add(r));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Scanner uniquement si au moins une règle s'applique
|
|
190
|
+
if (applicableRules.size > 0) {
|
|
191
|
+
matches.push(...applyRules(fullPath, Array.from(applicableRules), projectRoot));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
198
|
+
core_2.logger.warning(`Impossible de lire: ${currentDir} - ${errorMsg}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Détecte si une regex nécessite un contexte multiligne
|
|
203
|
+
* Patterns détectés : [\s\S], [\S\s], \n, \r
|
|
204
|
+
*/
|
|
205
|
+
function needsMultilineContext(regexPattern) {
|
|
206
|
+
return regexPattern.includes('[\\s\\S]') ||
|
|
207
|
+
regexPattern.includes('[\\S\\s]') ||
|
|
208
|
+
regexPattern.includes('\\n') ||
|
|
209
|
+
regexPattern.includes('\\r');
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Trouve le numéro de ligne d'un match dans le contenu
|
|
213
|
+
*/
|
|
214
|
+
function findLineNumber(content, matchIndex) {
|
|
215
|
+
const textBeforeMatch = content.substring(0, matchIndex);
|
|
216
|
+
return textBeforeMatch.split('\n').length;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Applique plusieurs règles à un fichier (une seule lecture)
|
|
220
|
+
* @param filePath Chemin complet du fichier
|
|
221
|
+
* @param rules Règles de migration à appliquer
|
|
222
|
+
* @param projectRoot Racine du projet (pour chemin relatif)
|
|
223
|
+
*/
|
|
224
|
+
function applyRules(filePath, rules, projectRoot) {
|
|
225
|
+
const matches = [];
|
|
226
|
+
// Séparer les règles selon leur type (ligne unique vs multiligne)
|
|
227
|
+
const singleLineRules = [];
|
|
228
|
+
const multilineRules = [];
|
|
229
|
+
for (const rule of rules) {
|
|
230
|
+
if (rule.regex) {
|
|
231
|
+
const compiledRegex = new RegExp(rule.regex, 'g');
|
|
232
|
+
const ruleWithRegex = { rule, regex: compiledRegex };
|
|
233
|
+
if (needsMultilineContext(rule.regex)) {
|
|
234
|
+
multilineRules.push(ruleWithRegex);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
singleLineRules.push(ruleWithRegex);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (singleLineRules.length === 0 && multilineRules.length === 0) {
|
|
242
|
+
return matches;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
246
|
+
const relativeFilePath = path.relative(projectRoot, filePath);
|
|
247
|
+
// ÉTAPE 1 : Appliquer les règles multilignes sur le fichier entier
|
|
248
|
+
for (const { rule, regex } of multilineRules) {
|
|
249
|
+
regex.lastIndex = 0;
|
|
250
|
+
let match;
|
|
251
|
+
while ((match = regex.exec(fileContent)) !== null) {
|
|
252
|
+
const lineNumber = findLineNumber(fileContent, match.index);
|
|
253
|
+
matches.push({
|
|
254
|
+
ruleKey: rule.key,
|
|
255
|
+
ruleSummary: rule.summary,
|
|
256
|
+
filePath: relativeFilePath,
|
|
257
|
+
lineNumber,
|
|
258
|
+
matchedText: match[0].trim()
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// ÉTAPE 2 : Appliquer les règles simples ligne par ligne
|
|
263
|
+
if (singleLineRules.length > 0) {
|
|
264
|
+
const fileLines = fileContent.split('\n');
|
|
265
|
+
for (let lineIndex = 0; lineIndex < fileLines.length; lineIndex++) {
|
|
266
|
+
const currentLine = fileLines[lineIndex];
|
|
267
|
+
for (const { rule, regex } of singleLineRules) {
|
|
268
|
+
regex.lastIndex = 0;
|
|
269
|
+
const lineMatches = currentLine.match(regex);
|
|
270
|
+
if (lineMatches) {
|
|
271
|
+
for (const match of lineMatches) {
|
|
272
|
+
matches.push({
|
|
273
|
+
ruleKey: rule.key,
|
|
274
|
+
ruleSummary: rule.summary,
|
|
275
|
+
filePath: relativeFilePath,
|
|
276
|
+
lineNumber: lineIndex + 1,
|
|
277
|
+
matchedText: match.trim()
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
core_2.logger.warning(`Impossible de lire: ${filePath}`);
|
|
287
|
+
}
|
|
288
|
+
return matches;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Applique une règle regex à un fichier (pour onFile)
|
|
292
|
+
* @param filePath Chemin complet du fichier
|
|
293
|
+
* @param rule Règle de migration à appliquer
|
|
294
|
+
* @param projectRoot Racine du projet (pour chemin relatif)
|
|
295
|
+
*/
|
|
296
|
+
function applyRule(filePath, rule, projectRoot) {
|
|
297
|
+
return applyRules(filePath, [rule], projectRoot);
|
|
298
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateWorkloadReport = calculateWorkloadReport;
|
|
4
|
+
const workload_formatter_1 = require("../../utils/core/workload-formatter");
|
|
5
|
+
const rule_helpers_1 = require("../../utils/core/rule-helpers");
|
|
6
|
+
const metadata_1 = require("./metadata");
|
|
7
|
+
const grouping_1 = require("./grouping");
|
|
8
|
+
const hierarchy_calculator_1 = require("./hierarchy-calculator");
|
|
9
|
+
const rules_loader_1 = require("../rules-loader");
|
|
10
|
+
const project_strategy_1 = require("../project-strategy");
|
|
11
|
+
/**
|
|
12
|
+
* Calcule le rapport complet de charge de travail hiérarchique
|
|
13
|
+
*/
|
|
14
|
+
function calculateWorkloadReport(projectInfo, matches, rules) {
|
|
15
|
+
// Créer maps pour accès rapide O(1)
|
|
16
|
+
const ruleMap = (0, rule_helpers_1.createRuleMap)(rules);
|
|
17
|
+
const migrationRules = (0, rules_loader_1.loadMigrationRules)();
|
|
18
|
+
const { ruleMigrationMap, rulePriorityMap } = (0, metadata_1.createRuleMetadataMaps)(migrationRules);
|
|
19
|
+
// Grouper matches par target (app/lib)
|
|
20
|
+
const matchesByTarget = (0, grouping_1.groupMatchesByTarget)(matches, projectInfo);
|
|
21
|
+
// Créer la stratégie appropriée
|
|
22
|
+
const strategy = (0, project_strategy_1.createProjectStrategy)(projectInfo);
|
|
23
|
+
// Calculer charge pour chaque target + stats globales en 1 seul parcours
|
|
24
|
+
const targets = [];
|
|
25
|
+
let globalTotalMinutes = 0;
|
|
26
|
+
// Stats globales (calculées en même temps)
|
|
27
|
+
let totalOccurrences = 0;
|
|
28
|
+
let totalSpecialWorkloadFiles = 0;
|
|
29
|
+
let totalRoutineRules = 0;
|
|
30
|
+
const uniqueRulesSet = new Set();
|
|
31
|
+
// Calculer charge pour chaque target (app/lib/monorepo)
|
|
32
|
+
for (const [targetName, targetMatches] of matchesByTarget) {
|
|
33
|
+
// Déterminer le type de target via la stratégie
|
|
34
|
+
const targetType = strategy.getTargetType(projectInfo, targetName);
|
|
35
|
+
// Filtrer les routines selon la stratégie
|
|
36
|
+
const shouldFilterRoutines = strategy.shouldFilterRoutinesFromTarget(targetName, projectInfo);
|
|
37
|
+
const filteredMatches = shouldFilterRoutines
|
|
38
|
+
? targetMatches.filter(match => {
|
|
39
|
+
const rule = ruleMap.get(match.ruleKey);
|
|
40
|
+
return rule && !(0, rule_helpers_1.isRoutineRule)(rule);
|
|
41
|
+
})
|
|
42
|
+
: targetMatches;
|
|
43
|
+
const migrations = (0, hierarchy_calculator_1.calculateMigrationsForTarget)(filteredMatches, ruleMap, ruleMigrationMap, rulePriorityMap, targetName, projectInfo, rules, false // includeRoutines toujours false (routines viennent des matches ou de target MONOREPO)
|
|
44
|
+
);
|
|
45
|
+
const targetTotalMinutes = migrations.reduce((sum, m) => sum + m.totalTime.minutes, 0);
|
|
46
|
+
targets.push({
|
|
47
|
+
targetName,
|
|
48
|
+
targetType,
|
|
49
|
+
totalTime: (0, workload_formatter_1.convertMinutesToWorkloadTime)(targetTotalMinutes),
|
|
50
|
+
migrations
|
|
51
|
+
});
|
|
52
|
+
// Calculer stats en parcourant migrations
|
|
53
|
+
for (const migration of migrations) {
|
|
54
|
+
for (const priority of migration.priorities) {
|
|
55
|
+
for (const rule of priority.rules) {
|
|
56
|
+
uniqueRulesSet.add(rule.ruleKey);
|
|
57
|
+
if (rule.type === 'routine') {
|
|
58
|
+
totalRoutineRules++;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
totalOccurrences += rule.occurrences;
|
|
62
|
+
}
|
|
63
|
+
if (rule.specialWorkloadFiles) {
|
|
64
|
+
totalSpecialWorkloadFiles += rule.specialWorkloadFiles;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
globalTotalMinutes += targetTotalMinutes;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
totalTime: (0, workload_formatter_1.convertMinutesToWorkloadTime)(globalTotalMinutes),
|
|
73
|
+
projectInfo,
|
|
74
|
+
targets,
|
|
75
|
+
stats: {
|
|
76
|
+
totalOccurrences,
|
|
77
|
+
totalSpecialWorkloadFiles,
|
|
78
|
+
totalRoutineRules,
|
|
79
|
+
uniqueRules: uniqueRulesSet.size
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRIORITY_ORDER = exports.MIGRATION_ORDER = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Ordre d'affichage des migrations (to18 → to19 → to20)
|
|
6
|
+
*/
|
|
7
|
+
exports.MIGRATION_ORDER = ['to18', 'to19', 'to20'];
|
|
8
|
+
/**
|
|
9
|
+
* Ordre d'affichage des priorités (obligatoire → recommandé → optionnel)
|
|
10
|
+
*/
|
|
11
|
+
exports.PRIORITY_ORDER = [
|
|
12
|
+
'obligatoire',
|
|
13
|
+
'recommande',
|
|
14
|
+
'optionnelle'
|
|
15
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.groupMatchesByTarget = groupMatchesByTarget;
|
|
4
|
+
exports.groupMatchesByRule = groupMatchesByRule;
|
|
5
|
+
const array_helpers_1 = require("../../utils/core/array-helpers");
|
|
6
|
+
const target_resolver_1 = require("./target-resolver");
|
|
7
|
+
/**
|
|
8
|
+
* Groupe les matches par target (app ou lib) via filePath
|
|
9
|
+
*/
|
|
10
|
+
function groupMatchesByTarget(matches, projectInfo) {
|
|
11
|
+
return (0, array_helpers_1.groupBy)(matches, match => (0, target_resolver_1.extractTargetFromFilePath)(match.filePath, projectInfo));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Groupe les matches par ruleKey
|
|
15
|
+
*/
|
|
16
|
+
function groupMatchesByRule(matches) {
|
|
17
|
+
return (0, array_helpers_1.groupBy)(matches, match => match.ruleKey);
|
|
18
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateMigrationsForTarget = calculateMigrationsForTarget;
|
|
4
|
+
const workload_formatter_1 = require("../../utils/core/workload-formatter");
|
|
5
|
+
const logger_1 = require("../../utils/core/logger");
|
|
6
|
+
const rule_helpers_1 = require("../../utils/core/rule-helpers");
|
|
7
|
+
const grouping_1 = require("./grouping");
|
|
8
|
+
const target_resolver_1 = require("./target-resolver");
|
|
9
|
+
const special_workload_1 = require("./special-workload");
|
|
10
|
+
const constants_1 = require("./constants");
|
|
11
|
+
/**
|
|
12
|
+
* Calcule les migrations pour une target donnée
|
|
13
|
+
*/
|
|
14
|
+
function calculateMigrationsForTarget(matches, ruleMap, ruleMigrationMap, rulePriorityMap, targetName, projectInfo, allRules, includeRoutines) {
|
|
15
|
+
const matchesByMigration = new Map();
|
|
16
|
+
for (const match of matches) {
|
|
17
|
+
const migration = ruleMigrationMap.get(match.ruleKey) || 'unknown';
|
|
18
|
+
if (!matchesByMigration.has(migration)) {
|
|
19
|
+
matchesByMigration.set(migration, []);
|
|
20
|
+
}
|
|
21
|
+
matchesByMigration.get(migration).push(match);
|
|
22
|
+
}
|
|
23
|
+
const migrations = [];
|
|
24
|
+
for (const migration of constants_1.MIGRATION_ORDER) {
|
|
25
|
+
const migrationMatches = matchesByMigration.get(migration);
|
|
26
|
+
if (!migrationMatches || migrationMatches.length === 0)
|
|
27
|
+
continue;
|
|
28
|
+
const priorities = calculatePrioritiesForMigration(migrationMatches, ruleMap, rulePriorityMap, targetName, projectInfo, allRules, includeRoutines, migration, ruleMigrationMap);
|
|
29
|
+
const totalMinutes = priorities.reduce((sum, p) => sum + p.totalTime.minutes, 0);
|
|
30
|
+
migrations.push({
|
|
31
|
+
migration,
|
|
32
|
+
totalTime: (0, workload_formatter_1.convertMinutesToWorkloadTime)(totalMinutes),
|
|
33
|
+
priorities
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return migrations;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Calcule les priorités pour une migration donnée
|
|
40
|
+
*/
|
|
41
|
+
function calculatePrioritiesForMigration(matches, ruleMap, rulePriorityMap, targetName, projectInfo, allRules, includeRoutines, currentMigration, ruleMigrationMap) {
|
|
42
|
+
const matchesByPriority = new Map();
|
|
43
|
+
for (const match of matches) {
|
|
44
|
+
const priority = rulePriorityMap.get(match.ruleKey) || 'optionnelle';
|
|
45
|
+
if (!matchesByPriority.has(priority)) {
|
|
46
|
+
matchesByPriority.set(priority, []);
|
|
47
|
+
}
|
|
48
|
+
matchesByPriority.get(priority).push(match);
|
|
49
|
+
}
|
|
50
|
+
const priorities = [];
|
|
51
|
+
for (const priority of constants_1.PRIORITY_ORDER) {
|
|
52
|
+
const priorityMatches = matchesByPriority.get(priority);
|
|
53
|
+
if (!priorityMatches || priorityMatches.length === 0)
|
|
54
|
+
continue;
|
|
55
|
+
const rules = calculateRulesForPriority(priorityMatches, ruleMap, targetName, projectInfo, allRules, includeRoutines, currentMigration, priority, ruleMigrationMap, rulePriorityMap);
|
|
56
|
+
const totalMinutes = rules.reduce((sum, r) => sum + r.totalTime.minutes, 0);
|
|
57
|
+
priorities.push({
|
|
58
|
+
priority,
|
|
59
|
+
totalTime: (0, workload_formatter_1.convertMinutesToWorkloadTime)(totalMinutes),
|
|
60
|
+
rules
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return priorities;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Calcule les règles pour une priorité donnée (standard + special + routine)
|
|
67
|
+
*/
|
|
68
|
+
function calculateRulesForPriority(matches, ruleMap, targetName, projectInfo, allRules, includeRoutines, currentMigration, currentPriority, ruleMigrationMap, rulePriorityMap) {
|
|
69
|
+
const matchesByRule = (0, grouping_1.groupMatchesByRule)(matches);
|
|
70
|
+
const rules = [];
|
|
71
|
+
// Traiter toutes les règles avec matches (standard + special + routine)
|
|
72
|
+
// Les routines sont maintenant détectées automatiquement via leur match dans le scanner
|
|
73
|
+
for (const [ruleKey, ruleMatches] of matchesByRule) {
|
|
74
|
+
const rule = ruleMap.get(ruleKey);
|
|
75
|
+
if (!rule)
|
|
76
|
+
continue;
|
|
77
|
+
const ruleWorkload = calculateRuleWorkload(rule, ruleMatches, targetName, projectInfo);
|
|
78
|
+
rules.push(ruleWorkload);
|
|
79
|
+
}
|
|
80
|
+
return rules.sort((a, b) => b.totalTime.minutes - a.totalTime.minutes);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Calcule la charge pour une règle unique (avec support special_workload et routine)
|
|
84
|
+
*/
|
|
85
|
+
function calculateRuleWorkload(rule, matches, targetName, projectInfo) {
|
|
86
|
+
const occurrences = matches.length;
|
|
87
|
+
let totalMinutes = occurrences * rule.estimated_time_per_occurrence;
|
|
88
|
+
let type = 'standard';
|
|
89
|
+
let specialWorkloadFiles;
|
|
90
|
+
let baseOccurrences;
|
|
91
|
+
// Détecter si c'est une routine (règle avec onFile)
|
|
92
|
+
if ((0, rule_helpers_1.isRoutineRule)(rule)) {
|
|
93
|
+
type = 'routine';
|
|
94
|
+
// Pour les routines, occurrences = 1 (tâche unique par projet)
|
|
95
|
+
totalMinutes = rule.estimated_time_per_occurrence;
|
|
96
|
+
logger_1.logger.debug(` 🔧 Routine [${rule.key}]: 1 × ${rule.estimated_time_per_occurrence}min (tâche unique)`);
|
|
97
|
+
}
|
|
98
|
+
// Support special_workload
|
|
99
|
+
else if (rule.special_workload) {
|
|
100
|
+
const targetPath = (0, target_resolver_1.getTargetPath)(projectInfo, targetName);
|
|
101
|
+
const fileCount = (0, special_workload_1.countSpecialWorkloadFiles)(targetPath, rule, projectInfo.path);
|
|
102
|
+
// Marquer comme special/combined même si fileCount === 0 (règle a special_workload défini)
|
|
103
|
+
specialWorkloadFiles = fileCount;
|
|
104
|
+
baseOccurrences = occurrences;
|
|
105
|
+
type = occurrences > 0 ? 'combined' : 'special';
|
|
106
|
+
if (fileCount > 0) {
|
|
107
|
+
const specialMinutes = fileCount * rule.special_workload.bb_time_by_occurrence;
|
|
108
|
+
logger_1.logger.debug(` 📦 Special workload [${rule.key}]: ${fileCount} fichiers × ${rule.special_workload.bb_time_by_occurrence}min = ${specialMinutes}min`);
|
|
109
|
+
totalMinutes += specialMinutes;
|
|
110
|
+
rule.special_workload.occurrences_found = fileCount;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const ruleWorkload = {
|
|
114
|
+
ruleKey: rule.key,
|
|
115
|
+
ruleSummary: rule.summary,
|
|
116
|
+
occurrences: type === 'routine' ? 1 : occurrences, // Routine = toujours 1
|
|
117
|
+
minutesPerOccurrence: rule.estimated_time_per_occurrence,
|
|
118
|
+
totalTime: (0, workload_formatter_1.convertMinutesToWorkloadTime)(totalMinutes),
|
|
119
|
+
type
|
|
120
|
+
};
|
|
121
|
+
if (specialWorkloadFiles !== undefined) {
|
|
122
|
+
ruleWorkload.specialWorkloadFiles = specialWorkloadFiles;
|
|
123
|
+
ruleWorkload.baseOccurrences = baseOccurrences;
|
|
124
|
+
ruleWorkload.specialTimePerFile = rule.special_workload?.bb_time_by_occurrence;
|
|
125
|
+
}
|
|
126
|
+
return ruleWorkload;
|
|
127
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Module workload - Calcul hiérarchique de charge de travail
|
|
4
|
+
*
|
|
5
|
+
* API publique :
|
|
6
|
+
* - calculateWorkloadReport() : Fonction principale
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.calculateWorkloadReport = void 0;
|
|
10
|
+
var calculator_1 = require("./calculator");
|
|
11
|
+
Object.defineProperty(exports, "calculateWorkloadReport", { enumerable: true, get: function () { return calculator_1.calculateWorkloadReport; } });
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRuleMetadataMaps = createRuleMetadataMaps;
|
|
4
|
+
/**
|
|
5
|
+
* Crée les maps ruleKey → migration et ruleKey → priority
|
|
6
|
+
* @param migrationRules Structure hiérarchique des règles par migration et priorité
|
|
7
|
+
*/
|
|
8
|
+
function createRuleMetadataMaps(migrationRules) {
|
|
9
|
+
const ruleMigrationMap = new Map();
|
|
10
|
+
const rulePriorityMap = new Map();
|
|
11
|
+
for (const [migration, priorities] of Object.entries(migrationRules)) {
|
|
12
|
+
for (const [priority, rulesList] of Object.entries(priorities)) {
|
|
13
|
+
for (const rule of rulesList) {
|
|
14
|
+
ruleMigrationMap.set(rule.key, migration);
|
|
15
|
+
rulePriorityMap.set(rule.key, priority);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { ruleMigrationMap, rulePriorityMap };
|
|
20
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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.countSpecialWorkloadFiles = countSpecialWorkloadFiles;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const file_helpers_1 = require("../../utils/core/file-helpers");
|
|
40
|
+
const logger_1 = require("../../utils/core/logger");
|
|
41
|
+
/**
|
|
42
|
+
* Cache pour éviter de recompter les mêmes fichiers
|
|
43
|
+
* Clé: `${targetPath}::${projectRoot}::${pattern1,pattern2,...}`
|
|
44
|
+
*/
|
|
45
|
+
const specialWorkloadCache = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* Détermine si un pattern correspond à un fichier racine
|
|
48
|
+
* Fichiers racine = noms exacts sans wildcard (ex: package.json, tsconfig.json)
|
|
49
|
+
* Patterns de scan = patterns avec wildcard (ex: *.module.ts, tsconfig*.json)
|
|
50
|
+
*/
|
|
51
|
+
function isRootFilePattern(pattern) {
|
|
52
|
+
// Fichier racine si pas de wildcard (*) et pas de séparateur de chemin (/)
|
|
53
|
+
return !pattern.includes('*') && !pattern.includes('/');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compte les fichiers correspondant aux patterns special_workload
|
|
57
|
+
* Utilise un cache pour éviter de recompter les mêmes patterns
|
|
58
|
+
*
|
|
59
|
+
* PRIORITÉ : Cherche d'abord les fichiers racine (package.json, tsconfig.json, etc.) dans projectRoot
|
|
60
|
+
* FALLBACK : Scanne récursivement les patterns avec wildcard dans targetPath
|
|
61
|
+
*/
|
|
62
|
+
function countSpecialWorkloadFiles(targetPath, rule, projectRoot) {
|
|
63
|
+
if (!rule.special_workload || !rule.special_workload.bb_fileTypes) {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
const patterns = rule.special_workload.bb_fileTypes;
|
|
67
|
+
const cacheKey = `${targetPath}::${projectRoot}::${patterns.join(',')}`;
|
|
68
|
+
// Vérifier le cache
|
|
69
|
+
if (specialWorkloadCache.has(cacheKey)) {
|
|
70
|
+
const cached = specialWorkloadCache.get(cacheKey);
|
|
71
|
+
logger_1.logger.debug(` 📦 Cache hit pour ${rule.key}: ${cached} fichiers`);
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
// Compter les fichiers pour chaque pattern
|
|
75
|
+
logger_1.logger.debug(` 🔍 Comptage fichiers special_workload pour ${rule.key}...`);
|
|
76
|
+
let totalCount = 0;
|
|
77
|
+
for (const pattern of patterns) {
|
|
78
|
+
// Détecter si c'est un fichier racine (nom exact sans wildcard)
|
|
79
|
+
if (isRootFilePattern(pattern)) {
|
|
80
|
+
// Vérifier à la racine du projet (ex: package.json, tsconfig.json)
|
|
81
|
+
const rootFilePath = path.join(projectRoot, pattern);
|
|
82
|
+
if (fs.existsSync(rootFilePath)) {
|
|
83
|
+
totalCount += 1;
|
|
84
|
+
logger_1.logger.debug(` • ${pattern}: 1 fichier (racine)`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Scanner récursivement dans targetPath (ex: *.module.ts, tsconfig*.json)
|
|
89
|
+
const regex = (0, file_helpers_1.buildBlockToRegex)(pattern);
|
|
90
|
+
const count = (0, file_helpers_1.countFiles)(targetPath, regex);
|
|
91
|
+
if (count > 0) {
|
|
92
|
+
logger_1.logger.debug(` • ${pattern}: ${count} fichier(s)`);
|
|
93
|
+
totalCount += count;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
logger_1.logger.debug(` ✓ Total: ${totalCount} fichier(s) trouvé(s)`);
|
|
98
|
+
// Stocker dans le cache
|
|
99
|
+
specialWorkloadCache.set(cacheKey, totalCount);
|
|
100
|
+
return totalCount;
|
|
101
|
+
}
|