archicore 0.1.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/README.md +530 -0
- package/dist/analyzers/dead-code.d.ts +95 -0
- package/dist/analyzers/dead-code.js +327 -0
- package/dist/analyzers/duplication.d.ts +90 -0
- package/dist/analyzers/duplication.js +344 -0
- package/dist/analyzers/security.d.ts +79 -0
- package/dist/analyzers/security.js +484 -0
- package/dist/architecture/index.d.ts +35 -0
- package/dist/architecture/index.js +249 -0
- package/dist/cli/commands/analyzers.d.ts +6 -0
- package/dist/cli/commands/analyzers.js +431 -0
- package/dist/cli/commands/export.d.ts +6 -0
- package/dist/cli/commands/export.js +78 -0
- package/dist/cli/commands/index.d.ts +8 -0
- package/dist/cli/commands/index.js +8 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +140 -0
- package/dist/cli/commands/interactive.d.ts +7 -0
- package/dist/cli/commands/interactive.js +522 -0
- package/dist/cli/commands/projects.d.ts +6 -0
- package/dist/cli/commands/projects.js +249 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/ui/box.d.ts +17 -0
- package/dist/cli/ui/box.js +62 -0
- package/dist/cli/ui/colors.d.ts +49 -0
- package/dist/cli/ui/colors.js +86 -0
- package/dist/cli/ui/index.d.ts +9 -0
- package/dist/cli/ui/index.js +9 -0
- package/dist/cli/ui/prompt.d.ts +34 -0
- package/dist/cli/ui/prompt.js +122 -0
- package/dist/cli/ui/spinner.d.ts +29 -0
- package/dist/cli/ui/spinner.js +80 -0
- package/dist/cli/ui/table.d.ts +33 -0
- package/dist/cli/ui/table.js +84 -0
- package/dist/cli/utils/config.d.ts +23 -0
- package/dist/cli/utils/config.js +73 -0
- package/dist/cli/utils/index.d.ts +6 -0
- package/dist/cli/utils/index.js +6 -0
- package/dist/cli/utils/session.d.ts +27 -0
- package/dist/cli/utils/session.js +117 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +295 -0
- package/dist/code-index/ast-parser.d.ts +16 -0
- package/dist/code-index/ast-parser.js +330 -0
- package/dist/code-index/dependency-graph.d.ts +16 -0
- package/dist/code-index/dependency-graph.js +161 -0
- package/dist/code-index/index.d.ts +44 -0
- package/dist/code-index/index.js +124 -0
- package/dist/code-index/symbol-extractor.d.ts +13 -0
- package/dist/code-index/symbol-extractor.js +150 -0
- package/dist/export/index.d.ts +92 -0
- package/dist/export/index.js +676 -0
- package/dist/github/github-service.d.ts +146 -0
- package/dist/github/github-service.js +609 -0
- package/dist/impact-engine/index.d.ts +25 -0
- package/dist/impact-engine/index.js +284 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +149 -0
- package/dist/metrics/index.d.ts +136 -0
- package/dist/metrics/index.js +525 -0
- package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
- package/dist/orchestrator/deepseek-optimizer.js +320 -0
- package/dist/orchestrator/index.d.ts +34 -0
- package/dist/orchestrator/index.js +305 -0
- package/dist/pr-guardian/index.d.ts +143 -0
- package/dist/pr-guardian/index.js +553 -0
- package/dist/refactoring/index.d.ts +108 -0
- package/dist/refactoring/index.js +580 -0
- package/dist/rules-engine/index.d.ts +129 -0
- package/dist/rules-engine/index.js +482 -0
- package/dist/semantic-memory/embedding-service.d.ts +24 -0
- package/dist/semantic-memory/embedding-service.js +120 -0
- package/dist/semantic-memory/index.d.ts +45 -0
- package/dist/semantic-memory/index.js +206 -0
- package/dist/semantic-memory/vector-store.d.ts +27 -0
- package/dist/semantic-memory/vector-store.js +166 -0
- package/dist/server/index.d.ts +28 -0
- package/dist/server/index.js +141 -0
- package/dist/server/middleware/api-auth.d.ts +43 -0
- package/dist/server/middleware/api-auth.js +256 -0
- package/dist/server/routes/admin.d.ts +5 -0
- package/dist/server/routes/admin.js +123 -0
- package/dist/server/routes/api.d.ts +7 -0
- package/dist/server/routes/api.js +362 -0
- package/dist/server/routes/auth.d.ts +16 -0
- package/dist/server/routes/auth.js +191 -0
- package/dist/server/routes/developer.d.ts +8 -0
- package/dist/server/routes/developer.js +439 -0
- package/dist/server/routes/github.d.ts +7 -0
- package/dist/server/routes/github.js +495 -0
- package/dist/server/routes/upload.d.ts +7 -0
- package/dist/server/routes/upload.js +196 -0
- package/dist/server/services/api-key-service.d.ts +81 -0
- package/dist/server/services/api-key-service.js +281 -0
- package/dist/server/services/auth-service.d.ts +40 -0
- package/dist/server/services/auth-service.js +315 -0
- package/dist/server/services/project-service.d.ts +123 -0
- package/dist/server/services/project-service.js +533 -0
- package/dist/server/services/token-service.d.ts +107 -0
- package/dist/server/services/token-service.js +416 -0
- package/dist/server/services/upload-service.d.ts +93 -0
- package/dist/server/services/upload-service.js +464 -0
- package/dist/types/api.d.ts +188 -0
- package/dist/types/api.js +86 -0
- package/dist/types/github.d.ts +335 -0
- package/dist/types/github.js +5 -0
- package/dist/types/index.d.ts +265 -0
- package/dist/types/index.js +32 -0
- package/dist/types/user.d.ts +69 -0
- package/dist/types/user.js +42 -0
- package/dist/utils/file-utils.d.ts +20 -0
- package/dist/utils/file-utils.js +163 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.js +41 -0
- package/dist/watcher/index.d.ts +125 -0
- package/dist/watcher/index.js +397 -0
- package/package.json +71 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export/Import System
|
|
3
|
+
*
|
|
4
|
+
* Форматы экспорта:
|
|
5
|
+
* - JSON (полный)
|
|
6
|
+
* - HTML отчёт
|
|
7
|
+
* - Markdown отчёт
|
|
8
|
+
* - PDF (через HTML)
|
|
9
|
+
* - CSV (метрики)
|
|
10
|
+
* - GraphML (граф зависимостей)
|
|
11
|
+
*/
|
|
12
|
+
import { Logger } from '../utils/logger.js';
|
|
13
|
+
const translations = {
|
|
14
|
+
en: {
|
|
15
|
+
reportTitle: 'Architecture Analysis Report',
|
|
16
|
+
generatedOn: 'Generated on',
|
|
17
|
+
projectOverview: 'Project Overview',
|
|
18
|
+
totalFiles: 'Total Files',
|
|
19
|
+
totalSymbols: 'Total Symbols',
|
|
20
|
+
dependencies: 'Dependencies',
|
|
21
|
+
circularDeps: 'Circular Dependencies',
|
|
22
|
+
metricsSection: 'Code Metrics',
|
|
23
|
+
totalLOC: 'Total Lines of Code',
|
|
24
|
+
avgComplexity: 'Average Complexity',
|
|
25
|
+
maintainabilityIndex: 'Maintainability Index',
|
|
26
|
+
hotspots: 'Hotspots',
|
|
27
|
+
rulesSection: 'Architecture Rules',
|
|
28
|
+
rulesPassed: 'Rules Passed',
|
|
29
|
+
violations: 'Violations',
|
|
30
|
+
securitySection: 'Security Analysis',
|
|
31
|
+
vulnerabilities: 'Vulnerabilities',
|
|
32
|
+
critical: 'Critical',
|
|
33
|
+
high: 'High',
|
|
34
|
+
medium: 'Medium',
|
|
35
|
+
low: 'Low',
|
|
36
|
+
hardcodedSecrets: 'Hardcoded Secrets',
|
|
37
|
+
deadCodeSection: 'Dead Code',
|
|
38
|
+
unusedExports: 'Unused Exports',
|
|
39
|
+
unusedVariables: 'Unused Variables',
|
|
40
|
+
unreachableCode: 'Unreachable Code',
|
|
41
|
+
duplicationSection: 'Code Duplication',
|
|
42
|
+
totalClones: 'Total Clones',
|
|
43
|
+
duplicationPercentage: 'Duplication Percentage',
|
|
44
|
+
estimatedRefactorTime: 'Estimated Refactor Time',
|
|
45
|
+
hours: 'hours',
|
|
46
|
+
file: 'File',
|
|
47
|
+
line: 'Line',
|
|
48
|
+
type: 'Type',
|
|
49
|
+
severity: 'Severity',
|
|
50
|
+
message: 'Message',
|
|
51
|
+
recommendation: 'Recommendation'
|
|
52
|
+
},
|
|
53
|
+
ru: {
|
|
54
|
+
reportTitle: 'Отчёт об анализе архитектуры',
|
|
55
|
+
generatedOn: 'Сформирован',
|
|
56
|
+
projectOverview: 'Обзор проекта',
|
|
57
|
+
totalFiles: 'Всего файлов',
|
|
58
|
+
totalSymbols: 'Всего символов',
|
|
59
|
+
dependencies: 'Зависимостей',
|
|
60
|
+
circularDeps: 'Циклических зависимостей',
|
|
61
|
+
metricsSection: 'Метрики кода',
|
|
62
|
+
totalLOC: 'Всего строк кода',
|
|
63
|
+
avgComplexity: 'Средняя сложность',
|
|
64
|
+
maintainabilityIndex: 'Индекс поддерживаемости',
|
|
65
|
+
hotspots: 'Проблемные места',
|
|
66
|
+
rulesSection: 'Правила архитектуры',
|
|
67
|
+
rulesPassed: 'Правил выполнено',
|
|
68
|
+
violations: 'Нарушений',
|
|
69
|
+
securitySection: 'Анализ безопасности',
|
|
70
|
+
vulnerabilities: 'Уязвимостей',
|
|
71
|
+
critical: 'Критических',
|
|
72
|
+
high: 'Высоких',
|
|
73
|
+
medium: 'Средних',
|
|
74
|
+
low: 'Низких',
|
|
75
|
+
hardcodedSecrets: 'Захардкоженных секретов',
|
|
76
|
+
deadCodeSection: 'Мёртвый код',
|
|
77
|
+
unusedExports: 'Неиспользуемых экспортов',
|
|
78
|
+
unusedVariables: 'Неиспользуемых переменных',
|
|
79
|
+
unreachableCode: 'Недостижимый код',
|
|
80
|
+
duplicationSection: 'Дублирование кода',
|
|
81
|
+
totalClones: 'Всего клонов',
|
|
82
|
+
duplicationPercentage: 'Процент дублирования',
|
|
83
|
+
estimatedRefactorTime: 'Время на рефакторинг',
|
|
84
|
+
hours: 'часов',
|
|
85
|
+
file: 'Файл',
|
|
86
|
+
line: 'Строка',
|
|
87
|
+
type: 'Тип',
|
|
88
|
+
severity: 'Серьёзность',
|
|
89
|
+
message: 'Сообщение',
|
|
90
|
+
recommendation: 'Рекомендация'
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
export class ExportManager {
|
|
94
|
+
version = '1.0.0';
|
|
95
|
+
/**
|
|
96
|
+
* Экспорт данных в указанный формат
|
|
97
|
+
*/
|
|
98
|
+
async export(data, options) {
|
|
99
|
+
Logger.progress(`Exporting to ${options.format}...`);
|
|
100
|
+
let result;
|
|
101
|
+
switch (options.format) {
|
|
102
|
+
case 'json':
|
|
103
|
+
result = this.exportJSON(data, options);
|
|
104
|
+
break;
|
|
105
|
+
case 'html':
|
|
106
|
+
result = this.exportHTML(data, options);
|
|
107
|
+
break;
|
|
108
|
+
case 'markdown':
|
|
109
|
+
result = this.exportMarkdown(data, options);
|
|
110
|
+
break;
|
|
111
|
+
case 'csv':
|
|
112
|
+
result = this.exportCSV(data);
|
|
113
|
+
break;
|
|
114
|
+
case 'graphml':
|
|
115
|
+
result = this.exportGraphML(data);
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
throw new Error(`Unsupported export format: ${options.format}`);
|
|
119
|
+
}
|
|
120
|
+
Logger.success(`Export completed (${result.length} bytes)`);
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Импорт данных из JSON
|
|
125
|
+
*/
|
|
126
|
+
async import(jsonString) {
|
|
127
|
+
Logger.progress('Importing data...');
|
|
128
|
+
try {
|
|
129
|
+
const parsed = JSON.parse(jsonString);
|
|
130
|
+
// Валидация
|
|
131
|
+
const errors = [];
|
|
132
|
+
if (!parsed.projectName) {
|
|
133
|
+
errors.push('Missing projectName');
|
|
134
|
+
}
|
|
135
|
+
if (!parsed.exportDate) {
|
|
136
|
+
errors.push('Missing exportDate');
|
|
137
|
+
}
|
|
138
|
+
// Восстанавливаем Map из объектов
|
|
139
|
+
if (parsed.symbols && typeof parsed.symbols === 'object') {
|
|
140
|
+
parsed.symbols = new Map(Object.entries(parsed.symbols));
|
|
141
|
+
}
|
|
142
|
+
if (errors.length > 0) {
|
|
143
|
+
return { success: false, errors };
|
|
144
|
+
}
|
|
145
|
+
Logger.success('Import completed');
|
|
146
|
+
return { success: true, data: parsed };
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
errors: [`Parse error: ${error instanceof Error ? error.message : 'Unknown error'}`]
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Экспорт в JSON
|
|
157
|
+
*/
|
|
158
|
+
exportJSON(data, options) {
|
|
159
|
+
const exportObj = {
|
|
160
|
+
projectName: data.projectName,
|
|
161
|
+
exportDate: data.exportDate,
|
|
162
|
+
version: this.version
|
|
163
|
+
};
|
|
164
|
+
if (options.includeGraph && data.graph) {
|
|
165
|
+
exportObj.graph = {
|
|
166
|
+
nodes: data.graph.nodes,
|
|
167
|
+
edges: Object.fromEntries(data.graph.edges)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (options.includeSymbols && data.symbols) {
|
|
171
|
+
exportObj.symbols = Object.fromEntries(data.symbols);
|
|
172
|
+
}
|
|
173
|
+
if (options.includeMetrics && data.metrics) {
|
|
174
|
+
exportObj.metrics = data.metrics;
|
|
175
|
+
}
|
|
176
|
+
if (options.includeRules && data.rules) {
|
|
177
|
+
exportObj.rules = data.rules;
|
|
178
|
+
}
|
|
179
|
+
if (options.includeDeadCode && data.deadCode) {
|
|
180
|
+
exportObj.deadCode = data.deadCode;
|
|
181
|
+
}
|
|
182
|
+
if (options.includeDuplication && data.duplication) {
|
|
183
|
+
exportObj.duplication = data.duplication;
|
|
184
|
+
}
|
|
185
|
+
if (options.includeSecurity && data.security) {
|
|
186
|
+
exportObj.security = data.security;
|
|
187
|
+
}
|
|
188
|
+
return JSON.stringify(exportObj, null, 2);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Экспорт в HTML
|
|
192
|
+
*/
|
|
193
|
+
exportHTML(data, options) {
|
|
194
|
+
const t = translations[options.language || 'en'];
|
|
195
|
+
let html = `<!DOCTYPE html>
|
|
196
|
+
<html lang="${options.language || 'en'}">
|
|
197
|
+
<head>
|
|
198
|
+
<meta charset="UTF-8">
|
|
199
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
200
|
+
<title>${t.reportTitle} - ${data.projectName}</title>
|
|
201
|
+
<style>
|
|
202
|
+
:root {
|
|
203
|
+
--bg: #0a0a0f;
|
|
204
|
+
--card-bg: #12121a;
|
|
205
|
+
--border: #1e1e2e;
|
|
206
|
+
--text: #e4e4e7;
|
|
207
|
+
--text-muted: #71717a;
|
|
208
|
+
--primary: #8b5cf6;
|
|
209
|
+
--success: #22c55e;
|
|
210
|
+
--warning: #f59e0b;
|
|
211
|
+
--error: #ef4444;
|
|
212
|
+
}
|
|
213
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
214
|
+
body {
|
|
215
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
216
|
+
background: var(--bg);
|
|
217
|
+
color: var(--text);
|
|
218
|
+
line-height: 1.6;
|
|
219
|
+
padding: 2rem;
|
|
220
|
+
}
|
|
221
|
+
.container { max-width: 1200px; margin: 0 auto; }
|
|
222
|
+
h1 { color: var(--primary); margin-bottom: 0.5rem; }
|
|
223
|
+
h2 { color: var(--text); margin: 2rem 0 1rem; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; }
|
|
224
|
+
.meta { color: var(--text-muted); margin-bottom: 2rem; }
|
|
225
|
+
.card {
|
|
226
|
+
background: var(--card-bg);
|
|
227
|
+
border: 1px solid var(--border);
|
|
228
|
+
border-radius: 8px;
|
|
229
|
+
padding: 1.5rem;
|
|
230
|
+
margin-bottom: 1rem;
|
|
231
|
+
}
|
|
232
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; }
|
|
233
|
+
.stat { text-align: center; }
|
|
234
|
+
.stat-value { font-size: 2rem; font-weight: bold; color: var(--primary); }
|
|
235
|
+
.stat-label { color: var(--text-muted); font-size: 0.875rem; }
|
|
236
|
+
table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
|
|
237
|
+
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid var(--border); }
|
|
238
|
+
th { color: var(--text-muted); font-weight: 500; }
|
|
239
|
+
.badge {
|
|
240
|
+
display: inline-block;
|
|
241
|
+
padding: 0.25rem 0.5rem;
|
|
242
|
+
border-radius: 4px;
|
|
243
|
+
font-size: 0.75rem;
|
|
244
|
+
font-weight: 500;
|
|
245
|
+
}
|
|
246
|
+
.badge-critical { background: var(--error); color: white; }
|
|
247
|
+
.badge-high { background: #dc2626; color: white; }
|
|
248
|
+
.badge-medium { background: var(--warning); color: black; }
|
|
249
|
+
.badge-low { background: var(--success); color: white; }
|
|
250
|
+
.progress { height: 8px; background: var(--border); border-radius: 4px; overflow: hidden; }
|
|
251
|
+
.progress-bar { height: 100%; background: var(--primary); }
|
|
252
|
+
</style>
|
|
253
|
+
</head>
|
|
254
|
+
<body>
|
|
255
|
+
<div class="container">
|
|
256
|
+
<h1>${t.reportTitle}</h1>
|
|
257
|
+
<p class="meta">${data.projectName} | ${t.generatedOn}: ${new Date(data.exportDate).toLocaleString()}</p>
|
|
258
|
+
`;
|
|
259
|
+
// Project Overview
|
|
260
|
+
if (data.graph) {
|
|
261
|
+
const totalDeps = Array.from(data.graph.edges.values()).reduce((sum, e) => sum + e.length, 0);
|
|
262
|
+
html += `
|
|
263
|
+
<h2>${t.projectOverview}</h2>
|
|
264
|
+
<div class="card">
|
|
265
|
+
<div class="grid">
|
|
266
|
+
<div class="stat">
|
|
267
|
+
<div class="stat-value">${data.graph.nodes.size}</div>
|
|
268
|
+
<div class="stat-label">${t.totalFiles}</div>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="stat">
|
|
271
|
+
<div class="stat-value">${data.symbols?.size || 0}</div>
|
|
272
|
+
<div class="stat-label">${t.totalSymbols}</div>
|
|
273
|
+
</div>
|
|
274
|
+
<div class="stat">
|
|
275
|
+
<div class="stat-value">${totalDeps}</div>
|
|
276
|
+
<div class="stat-label">${t.dependencies}</div>
|
|
277
|
+
</div>
|
|
278
|
+
<div class="stat">
|
|
279
|
+
<div class="stat-value">0</div>
|
|
280
|
+
<div class="stat-label">${t.circularDeps}</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
286
|
+
// Metrics
|
|
287
|
+
if (data.metrics) {
|
|
288
|
+
html += `
|
|
289
|
+
<h2>${t.metricsSection}</h2>
|
|
290
|
+
<div class="card">
|
|
291
|
+
<div class="grid">
|
|
292
|
+
<div class="stat">
|
|
293
|
+
<div class="stat-value">${data.metrics.summary.totalLOC.toLocaleString()}</div>
|
|
294
|
+
<div class="stat-label">${t.totalLOC}</div>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="stat">
|
|
297
|
+
<div class="stat-value">${data.metrics.summary.avgComplexity.toFixed(1)}</div>
|
|
298
|
+
<div class="stat-label">${t.avgComplexity}</div>
|
|
299
|
+
</div>
|
|
300
|
+
<div class="stat">
|
|
301
|
+
<div class="stat-value">${data.metrics.summary.avgMaintainability.toFixed(0)}</div>
|
|
302
|
+
<div class="stat-label">${t.maintainabilityIndex}</div>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
`;
|
|
307
|
+
if (data.metrics.hotspots.length > 0) {
|
|
308
|
+
html += `
|
|
309
|
+
<h3>${t.hotspots}</h3>
|
|
310
|
+
<div class="card">
|
|
311
|
+
<table>
|
|
312
|
+
<thead><tr><th>${t.file}</th><th>Score</th></tr></thead>
|
|
313
|
+
<tbody>
|
|
314
|
+
${data.metrics.hotspots.slice(0, 10).map(h => `
|
|
315
|
+
<tr><td>${h.filePath}</td><td>${h.score.toFixed(0)}</td></tr>
|
|
316
|
+
`).join('')}
|
|
317
|
+
</tbody>
|
|
318
|
+
</table>
|
|
319
|
+
</div>
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Security
|
|
324
|
+
if (data.security) {
|
|
325
|
+
html += `
|
|
326
|
+
<h2>${t.securitySection}</h2>
|
|
327
|
+
<div class="card">
|
|
328
|
+
<div class="grid">
|
|
329
|
+
<div class="stat">
|
|
330
|
+
<div class="stat-value" style="color: var(--error)">${data.security.summary.critical}</div>
|
|
331
|
+
<div class="stat-label">${t.critical}</div>
|
|
332
|
+
</div>
|
|
333
|
+
<div class="stat">
|
|
334
|
+
<div class="stat-value" style="color: #dc2626">${data.security.summary.high}</div>
|
|
335
|
+
<div class="stat-label">${t.high}</div>
|
|
336
|
+
</div>
|
|
337
|
+
<div class="stat">
|
|
338
|
+
<div class="stat-value" style="color: var(--warning)">${data.security.summary.medium}</div>
|
|
339
|
+
<div class="stat-label">${t.medium}</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="stat">
|
|
342
|
+
<div class="stat-value" style="color: var(--success)">${data.security.summary.low}</div>
|
|
343
|
+
<div class="stat-label">${t.low}</div>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
`;
|
|
348
|
+
if (data.security.vulnerabilities.length > 0) {
|
|
349
|
+
html += `
|
|
350
|
+
<div class="card">
|
|
351
|
+
<table>
|
|
352
|
+
<thead><tr><th>${t.file}</th><th>${t.line}</th><th>${t.type}</th><th>${t.severity}</th></tr></thead>
|
|
353
|
+
<tbody>
|
|
354
|
+
${data.security.vulnerabilities.slice(0, 20).map(v => `
|
|
355
|
+
<tr>
|
|
356
|
+
<td>${v.file}</td>
|
|
357
|
+
<td>${v.line}</td>
|
|
358
|
+
<td>${v.type}</td>
|
|
359
|
+
<td><span class="badge badge-${v.severity}">${v.severity}</span></td>
|
|
360
|
+
</tr>
|
|
361
|
+
`).join('')}
|
|
362
|
+
</tbody>
|
|
363
|
+
</table>
|
|
364
|
+
</div>
|
|
365
|
+
`;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Rules
|
|
369
|
+
if (data.rules) {
|
|
370
|
+
html += `
|
|
371
|
+
<h2>${t.rulesSection}</h2>
|
|
372
|
+
<div class="card">
|
|
373
|
+
<div class="grid">
|
|
374
|
+
<div class="stat">
|
|
375
|
+
<div class="stat-value" style="color: var(--success)">${data.rules.passed}</div>
|
|
376
|
+
<div class="stat-label">${t.rulesPassed}</div>
|
|
377
|
+
</div>
|
|
378
|
+
<div class="stat">
|
|
379
|
+
<div class="stat-value" style="color: var(--error)">${data.rules.violations.length}</div>
|
|
380
|
+
<div class="stat-label">${t.violations}</div>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
// Dead Code
|
|
387
|
+
if (data.deadCode) {
|
|
388
|
+
html += `
|
|
389
|
+
<h2>${t.deadCodeSection}</h2>
|
|
390
|
+
<div class="card">
|
|
391
|
+
<div class="grid">
|
|
392
|
+
<div class="stat">
|
|
393
|
+
<div class="stat-value">${data.deadCode.summary.unusedExportsCount}</div>
|
|
394
|
+
<div class="stat-label">${t.unusedExports}</div>
|
|
395
|
+
</div>
|
|
396
|
+
<div class="stat">
|
|
397
|
+
<div class="stat-value">${data.deadCode.summary.unusedVariablesCount}</div>
|
|
398
|
+
<div class="stat-label">${t.unusedVariables}</div>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="stat">
|
|
401
|
+
<div class="stat-value">${data.deadCode.summary.unreachableCodeLines}</div>
|
|
402
|
+
<div class="stat-label">${t.unreachableCode}</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
408
|
+
// Duplication
|
|
409
|
+
if (data.duplication) {
|
|
410
|
+
html += `
|
|
411
|
+
<h2>${t.duplicationSection}</h2>
|
|
412
|
+
<div class="card">
|
|
413
|
+
<div class="grid">
|
|
414
|
+
<div class="stat">
|
|
415
|
+
<div class="stat-value">${data.duplication.summary.totalClones}</div>
|
|
416
|
+
<div class="stat-label">${t.totalClones}</div>
|
|
417
|
+
</div>
|
|
418
|
+
<div class="stat">
|
|
419
|
+
<div class="stat-value">${data.duplication.summary.duplicationPercentage}%</div>
|
|
420
|
+
<div class="stat-label">${t.duplicationPercentage}</div>
|
|
421
|
+
</div>
|
|
422
|
+
<div class="stat">
|
|
423
|
+
<div class="stat-value">${data.duplication.summary.estimatedRefactorHours}</div>
|
|
424
|
+
<div class="stat-label">${t.estimatedRefactorTime} (${t.hours})</div>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
`;
|
|
429
|
+
}
|
|
430
|
+
html += `
|
|
431
|
+
</div>
|
|
432
|
+
</body>
|
|
433
|
+
</html>`;
|
|
434
|
+
return html;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Экспорт в Markdown
|
|
438
|
+
*/
|
|
439
|
+
exportMarkdown(data, options) {
|
|
440
|
+
const t = translations[options.language || 'en'];
|
|
441
|
+
let md = `# ${t.reportTitle}
|
|
442
|
+
|
|
443
|
+
**${data.projectName}**
|
|
444
|
+
${t.generatedOn}: ${new Date(data.exportDate).toLocaleString()}
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
`;
|
|
449
|
+
// Overview
|
|
450
|
+
if (data.graph) {
|
|
451
|
+
const totalDeps = Array.from(data.graph.edges.values()).reduce((sum, e) => sum + e.length, 0);
|
|
452
|
+
md += `## ${t.projectOverview}
|
|
453
|
+
|
|
454
|
+
| Metric | Value |
|
|
455
|
+
|--------|-------|
|
|
456
|
+
| ${t.totalFiles} | ${data.graph.nodes.size} |
|
|
457
|
+
| ${t.totalSymbols} | ${data.symbols?.size || 0} |
|
|
458
|
+
| ${t.dependencies} | ${totalDeps} |
|
|
459
|
+
| ${t.circularDeps} | 0 |
|
|
460
|
+
|
|
461
|
+
`;
|
|
462
|
+
}
|
|
463
|
+
// Metrics
|
|
464
|
+
if (data.metrics) {
|
|
465
|
+
md += `## ${t.metricsSection}
|
|
466
|
+
|
|
467
|
+
| Metric | Value |
|
|
468
|
+
|--------|-------|
|
|
469
|
+
| ${t.totalLOC} | ${data.metrics.summary.totalLOC.toLocaleString()} |
|
|
470
|
+
| ${t.avgComplexity} | ${data.metrics.summary.avgComplexity.toFixed(1)} |
|
|
471
|
+
| ${t.maintainabilityIndex} | ${data.metrics.summary.avgMaintainability.toFixed(0)} |
|
|
472
|
+
|
|
473
|
+
`;
|
|
474
|
+
if (data.metrics.hotspots.length > 0) {
|
|
475
|
+
md += `### ${t.hotspots}
|
|
476
|
+
|
|
477
|
+
| ${t.file} | Score |
|
|
478
|
+
|------|-------|
|
|
479
|
+
${data.metrics.hotspots.slice(0, 10).map(h => `| ${h.filePath} | ${h.score.toFixed(0)} |`).join('\n')}
|
|
480
|
+
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Security
|
|
485
|
+
if (data.security) {
|
|
486
|
+
md += `## ${t.securitySection}
|
|
487
|
+
|
|
488
|
+
| ${t.severity} | Count |
|
|
489
|
+
|----------|-------|
|
|
490
|
+
| ${t.critical} | ${data.security.summary.critical} |
|
|
491
|
+
| ${t.high} | ${data.security.summary.high} |
|
|
492
|
+
| ${t.medium} | ${data.security.summary.medium} |
|
|
493
|
+
| ${t.low} | ${data.security.summary.low} |
|
|
494
|
+
|
|
495
|
+
`;
|
|
496
|
+
if (data.security.vulnerabilities.length > 0) {
|
|
497
|
+
md += `### ${t.vulnerabilities}
|
|
498
|
+
|
|
499
|
+
| ${t.file} | ${t.line} | ${t.type} | ${t.severity} |
|
|
500
|
+
|------|------|------|----------|
|
|
501
|
+
${data.security.vulnerabilities.slice(0, 20).map(v => `| ${v.file} | ${v.line} | ${v.type} | ${v.severity} |`).join('\n')}
|
|
502
|
+
|
|
503
|
+
`;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
// Rules
|
|
507
|
+
if (data.rules) {
|
|
508
|
+
md += `## ${t.rulesSection}
|
|
509
|
+
|
|
510
|
+
- ${t.rulesPassed}: ${data.rules.passed}
|
|
511
|
+
- ${t.violations}: ${data.rules.violations.length}
|
|
512
|
+
|
|
513
|
+
`;
|
|
514
|
+
}
|
|
515
|
+
// Dead Code
|
|
516
|
+
if (data.deadCode) {
|
|
517
|
+
md += `## ${t.deadCodeSection}
|
|
518
|
+
|
|
519
|
+
| ${t.type} | Count |
|
|
520
|
+
|------|-------|
|
|
521
|
+
| ${t.unusedExports} | ${data.deadCode.summary.unusedExportsCount} |
|
|
522
|
+
| ${t.unusedVariables} | ${data.deadCode.summary.unusedVariablesCount} |
|
|
523
|
+
| ${t.unreachableCode} | ${data.deadCode.summary.unreachableCodeLines} |
|
|
524
|
+
|
|
525
|
+
`;
|
|
526
|
+
}
|
|
527
|
+
// Duplication
|
|
528
|
+
if (data.duplication) {
|
|
529
|
+
md += `## ${t.duplicationSection}
|
|
530
|
+
|
|
531
|
+
| Metric | Value |
|
|
532
|
+
|--------|-------|
|
|
533
|
+
| ${t.totalClones} | ${data.duplication.summary.totalClones} |
|
|
534
|
+
| ${t.duplicationPercentage} | ${data.duplication.summary.duplicationPercentage}% |
|
|
535
|
+
| ${t.estimatedRefactorTime} | ${data.duplication.summary.estimatedRefactorHours} ${t.hours} |
|
|
536
|
+
|
|
537
|
+
`;
|
|
538
|
+
}
|
|
539
|
+
return md;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Экспорт метрик в CSV
|
|
543
|
+
*/
|
|
544
|
+
exportCSV(data) {
|
|
545
|
+
const rows = [];
|
|
546
|
+
// Header
|
|
547
|
+
rows.push('File,LOC,SLOC,Comments,Complexity,Cognitive,Maintainability,Afferent,Efferent,Instability');
|
|
548
|
+
// Data
|
|
549
|
+
if (data.metrics?.files) {
|
|
550
|
+
for (const fileMetrics of data.metrics.files) {
|
|
551
|
+
rows.push([
|
|
552
|
+
`"${fileMetrics.filePath}"`,
|
|
553
|
+
fileMetrics.loc.total,
|
|
554
|
+
fileMetrics.loc.code,
|
|
555
|
+
fileMetrics.loc.comments,
|
|
556
|
+
fileMetrics.complexity.cyclomatic,
|
|
557
|
+
fileMetrics.complexity.cognitive,
|
|
558
|
+
fileMetrics.maintainability.toFixed(2),
|
|
559
|
+
fileMetrics.coupling.afferentCoupling,
|
|
560
|
+
fileMetrics.coupling.efferentCoupling,
|
|
561
|
+
fileMetrics.coupling.instability.toFixed(2)
|
|
562
|
+
].join(','));
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return rows.join('\n');
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Экспорт графа в GraphML
|
|
569
|
+
*/
|
|
570
|
+
exportGraphML(data) {
|
|
571
|
+
if (!data.graph) {
|
|
572
|
+
return '<?xml version="1.0" encoding="UTF-8"?><graphml/>';
|
|
573
|
+
}
|
|
574
|
+
let graphml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
575
|
+
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
|
|
576
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
577
|
+
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
|
|
578
|
+
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
|
|
579
|
+
|
|
580
|
+
<key id="d0" for="node" attr.name="label" attr.type="string"/>
|
|
581
|
+
<key id="d1" for="node" attr.name="type" attr.type="string"/>
|
|
582
|
+
<key id="d2" for="edge" attr.name="type" attr.type="string"/>
|
|
583
|
+
|
|
584
|
+
<graph id="G" edgedefault="directed">
|
|
585
|
+
`;
|
|
586
|
+
// Nodes
|
|
587
|
+
for (const [nodeId, node] of data.graph.nodes) {
|
|
588
|
+
const id = this.sanitizeId(nodeId);
|
|
589
|
+
const label = node.filePath.split(/[/\\]/).pop() || node.filePath;
|
|
590
|
+
graphml += ` <node id="${id}">
|
|
591
|
+
<data key="d0">${this.escapeXml(label)}</data>
|
|
592
|
+
<data key="d1">${node.type}</data>
|
|
593
|
+
</node>
|
|
594
|
+
`;
|
|
595
|
+
}
|
|
596
|
+
// Edges
|
|
597
|
+
let edgeId = 0;
|
|
598
|
+
for (const [source, edges] of data.graph.edges) {
|
|
599
|
+
for (const edge of edges) {
|
|
600
|
+
const sourceId = this.sanitizeId(source);
|
|
601
|
+
const targetId = this.sanitizeId(edge.to);
|
|
602
|
+
graphml += ` <edge id="e${edgeId++}" source="${sourceId}" target="${targetId}">
|
|
603
|
+
<data key="d2">${edge.type}</data>
|
|
604
|
+
</edge>
|
|
605
|
+
`;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
graphml += ` </graph>
|
|
609
|
+
</graphml>`;
|
|
610
|
+
return graphml;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Санитизация ID для GraphML
|
|
614
|
+
*/
|
|
615
|
+
sanitizeId(str) {
|
|
616
|
+
return str.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Экранирование XML
|
|
620
|
+
*/
|
|
621
|
+
escapeXml(str) {
|
|
622
|
+
return str
|
|
623
|
+
.replace(/&/g, '&')
|
|
624
|
+
.replace(/</g, '<')
|
|
625
|
+
.replace(/>/g, '>')
|
|
626
|
+
.replace(/"/g, '"')
|
|
627
|
+
.replace(/'/g, ''');
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Быстрые функции экспорта
|
|
632
|
+
*/
|
|
633
|
+
export async function exportToJSON(data) {
|
|
634
|
+
const manager = new ExportManager();
|
|
635
|
+
return manager.export(data, {
|
|
636
|
+
format: 'json',
|
|
637
|
+
includeGraph: true,
|
|
638
|
+
includeSymbols: true,
|
|
639
|
+
includeMetrics: true,
|
|
640
|
+
includeRules: true,
|
|
641
|
+
includeDeadCode: true,
|
|
642
|
+
includeDuplication: true,
|
|
643
|
+
includeSecurity: true
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
export async function exportToHTML(data, language = 'en') {
|
|
647
|
+
const manager = new ExportManager();
|
|
648
|
+
return manager.export(data, {
|
|
649
|
+
format: 'html',
|
|
650
|
+
includeGraph: true,
|
|
651
|
+
includeMetrics: true,
|
|
652
|
+
includeRules: true,
|
|
653
|
+
includeDeadCode: true,
|
|
654
|
+
includeDuplication: true,
|
|
655
|
+
includeSecurity: true,
|
|
656
|
+
language
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
export async function exportToMarkdown(data, language = 'en') {
|
|
660
|
+
const manager = new ExportManager();
|
|
661
|
+
return manager.export(data, {
|
|
662
|
+
format: 'markdown',
|
|
663
|
+
includeGraph: true,
|
|
664
|
+
includeMetrics: true,
|
|
665
|
+
includeRules: true,
|
|
666
|
+
includeDeadCode: true,
|
|
667
|
+
includeDuplication: true,
|
|
668
|
+
includeSecurity: true,
|
|
669
|
+
language
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
export async function importFromJSON(jsonString) {
|
|
673
|
+
const manager = new ExportManager();
|
|
674
|
+
return manager.import(jsonString);
|
|
675
|
+
}
|
|
676
|
+
//# sourceMappingURL=index.js.map
|