@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.
Files changed (122) hide show
  1. package/LICENSE +96 -0
  2. package/README.fr.md +359 -0
  3. package/README.md +360 -0
  4. package/SECURITY.md +187 -0
  5. package/dist/client.bundle.js +357 -0
  6. package/dist/src/core/app-analyzer.js +134 -0
  7. package/dist/src/core/ast/matchers/html/html-attribute-matcher.js +86 -0
  8. package/dist/src/core/ast/matchers/html/html-component-matcher.js +40 -0
  9. package/dist/src/core/ast/matchers/html/html-element-matcher.js +54 -0
  10. package/dist/src/core/ast/matchers/html/html-parser.js +58 -0
  11. package/dist/src/core/ast/matchers/html/html-pipe-matcher.js +95 -0
  12. package/dist/src/core/ast/matchers/html/html-text-matcher.js +53 -0
  13. package/dist/src/core/ast/matchers/html/index.js +118 -0
  14. package/dist/src/core/ast/matchers/index.js +377 -0
  15. package/dist/src/core/ast/matchers/ts/collection-matcher.js +51 -0
  16. package/dist/src/core/ast/matchers/ts/context-matcher.js +275 -0
  17. package/dist/src/core/ast/matchers/ts/decorator-matcher.js +465 -0
  18. package/dist/src/core/ast/matchers/ts/expression-matcher.js +237 -0
  19. package/dist/src/core/ast/matchers/ts/file-matcher.js +97 -0
  20. package/dist/src/core/ast/matchers/ts/hierarchy-matcher.js +172 -0
  21. package/dist/src/core/ast/matchers/ts/import-matcher.js +39 -0
  22. package/dist/src/core/ast/matchers/ts/index.js +53 -0
  23. package/dist/src/core/ast/matchers/ts/node-matcher.js +156 -0
  24. package/dist/src/core/ast/matchers/ts/symbol-matcher.js +281 -0
  25. package/dist/src/core/ast/matchers/ts/type-matcher.js +207 -0
  26. package/dist/src/core/ast/matchers/utils/matcher-helpers.js +37 -0
  27. package/dist/src/core/ast/scanner-ast.js +444 -0
  28. package/dist/src/core/project-detector.js +196 -0
  29. package/dist/src/core/project-strategy/index.js +9 -0
  30. package/dist/src/core/project-strategy/nx-strategy.js +130 -0
  31. package/dist/src/core/project-strategy/project-strategy.interface.js +2 -0
  32. package/dist/src/core/project-strategy/standalone-strategy.js +74 -0
  33. package/dist/src/core/project-strategy/strategy-factory.js +15 -0
  34. package/dist/src/core/rules-loader.js +89 -0
  35. package/dist/src/core/scan-reporter.js +316 -0
  36. package/dist/src/core/scanner-delta.js +339 -0
  37. package/dist/src/core/scanner-orchestrator.js +266 -0
  38. package/dist/src/core/scanner-regex.js +298 -0
  39. package/dist/src/core/workload/calculator.js +82 -0
  40. package/dist/src/core/workload/constants.js +15 -0
  41. package/dist/src/core/workload/grouping.js +18 -0
  42. package/dist/src/core/workload/hierarchy-calculator.js +127 -0
  43. package/dist/src/core/workload/index.js +11 -0
  44. package/dist/src/core/workload/metadata.js +20 -0
  45. package/dist/src/core/workload/special-workload.js +101 -0
  46. package/dist/src/core/workload/target-resolver.js +34 -0
  47. package/dist/src/data/angular-migration-rules.json +2337 -0
  48. package/dist/src/data/markdown/angular-migration-17-18.md +408 -0
  49. package/dist/src/data/markdown/angular-migration-18-19.md +600 -0
  50. package/dist/src/data/markdown/angular-migration-19-20.md +521 -0
  51. package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +66 -0
  52. package/dist/src/data/rules/to18/rules-18-obligatoire.json +374 -0
  53. package/dist/src/data/rules/to18/rules-18-optionnelle.json +188 -0
  54. package/dist/src/data/rules/to18/rules-18-recommande.json +218 -0
  55. package/dist/src/data/rules/to19/rules-19-obligatoire.json +348 -0
  56. package/dist/src/data/rules/to19/rules-19-optionnelle.json +223 -0
  57. package/dist/src/data/rules/to19/rules-19-recommande.json +200 -0
  58. package/dist/src/data/rules/to20/rules-20-obligatoire.json +556 -0
  59. package/dist/src/data/rules/to20/rules-20-optionnelle.json +190 -0
  60. package/dist/src/data/rules/to20/rules-20-recommande.json +151 -0
  61. package/dist/src/index.js +161 -0
  62. package/dist/src/models/chip-config.js +45 -0
  63. package/dist/src/models/interfaces/app-details.interface.js +2 -0
  64. package/dist/src/models/interfaces/ast-interfaces.js +5 -0
  65. package/dist/src/models/interfaces/ast-pattern.interface.js +2 -0
  66. package/dist/src/models/interfaces/client-interfaces.js +6 -0
  67. package/dist/src/models/interfaces/detection-stats.interface.js +2 -0
  68. package/dist/src/models/interfaces/html-match.interface.js +2 -0
  69. package/dist/src/models/interfaces/html-report-data.interface.js +2 -0
  70. package/dist/src/models/interfaces/lib-details.interface.js +2 -0
  71. package/dist/src/models/interfaces/migration-rules.interface.js +2 -0
  72. package/dist/src/models/interfaces/parsed-args.interface.js +2 -0
  73. package/dist/src/models/interfaces/project-info.interface.js +2 -0
  74. package/dist/src/models/interfaces/project-overview-data.interface.js +2 -0
  75. package/dist/src/models/interfaces/rule-match.interface.js +2 -0
  76. package/dist/src/models/interfaces/rule.interface.js +2 -0
  77. package/dist/src/models/interfaces/rules-by-priority.interface.js +2 -0
  78. package/dist/src/models/interfaces/scanner-comparison.interface.js +2 -0
  79. package/dist/src/models/interfaces/special-workload.interface.js +2 -0
  80. package/dist/src/models/interfaces/workload-report.interface.js +2 -0
  81. package/dist/src/models/types/build-block-blob.type.js +2 -0
  82. package/dist/src/models/types/migration-version.type.js +2 -0
  83. package/dist/src/models/types/project-type.type.js +2 -0
  84. package/dist/src/models/types/risk-level.type.js +2 -0
  85. package/dist/src/models/types/rule-category.type.js +2 -0
  86. package/dist/src/models/types/rule-priority.type.js +2 -0
  87. package/dist/src/models/types/rule-workload-type.type.js +2 -0
  88. package/dist/src/templates/landing/applications-analyzed.template.js +18 -0
  89. package/dist/src/templates/landing/card-app-info.template.js +63 -0
  90. package/dist/src/templates/landing/card-lib-info.template.js +67 -0
  91. package/dist/src/templates/landing/libs-analyzed.template.js +22 -0
  92. package/dist/src/templates/landing/nx-summary.template.js +115 -0
  93. package/dist/src/templates/landing/project-overview.template.js +27 -0
  94. package/dist/src/templates/page/index-page.template.js +95 -0
  95. package/dist/src/templates/page/main.template.js +83 -0
  96. package/dist/src/templates/page/migration-guide.template.js +175 -0
  97. package/dist/src/templates/page/workload-report.template.js +53 -0
  98. package/dist/src/templates/workload/dashboard.template.js +184 -0
  99. package/dist/src/templates/workload/filters-panel.template.js +215 -0
  100. package/dist/src/templates/workload/guide-rule-card.template.js +107 -0
  101. package/dist/src/templates/workload/hierarchy-nx.template.js +104 -0
  102. package/dist/src/templates/workload/hierarchy-shared.js +163 -0
  103. package/dist/src/templates/workload/hierarchy-standalone.template.js +36 -0
  104. package/dist/src/templates/workload/hierarchy.template.js +35 -0
  105. package/dist/src/templates/workload/rule-modal.template.js +280 -0
  106. package/dist/src/utils/core/args-parser.js +123 -0
  107. package/dist/src/utils/core/array-helpers.js +18 -0
  108. package/dist/src/utils/core/ast-helpers.js +99 -0
  109. package/dist/src/utils/core/file-helpers.js +109 -0
  110. package/dist/src/utils/core/html-helpers.js +36 -0
  111. package/dist/src/utils/core/index.js +28 -0
  112. package/dist/src/utils/core/logger.js +38 -0
  113. package/dist/src/utils/core/rule-helpers.js +15 -0
  114. package/dist/src/utils/core/workload-formatter.js +6 -0
  115. package/dist/src/utils/shared/array-helpers.js +25 -0
  116. package/dist/src/utils/shared/date-helpers.js +109 -0
  117. package/dist/src/utils/shared/html-helpers.js +37 -0
  118. package/dist/src/utils/shared/index.js +25 -0
  119. package/dist/src/utils/shared/rule-helpers.js +20 -0
  120. package/dist/src/utils/shared/time-formatters.js +76 -0
  121. package/dist/styles.css +2 -0
  122. package/package.json +107 -0
@@ -0,0 +1,130 @@
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.NxStrategy = void 0;
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ /**
40
+ * Stratégie pour projets Nx Monorepo
41
+ * - Structure apps/ et libs/
42
+ * - Routines globales séparées des targets apps/libs
43
+ * - Target "monorepo" pour règles de routine
44
+ */
45
+ class NxStrategy {
46
+ getTargetPath(projectInfo, targetName) {
47
+ // Essayer d'abord apps/, puis libs/
48
+ const appPath = path.join(projectInfo.path, 'apps', targetName);
49
+ const libPath = path.join(projectInfo.path, 'libs', targetName);
50
+ if (fs.existsSync(appPath)) {
51
+ return appPath;
52
+ }
53
+ return libPath;
54
+ }
55
+ getTargetType(projectInfo, targetName) {
56
+ // Si targetName === projectInfo.name ET projet a plusieurs apps/libs → target "monorepo"
57
+ const hasMultipleTargets = (projectInfo.apps?.length || 0) + (projectInfo.libs?.length || 0) > 1;
58
+ if (hasMultipleTargets && targetName === projectInfo.name) {
59
+ return 'monorepo';
60
+ }
61
+ // Vérifier dans les apps
62
+ if (projectInfo.apps?.some(app => app.name === targetName)) {
63
+ return 'app';
64
+ }
65
+ // Vérifier dans les libs
66
+ if (projectInfo.libs?.some(lib => lib.name === targetName)) {
67
+ return 'lib';
68
+ }
69
+ // Par défaut app
70
+ return 'app';
71
+ }
72
+ extractTargetFromFilePath(filePath, projectInfo) {
73
+ const normalizedPath = filePath.replace(/\\/g, '/');
74
+ // Nx monorepo : apps/XXX ou libs/XXX (ou libs/groupe/XXX pour structure groupée)
75
+ const appsMatch = normalizedPath.match(/^apps\/([^\/]+)/);
76
+ if (appsMatch)
77
+ return appsMatch[1];
78
+ // Support libs groupées : libs/apis/git-platform → "apis/git-platform"
79
+ const libsMatch = normalizedPath.match(/^libs\/(.*?)(?:\/|$)/);
80
+ if (libsMatch) {
81
+ // Extraire tout le chemin après libs/ jusqu'au premier fichier
82
+ const fullLibPath = normalizedPath.substring(5); // Enlever "libs/"
83
+ // Trouver la lib complète en cherchant dans projectInfo.libs
84
+ const matchingLib = projectInfo.libs?.find(lib => fullLibPath.startsWith(lib.name));
85
+ if (matchingLib) {
86
+ return matchingLib.name; // Ex: "apis/git-platform"
87
+ }
88
+ // Fallback : prendre premier segment (ancien comportement)
89
+ return libsMatch[1];
90
+ }
91
+ // Si le chemin ne correspond pas à apps/ ou libs/, c'est un fichier global (ex: package.json)
92
+ // Pour Nx monorepo, ces règles de routine vont dans le target "monorepo" (nom du projet)
93
+ return projectInfo.name;
94
+ }
95
+ shouldFilterRoutinesFromTarget(targetName, projectInfo) {
96
+ // Nx avec plusieurs targets : filtrer routines des apps/libs normaux
97
+ // Les routines vont dans le target "monorepo"
98
+ const hasMultipleTargets = (projectInfo.apps?.length || 0) + (projectInfo.libs?.length || 0) > 1;
99
+ const isMonorepoTarget = hasMultipleTargets && targetName === projectInfo.name;
100
+ // Filtrer routines pour apps/libs normaux, PAS pour target monorepo
101
+ return hasMultipleTargets && !isMonorepoTarget;
102
+ }
103
+ shouldDisplayMonorepoTarget(target) {
104
+ // Ne pas afficher target MONOREPO s'il contient SEULEMENT des routines
105
+ if (target.targetType !== 'monorepo') {
106
+ return true; // Toujours afficher apps/libs normaux
107
+ }
108
+ // Vérifier si target monorepo contient des règles non-routine
109
+ for (const migration of target.migrations) {
110
+ for (const priority of migration.priorities) {
111
+ for (const rule of priority.rules) {
112
+ if (rule.type !== 'routine') {
113
+ return true; // A au moins une règle non-routine
114
+ }
115
+ }
116
+ }
117
+ }
118
+ // Target monorepo ne contient QUE des routines → ne pas afficher
119
+ return false;
120
+ }
121
+ getTargetsList(projectInfo) {
122
+ const apps = projectInfo.apps || [];
123
+ const libs = projectInfo.libs || [];
124
+ return [...apps, ...libs];
125
+ }
126
+ isMultiTarget(projectInfo) {
127
+ return ((projectInfo.apps?.length || 0) + (projectInfo.libs?.length || 0)) > 1;
128
+ }
129
+ }
130
+ exports.NxStrategy = NxStrategy;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,74 @@
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.StandaloneStrategy = void 0;
37
+ const path = __importStar(require("path"));
38
+ /**
39
+ * Stratégie pour projets Angular Standalone
40
+ * - Structure src/ unique
41
+ * - Pas de séparation apps/libs
42
+ * - Routines intégrées au target principal
43
+ */
44
+ class StandaloneStrategy {
45
+ getTargetPath(projectInfo, targetName) {
46
+ // Projet standalone : toujours src/
47
+ return path.join(projectInfo.path, 'src');
48
+ }
49
+ getTargetType(projectInfo, targetName) {
50
+ // Projet standalone : toujours 'app'
51
+ return 'app';
52
+ }
53
+ extractTargetFromFilePath(filePath, projectInfo) {
54
+ // Projet standalone : tout va dans le projet principal
55
+ return projectInfo.name;
56
+ }
57
+ shouldFilterRoutinesFromTarget(targetName, projectInfo) {
58
+ // Standalone : pas de filtrage, routines incluses dans le target principal
59
+ return false;
60
+ }
61
+ shouldDisplayMonorepoTarget(target) {
62
+ // Standalone : pas de target monorepo, N/A
63
+ return true;
64
+ }
65
+ getTargetsList(projectInfo) {
66
+ // Standalone : un seul target (le projet lui-même)
67
+ return projectInfo.apps || [{ name: projectInfo.name }];
68
+ }
69
+ isMultiTarget(projectInfo) {
70
+ // Standalone : toujours false
71
+ return false;
72
+ }
73
+ }
74
+ exports.StandaloneStrategy = StandaloneStrategy;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProjectStrategy = createProjectStrategy;
4
+ const nx_strategy_1 = require("./nx-strategy");
5
+ const standalone_strategy_1 = require("./standalone-strategy");
6
+ /**
7
+ * Factory pour créer la stratégie appropriée selon le type de projet
8
+ */
9
+ function createProjectStrategy(projectInfo) {
10
+ if (projectInfo.type === 'nx-monorepo') {
11
+ return new nx_strategy_1.NxStrategy();
12
+ }
13
+ // 'angular-standalone' ou 'unknown' → Stratégie standalone par défaut
14
+ return new standalone_strategy_1.StandaloneStrategy();
15
+ }
@@ -0,0 +1,89 @@
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.loadMigrationRules = loadMigrationRules;
37
+ exports.getRulesForMigration = getRulesForMigration;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const core_1 = require("../utils/core");
41
+ /**
42
+ * Charge les règles de migration depuis la nouvelle architecture
43
+ * Lit tous les fichiers JSON dans src/data/rules/to{18,19,20}/
44
+ */
45
+ function loadMigrationRules() {
46
+ const rulesDir = path.join(__dirname, '..', 'data', 'rules');
47
+ if (!fs.existsSync(rulesDir)) {
48
+ throw new Error(`Répertoire des règles introuvable: ${rulesDir}`);
49
+ }
50
+ const migrations = {
51
+ 'to18': { obligatoire: [], recommande: [], optionnelle: [] },
52
+ 'to19': { obligatoire: [], recommande: [], optionnelle: [] },
53
+ 'to20': { obligatoire: [], recommande: [], optionnelle: [] }
54
+ };
55
+ // Charger les règles pour chaque migration
56
+ const migrationDirs = ['to18', 'to19', 'to20'];
57
+ for (const dir of migrationDirs) {
58
+ const migrationDir = path.join(rulesDir, dir);
59
+ if (!fs.existsSync(migrationDir))
60
+ continue;
61
+ // Charger chaque fichier de priorité
62
+ for (const priority of ['obligatoire', 'recommande', 'optionnelle']) {
63
+ const filename = `rules-${dir.replace('to', '')}-${priority}.json`;
64
+ const filePath = path.join(migrationDir, filename);
65
+ if (fs.existsSync(filePath)) {
66
+ try {
67
+ const rules = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
68
+ migrations[dir][priority] = rules;
69
+ }
70
+ catch (error) {
71
+ core_1.logger.warning(`Erreur lors du chargement de ${filePath}: ${error}`);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return migrations;
77
+ }
78
+ /**
79
+ * Charge les règles pour une migration spécifique (17→18, 18→19, 19→20)
80
+ */
81
+ function getRulesForMigration(migration) {
82
+ const allRules = loadMigrationRules();
83
+ const migrationRules = allRules[migration];
84
+ return [
85
+ ...migrationRules.obligatoire,
86
+ ...migrationRules.recommande,
87
+ ...migrationRules.optionnelle
88
+ ];
89
+ }
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logScanStart = logScanStart;
4
+ exports.logTargetStart = logTargetStart;
5
+ exports.logTargetComplete = logTargetComplete;
6
+ exports.logScanProgress = logScanProgress;
7
+ exports.logMethodStats = logMethodStats;
8
+ exports.logWorkloadReport = logWorkloadReport;
9
+ exports.logTopRules = logTopRules;
10
+ exports.logScanComplete = logScanComplete;
11
+ exports.logScannerInfo = logScannerInfo;
12
+ exports.logSpecialRulesInfo = logSpecialRulesInfo;
13
+ exports.logFilesDetected = logFilesDetected;
14
+ const core_1 = require("../utils/core");
15
+ const core_2 = require("../utils/core");
16
+ const project_strategy_1 = require("./project-strategy");
17
+ /**
18
+ * Reporter centralisé pour afficher la progression et les résultats du scan
19
+ */
20
+ /**
21
+ * Affiche la bannière de démarrage du scan
22
+ */
23
+ function logScanStart(projectInfo, scannerType, rules) {
24
+ core_1.logger.blank();
25
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
26
+ core_1.logger.info(' ANGULAR MIGRATION PLANIFICATOR');
27
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
28
+ core_1.logger.blank();
29
+ core_1.logger.step(`Analyse du projet: ${projectInfo.name}`);
30
+ core_1.logger.info(`Scanner: ${scannerType.toUpperCase()}`);
31
+ core_1.logger.success(`Projet détecté: ${projectInfo.name}`);
32
+ core_1.logger.info(`Type: ${projectInfo.type}`);
33
+ if (projectInfo.angularVersion) {
34
+ core_1.logger.info(`Version Angular: ${projectInfo.angularVersion}`);
35
+ }
36
+ if (projectInfo.apps && projectInfo.apps.length > 0) {
37
+ const appNames = projectInfo.apps.map(a => a.name).join(', ');
38
+ core_1.logger.info(`Applications: ${projectInfo.apps.length} (${appNames})`);
39
+ }
40
+ if (projectInfo.libs && projectInfo.libs.length > 0) {
41
+ const libCount = projectInfo.libs.length;
42
+ const libNames = projectInfo.libs.slice(0, 3).join(', ');
43
+ const moreText = libCount > 3 ? `, +${libCount - 3}` : '';
44
+ core_1.logger.info(`Librairies: ${libCount} (${libNames}${moreText})`);
45
+ }
46
+ core_1.logger.blank();
47
+ // Info sur les règles chargées
48
+ const migrations = new Set(rules.map(r => {
49
+ if (r.key.includes('_18_'))
50
+ return 'to18';
51
+ if (r.key.includes('_19_'))
52
+ return 'to19';
53
+ if (r.key.includes('_20_'))
54
+ return 'to20';
55
+ return 'unknown';
56
+ }));
57
+ const migrationList = Array.from(migrations).filter(m => m !== 'unknown').map(m => m.replace('to', '')).join(', ');
58
+ core_1.logger.step(`Chargement des règles (${migrationList})...`);
59
+ core_1.logger.success(`${rules.length} règles chargées`);
60
+ }
61
+ /**
62
+ * Affiche le début du scan d'un target
63
+ */
64
+ function logTargetStart(targetName, targetType) {
65
+ core_1.logger.blank();
66
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
67
+ core_1.logger.info(` SCAN: ${targetName.toUpperCase()} (${targetType})`);
68
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
69
+ core_1.logger.blank();
70
+ }
71
+ /**
72
+ * Affiche la fin du scan d'un target avec statistiques
73
+ */
74
+ function logTargetComplete(targetName, matches, duration) {
75
+ const uniqueRules = new Set(matches.map(m => m.ruleKey)).size;
76
+ const durationText = duration ? ` en ${(duration / 1000).toFixed(2)}s` : '';
77
+ core_1.logger.blank();
78
+ core_1.logger.success(`Target ${targetName}: ${matches.length} occurrences (${uniqueRules} règles actives)${durationText}`);
79
+ }
80
+ /**
81
+ * Affiche la progression du scan AST par batch
82
+ */
83
+ function logScanProgress(current, total, message) {
84
+ const progress = Math.round((current / total) * 100);
85
+ const spinner = getSpinner(current);
86
+ const extraMsg = message ? ` - ${message}` : '';
87
+ core_1.logger.debug(` Progression: ${progress}% (${current}/${total})${extraMsg} ${spinner}`);
88
+ }
89
+ /**
90
+ * Affiche les statistiques de détection par méthode (AST vs Regex)
91
+ */
92
+ function logMethodStats(astCount, regexCount, astRulesCount, regexRulesCount) {
93
+ const total = astCount + regexCount;
94
+ const astPercent = total > 0 ? ((astCount / total) * 100).toFixed(1) : '0.0';
95
+ const regexPercent = total > 0 ? ((regexCount / total) * 100).toFixed(1) : '0.0';
96
+ core_1.logger.blank();
97
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
98
+ core_1.logger.info(' RAPPORT DE DÉTECTION PAR MÉTHODE');
99
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
100
+ core_1.logger.blank();
101
+ core_1.logger.info(`Total occurrences: ${total}`);
102
+ core_1.logger.info(` • AST: ${astCount} (${astPercent}%)`);
103
+ core_1.logger.info(` • Regex: ${regexCount} (${regexPercent}%)`);
104
+ core_1.logger.blank();
105
+ core_1.logger.info(`Règles actives: ${astRulesCount + regexRulesCount}`);
106
+ core_1.logger.info(` • Avec AST: ${astRulesCount}`);
107
+ core_1.logger.info(` • Regex fallback: ${regexRulesCount}`);
108
+ core_1.logger.blank();
109
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
110
+ }
111
+ /**
112
+ * Affiche le rapport hiérarchique de charge de travail
113
+ */
114
+ function logWorkloadReport(report) {
115
+ core_1.logger.blank();
116
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
117
+ core_1.logger.info(' RAPPORT DE CHARGE DE TRAVAIL');
118
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
119
+ core_1.logger.blank();
120
+ core_1.logger.info(`📊 Charge totale estimée: ${(0, core_2.formatWorkloadTime)(report.totalTime)}`);
121
+ // Créer la stratégie pour déterminer l'affichage
122
+ const strategy = (0, project_strategy_1.createProjectStrategy)(report.projectInfo);
123
+ const isMultiTarget = strategy.isMultiTarget(report.projectInfo);
124
+ // Afficher résumé des routines pour monorepo multi-target
125
+ if (isMultiTarget && report.stats.totalRoutineRules > 0) {
126
+ const routineRules = collectRoutineRules(report);
127
+ if (routineRules.length > 0) {
128
+ core_1.logger.blank();
129
+ core_1.logger.info(`🔧 Règles de routine (${routineRules.length}) - Tâches globales monorepo:`);
130
+ for (const routine of routineRules) {
131
+ const timeStr = (0, core_2.formatWorkloadTime)(routine.totalTime).padEnd(15);
132
+ core_1.logger.debug(` • [ROUTINE] ${timeStr} → ${routine.ruleSummary}`);
133
+ }
134
+ }
135
+ }
136
+ core_1.logger.blank();
137
+ for (const target of report.targets) {
138
+ // Pour multi-target (Nx) : skip l'affichage des targets qui ne doivent pas être affichés
139
+ if (isMultiTarget && !strategy.shouldDisplayMonorepoTarget(target)) {
140
+ continue;
141
+ }
142
+ let targetIcon;
143
+ if (target.targetType === 'app') {
144
+ targetIcon = '📦 APP';
145
+ }
146
+ else if (target.targetType === 'lib') {
147
+ targetIcon = '📚 LIB';
148
+ }
149
+ else {
150
+ targetIcon = '🏗️ MONOREPO';
151
+ }
152
+ core_1.logger.info(`${targetIcon}: ${target.targetName} (${(0, core_2.formatWorkloadTime)(target.totalTime)})`);
153
+ core_1.logger.blank();
154
+ for (const migration of target.migrations) {
155
+ core_1.logger.info(` 🔄 ${migration.migration.toUpperCase()} (${(0, core_2.formatWorkloadTime)(migration.totalTime)})`);
156
+ for (const priority of migration.priorities) {
157
+ const priorityIcon = getPriorityIcon(priority.priority);
158
+ const rulesCount = priority.rules.length;
159
+ core_1.logger.info(` ${priorityIcon} ${priority.priority.toUpperCase()} (${(0, core_2.formatWorkloadTime)(priority.totalTime)}) - ${rulesCount} règles`);
160
+ // Afficher top 5 règles par priorité
161
+ const topRules = priority.rules.slice(0, 5);
162
+ for (const rule of topRules) {
163
+ let displayStr;
164
+ if (rule.type === 'routine') {
165
+ // Règle de routine : charge unique
166
+ const timeStr = (0, core_2.formatWorkloadTime)(rule.totalTime).padEnd(15);
167
+ displayStr = `[ROUTINE] 1 × ${timeStr} → ${rule.ruleSummary}`;
168
+ }
169
+ else if (rule.type === 'combined') {
170
+ // Règle combinée : AST + fichiers
171
+ const timeStr = (0, core_2.formatWorkloadTime)(rule.totalTime).padEnd(15);
172
+ if (rule.specialWorkloadFiles === 0) {
173
+ // Cas spécial : règle avec special_workload mais 0 fichiers trouvés
174
+ const countStr = `${rule.baseOccurrences}`.padStart(3);
175
+ displayStr = `[SPECIAL] ${countStr} × ${timeStr} → ${rule.ruleSummary}`;
176
+ }
177
+ else {
178
+ displayStr = `[SPECIAL] ${rule.baseOccurrences} occ. + ${rule.specialWorkloadFiles} fichiers = ${timeStr} → ${rule.ruleSummary}`;
179
+ }
180
+ }
181
+ else if (rule.type === 'special') {
182
+ // Règle special seulement (pas d'occurrences AST)
183
+ const timeStr = (0, core_2.formatWorkloadTime)(rule.totalTime).padEnd(15);
184
+ displayStr = `[SPECIAL] ${rule.specialWorkloadFiles} fichiers × ${timeStr} → ${rule.ruleSummary}`;
185
+ }
186
+ else {
187
+ // Règle standard (AST/Regex)
188
+ const countStr = `${rule.occurrences}`.padStart(3);
189
+ const timeStr = (0, core_2.formatWorkloadTime)(rule.totalTime).padEnd(15);
190
+ displayStr = `${countStr} × ${timeStr} → ${rule.ruleSummary}`;
191
+ }
192
+ core_1.logger.debug(` ${displayStr}`);
193
+ }
194
+ if (priority.rules.length > 5) {
195
+ core_1.logger.debug(` ... et ${priority.rules.length - 5} règles supplémentaires`);
196
+ }
197
+ core_1.logger.blank();
198
+ }
199
+ }
200
+ }
201
+ core_1.logger.info('═══════════════════════════════════════════════════════════');
202
+ }
203
+ /**
204
+ * Affiche le top N des règles les plus fréquentes
205
+ */
206
+ function logTopRules(matches, topN = 10) {
207
+ if (matches.length === 0) {
208
+ return;
209
+ }
210
+ core_1.logger.blank();
211
+ core_1.logger.step(`Top ${topN} règles les plus fréquentes:`);
212
+ const ruleCount = new Map();
213
+ for (const match of matches) {
214
+ const existing = ruleCount.get(match.ruleKey);
215
+ if (existing) {
216
+ existing.count++;
217
+ }
218
+ else {
219
+ ruleCount.set(match.ruleKey, { summary: match.ruleSummary, count: 1 });
220
+ }
221
+ }
222
+ const topRules = Array.from(ruleCount.entries())
223
+ .sort((a, b) => b[1].count - a[1].count)
224
+ .slice(0, topN);
225
+ for (const [index, [ruleKey, data]] of topRules.entries()) {
226
+ core_1.logger.debug(` ${index + 1}. ${data.summary}: ${data.count} occurrences`);
227
+ }
228
+ }
229
+ /**
230
+ * Affiche la fin du scan global avec statistiques enrichies
231
+ */
232
+ function logScanComplete(totalDuration, totalMatches, routineCount, specialWorkloadFilesCount) {
233
+ core_1.logger.blank();
234
+ if (totalDuration) {
235
+ core_1.logger.success(`Analyse terminée en ${(totalDuration / 1000).toFixed(2)}s`);
236
+ }
237
+ else {
238
+ core_1.logger.success('Analyse terminée');
239
+ }
240
+ if (totalMatches !== undefined) {
241
+ core_1.logger.success(` • Occurrences détectées (AST/Regex): ${totalMatches}`);
242
+ }
243
+ if (specialWorkloadFilesCount && specialWorkloadFilesCount > 0) {
244
+ core_1.logger.success(` • Fichiers comptés (special_workload): ${specialWorkloadFilesCount}`);
245
+ }
246
+ if (routineCount && routineCount > 0) {
247
+ core_1.logger.success(` • Tâches de routine identifiées: ${routineCount}`);
248
+ }
249
+ core_1.logger.blank();
250
+ }
251
+ /**
252
+ * Affiche les informations sur le scanner utilisé
253
+ */
254
+ function logScannerInfo(astRulesCount, regexRulesCount) {
255
+ core_1.logger.info(`Scanner: ${astRulesCount} règles AST, ${regexRulesCount} règles Regex (fallback)`);
256
+ }
257
+ /**
258
+ * Affiche les compteurs de règles spéciales (special_workload et routines)
259
+ */
260
+ function logSpecialRulesInfo(specialWorkloadCount, routineCount) {
261
+ if (specialWorkloadCount > 0) {
262
+ core_1.logger.info(`📦 ${specialWorkloadCount} règle(s) avec special_workload détectée(s)`);
263
+ }
264
+ if (routineCount > 0) {
265
+ core_1.logger.info(`🔧 ${routineCount} règle(s) de routine détectée(s)`);
266
+ }
267
+ }
268
+ /**
269
+ * Affiche les fichiers détectés
270
+ */
271
+ function logFilesDetected(fileType, count) {
272
+ core_1.logger.info(`Fichiers ${fileType}: ${count} détectés`);
273
+ }
274
+ /**
275
+ * Retourne l'icône correspondant à une priorité
276
+ */
277
+ function getPriorityIcon(priority) {
278
+ switch (priority) {
279
+ case 'obligatoire':
280
+ return '🔴';
281
+ case 'recommande':
282
+ return '🟡';
283
+ case 'optionnelle':
284
+ return '🟢';
285
+ default:
286
+ return '⚪';
287
+ }
288
+ }
289
+ /**
290
+ * Retourne un caractère de spinner pour animer la progression
291
+ */
292
+ function getSpinner(step) {
293
+ const spinners = ['⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽', '⣾'];
294
+ return spinners[step % spinners.length];
295
+ }
296
+ /**
297
+ * Collecte toutes les règles de routine depuis le rapport (parcours hiérarchique)
298
+ */
299
+ function collectRoutineRules(report) {
300
+ const routineRules = [];
301
+ for (const target of report.targets) {
302
+ for (const migration of target.migrations) {
303
+ for (const priority of migration.priorities) {
304
+ for (const rule of priority.rules) {
305
+ if (rule.type === 'routine') {
306
+ routineRules.push({
307
+ ruleSummary: rule.ruleSummary,
308
+ totalTime: rule.totalTime
309
+ });
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ return routineRules;
316
+ }