@aiready/visualizer 0.1.37 → 0.1.40
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/dist/cli.js +46 -17
- package/dist/cli.js.map +1 -1
- package/dist/graph/index.js +36 -13
- package/dist/graph/index.js.map +1 -1
- package/dist/index.js +36 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/web/dist/assets/{index-C9b__qGk.js → index-5leRjvmV.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/dist/report-data.json +224 -742
- package/web/index.html +1 -1
- package/web/package.json +1 -1
- package/web/public/report-data.json +224 -742
- package/web/src/App.tsx +61 -47
- package/web/src/components/ErrorDisplay.tsx +31 -17
- package/web/src/components/GraphCanvas.tsx +90 -24
- package/web/src/components/LegendPanel.tsx +242 -139
- package/web/src/components/LoadingSpinner.tsx +6 -3
- package/web/src/components/Navbar.tsx +66 -52
- package/web/src/components/NodeDetails.tsx +98 -68
- package/web/src/hooks/useTheme.ts +5 -2
- package/web/src/main.tsx +1 -1
- package/web/src/style.css +14 -2
- package/web/src/styles/index.css +7 -5
- package/web/src/utils.ts +86 -25
- package/web/tsconfig.json +1 -1
- package/web/vite.config.ts +10 -5
package/web/src/utils.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
FileNode,
|
|
3
|
+
GraphEdge,
|
|
4
|
+
GraphData,
|
|
5
|
+
ReportData,
|
|
6
|
+
BusinessMetrics,
|
|
7
|
+
} from './types';
|
|
2
8
|
import { severityColors, GRAPH_CONFIG } from './constants';
|
|
3
9
|
|
|
4
10
|
export function getSeverityColor(severity: string | undefined): string {
|
|
@@ -45,7 +51,10 @@ function extractBusinessMetrics(report: ReportData): BusinessMetrics {
|
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
export function transformReportToGraph(
|
|
54
|
+
export function transformReportToGraph(
|
|
55
|
+
report: ReportData,
|
|
56
|
+
runtimeGraphConfig?: { maxNodes?: number; maxEdges?: number }
|
|
57
|
+
): GraphData {
|
|
49
58
|
// Use runtime config if available (from aiready.json), else use defaults from constants
|
|
50
59
|
const graphConfig = {
|
|
51
60
|
maxNodes: runtimeGraphConfig?.maxNodes ?? GRAPH_CONFIG.maxNodes,
|
|
@@ -55,19 +64,34 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
55
64
|
const edges: GraphEdge[] = [];
|
|
56
65
|
const nodeMap = new Map<string, FileNode>();
|
|
57
66
|
|
|
58
|
-
const fileIssues = new Map<
|
|
67
|
+
const fileIssues = new Map<
|
|
68
|
+
string,
|
|
69
|
+
{ count: number; severities: Set<string>; maxSeverity: string }
|
|
70
|
+
>();
|
|
59
71
|
|
|
60
72
|
for (const pattern of report.patterns) {
|
|
61
73
|
const issueCount = pattern.issues?.length || 0;
|
|
62
74
|
if (issueCount > 0) {
|
|
63
75
|
let maxSeverity = 'info';
|
|
64
|
-
const severityPriority: Record<string, number> = {
|
|
76
|
+
const severityPriority: Record<string, number> = {
|
|
77
|
+
critical: 4,
|
|
78
|
+
major: 3,
|
|
79
|
+
minor: 2,
|
|
80
|
+
info: 1,
|
|
81
|
+
};
|
|
65
82
|
for (const issue of pattern.issues) {
|
|
66
|
-
if (
|
|
83
|
+
if (
|
|
84
|
+
(severityPriority[issue.severity] || 0) >
|
|
85
|
+
(severityPriority[maxSeverity] || 0)
|
|
86
|
+
) {
|
|
67
87
|
maxSeverity = issue.severity;
|
|
68
88
|
}
|
|
69
89
|
}
|
|
70
|
-
fileIssues.set(pattern.fileName, {
|
|
90
|
+
fileIssues.set(pattern.fileName, {
|
|
91
|
+
count: issueCount,
|
|
92
|
+
severities: new Set(),
|
|
93
|
+
maxSeverity,
|
|
94
|
+
});
|
|
71
95
|
}
|
|
72
96
|
}
|
|
73
97
|
|
|
@@ -124,7 +148,7 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
124
148
|
if (dep.startsWith('.') || dep.startsWith('/')) {
|
|
125
149
|
// Try multiple matching strategies
|
|
126
150
|
let targetFile: string | undefined;
|
|
127
|
-
|
|
151
|
+
|
|
128
152
|
// Strategy 1: Direct resolve from source file's directory
|
|
129
153
|
const normalizedDep = dep.replace(/^\.\/?/, '');
|
|
130
154
|
const possiblePaths = [
|
|
@@ -136,25 +160,32 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
136
160
|
// Just the path
|
|
137
161
|
`${sourceDir}/${normalizedDep}`,
|
|
138
162
|
];
|
|
139
|
-
|
|
163
|
+
|
|
140
164
|
for (const p of possiblePaths) {
|
|
141
165
|
if (nodeMap.has(p)) {
|
|
142
166
|
targetFile = p;
|
|
143
167
|
break;
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
|
-
|
|
170
|
+
|
|
147
171
|
// Strategy 2: Fall back to loose endsWith matching
|
|
148
172
|
if (!targetFile) {
|
|
149
173
|
const depBase = normalizedDep.split('/').pop() || normalizedDep;
|
|
150
|
-
targetFile = [...nodeMap.keys()].find(
|
|
151
|
-
|
|
152
|
-
|
|
174
|
+
targetFile = [...nodeMap.keys()].find(
|
|
175
|
+
(k) =>
|
|
176
|
+
k.endsWith(`/${depBase}.ts`) ||
|
|
177
|
+
k.endsWith(`/${depBase}.tsx`) ||
|
|
178
|
+
k.endsWith(`/${depBase}/index.ts`) ||
|
|
179
|
+
k.endsWith(`/${depBase}/index.tsx`)
|
|
153
180
|
);
|
|
154
181
|
}
|
|
155
|
-
|
|
182
|
+
|
|
156
183
|
if (targetFile && targetFile !== ctx.file) {
|
|
157
|
-
edges.push({
|
|
184
|
+
edges.push({
|
|
185
|
+
source: ctx.file,
|
|
186
|
+
target: targetFile,
|
|
187
|
+
type: 'dependency',
|
|
188
|
+
});
|
|
158
189
|
}
|
|
159
190
|
}
|
|
160
191
|
}
|
|
@@ -186,19 +217,28 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
186
217
|
|
|
187
218
|
// Fallback: loose basename matching
|
|
188
219
|
if (!nodeMap.has(relatedId)) {
|
|
189
|
-
const relBase = (related.split('/').pop() || related).replace(
|
|
190
|
-
|
|
191
|
-
|
|
220
|
+
const relBase = (related.split('/').pop() || related).replace(
|
|
221
|
+
/\.(ts|tsx|js|jsx)$/,
|
|
222
|
+
''
|
|
223
|
+
);
|
|
224
|
+
relatedId = [...nodeMap.keys()].find(
|
|
225
|
+
(k) =>
|
|
226
|
+
k.endsWith(`/${relBase}.ts`) ||
|
|
227
|
+
k.endsWith(`/${relBase}.tsx`) ||
|
|
228
|
+
k.endsWith(`/${relBase}/index.ts`) ||
|
|
229
|
+
k.endsWith(`/${relBase}/index.tsx`) ||
|
|
230
|
+
k.endsWith(`/${relBase}`)
|
|
192
231
|
);
|
|
193
232
|
}
|
|
194
233
|
|
|
195
234
|
if (relatedId && nodeMap.has(relatedId) && relatedId !== ctx.file) {
|
|
196
235
|
const exists = edges.some(
|
|
197
|
-
e =>
|
|
236
|
+
(e) =>
|
|
198
237
|
(e.source === ctx.file && e.target === relatedId) ||
|
|
199
238
|
(e.source === relatedId && e.target === ctx.file)
|
|
200
239
|
);
|
|
201
|
-
if (!exists)
|
|
240
|
+
if (!exists)
|
|
241
|
+
edges.push({ source: ctx.file, target: relatedId, type: 'related' });
|
|
202
242
|
}
|
|
203
243
|
}
|
|
204
244
|
}
|
|
@@ -206,11 +246,16 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
206
246
|
for (const dup of report.duplicates || []) {
|
|
207
247
|
if (nodeMap.has(dup.file1) && nodeMap.has(dup.file2)) {
|
|
208
248
|
const exists = edges.some(
|
|
209
|
-
e =>
|
|
249
|
+
(e) =>
|
|
210
250
|
(e.source === dup.file1 && e.target === dup.file2) ||
|
|
211
251
|
(e.source === dup.file2 && e.target === dup.file1)
|
|
212
252
|
);
|
|
213
|
-
if (!exists)
|
|
253
|
+
if (!exists)
|
|
254
|
+
edges.push({
|
|
255
|
+
source: dup.file1,
|
|
256
|
+
target: dup.file2,
|
|
257
|
+
type: 'similarity',
|
|
258
|
+
});
|
|
214
259
|
}
|
|
215
260
|
}
|
|
216
261
|
|
|
@@ -243,7 +288,11 @@ export function transformReportToGraph(report: ReportData, runtimeGraphConfig?:
|
|
|
243
288
|
}
|
|
244
289
|
|
|
245
290
|
export async function loadReportData(): Promise<ReportData | null> {
|
|
246
|
-
const possiblePaths = [
|
|
291
|
+
const possiblePaths = [
|
|
292
|
+
'/report-data.json',
|
|
293
|
+
'../report-data.json',
|
|
294
|
+
'../../report-data.json',
|
|
295
|
+
];
|
|
247
296
|
|
|
248
297
|
for (const path of possiblePaths) {
|
|
249
298
|
try {
|
|
@@ -260,15 +309,27 @@ export async function loadReportData(): Promise<ReportData | null> {
|
|
|
260
309
|
}
|
|
261
310
|
|
|
262
311
|
export function getEdgeDistance(type: string): number {
|
|
263
|
-
return
|
|
312
|
+
return (
|
|
313
|
+
GRAPH_CONFIG.edgeDistances[
|
|
314
|
+
type as keyof typeof GRAPH_CONFIG.edgeDistances
|
|
315
|
+
] ?? GRAPH_CONFIG.edgeDistances.dependency
|
|
316
|
+
);
|
|
264
317
|
}
|
|
265
318
|
|
|
266
319
|
export function getEdgeStrength(type: string): number {
|
|
267
|
-
return
|
|
320
|
+
return (
|
|
321
|
+
GRAPH_CONFIG.edgeStrengths[
|
|
322
|
+
type as keyof typeof GRAPH_CONFIG.edgeStrengths
|
|
323
|
+
] ?? GRAPH_CONFIG.edgeStrengths.dependency
|
|
324
|
+
);
|
|
268
325
|
}
|
|
269
326
|
|
|
270
327
|
export function getEdgeOpacity(type: string): number {
|
|
271
|
-
return
|
|
328
|
+
return (
|
|
329
|
+
GRAPH_CONFIG.edgeOpacities[
|
|
330
|
+
type as keyof typeof GRAPH_CONFIG.edgeOpacities
|
|
331
|
+
] ?? GRAPH_CONFIG.edgeOpacities.dependency
|
|
332
|
+
);
|
|
272
333
|
}
|
|
273
334
|
|
|
274
335
|
export function getEdgeStrokeWidth(type: string): number {
|
package/web/tsconfig.json
CHANGED
package/web/vite.config.ts
CHANGED
|
@@ -34,7 +34,10 @@ export default defineConfig(async ({ command }) => {
|
|
|
34
34
|
server.middlewares.use(async (req: any, res: any, next: any) => {
|
|
35
35
|
try {
|
|
36
36
|
const url = req.url || '';
|
|
37
|
-
if (
|
|
37
|
+
if (
|
|
38
|
+
url === '/report-data.json' ||
|
|
39
|
+
url.startsWith('/report-data.json?')
|
|
40
|
+
) {
|
|
38
41
|
const { promises: fsp } = await import('fs');
|
|
39
42
|
if (!existsSync(reportPath)) {
|
|
40
43
|
res.statusCode = 404;
|
|
@@ -42,9 +45,9 @@ export default defineConfig(async ({ command }) => {
|
|
|
42
45
|
res.end('Report not found');
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
|
-
|
|
48
|
+
const data = await fsp.readFile(reportPath, 'utf8');
|
|
46
49
|
const report = JSON.parse(data);
|
|
47
|
-
|
|
50
|
+
|
|
48
51
|
// Inject visualizer config from env if available
|
|
49
52
|
if (visualizerConfigStr) {
|
|
50
53
|
try {
|
|
@@ -54,7 +57,7 @@ export default defineConfig(async ({ command }) => {
|
|
|
54
57
|
// Silently ignore parse errors
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
res.statusCode = 200;
|
|
59
62
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
60
63
|
res.end(JSON.stringify(report));
|
|
@@ -90,7 +93,9 @@ export default defineConfig(async ({ command }) => {
|
|
|
90
93
|
resolve: {
|
|
91
94
|
alias: {
|
|
92
95
|
// during dev resolve to source for HMR; during build use the built dist
|
|
93
|
-
'@aiready/components': isDev
|
|
96
|
+
'@aiready/components': isDev
|
|
97
|
+
? componentsPath
|
|
98
|
+
: resolve(__dirname, '../../components/dist'),
|
|
94
99
|
},
|
|
95
100
|
},
|
|
96
101
|
};
|