projscan 0.12.0 → 0.16.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 +20 -14
- package/dist/analyzers/crossPackageImportCheck.d.ts +13 -0
- package/dist/analyzers/crossPackageImportCheck.js +136 -0
- package/dist/analyzers/crossPackageImportCheck.js.map +1 -0
- package/dist/analyzers/cycleCheck.d.ts +12 -0
- package/dist/analyzers/cycleCheck.js +65 -0
- package/dist/analyzers/cycleCheck.js.map +1 -0
- package/dist/cli/_shared.js +2 -2
- package/dist/cli/_shared.js.map +1 -1
- package/dist/cli/commands/audit.js +8 -2
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/coupling.js +4 -0
- package/dist/cli/commands/coupling.js.map +1 -1
- package/dist/cli/commands/dependencies.js +4 -3
- package/dist/cli/commands/dependencies.js.map +1 -1
- package/dist/cli/commands/doctor.js +4 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/explainIssue.d.ts +1 -0
- package/dist/cli/commands/explainIssue.js +49 -0
- package/dist/cli/commands/explainIssue.js.map +1 -0
- package/dist/cli/commands/fixSuggest.d.ts +1 -0
- package/dist/cli/commands/fixSuggest.js +71 -0
- package/dist/cli/commands/fixSuggest.js.map +1 -0
- package/dist/cli/commands/hotspots.js +4 -0
- package/dist/cli/commands/hotspots.js.map +1 -1
- package/dist/cli/commands/impact.d.ts +1 -0
- package/dist/cli/commands/impact.js +59 -0
- package/dist/cli/commands/impact.js.map +1 -0
- package/dist/cli/commands/review.d.ts +1 -0
- package/dist/cli/commands/review.js +70 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/search.js +6 -2
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/watch.d.ts +1 -0
- package/dist/cli/commands/watch.js +58 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/ast.d.ts +28 -0
- package/dist/core/ast.js +190 -0
- package/dist/core/ast.js.map +1 -1
- package/dist/core/auditRunner.d.ts +8 -0
- package/dist/core/auditRunner.js +50 -1
- package/dist/core/auditRunner.js.map +1 -1
- package/dist/core/codeGraph.d.ts +23 -1
- package/dist/core/codeGraph.js +198 -0
- package/dist/core/codeGraph.js.map +1 -1
- package/dist/core/dependencyAnalyzer.d.ts +15 -1
- package/dist/core/dependencyAnalyzer.js +115 -18
- package/dist/core/dependencyAnalyzer.js.map +1 -1
- package/dist/core/explainIssue.d.ts +9 -0
- package/dist/core/explainIssue.js +106 -0
- package/dist/core/explainIssue.js.map +1 -0
- package/dist/core/fileInspector.js +13 -0
- package/dist/core/fileInspector.js.map +1 -1
- package/dist/core/fixSuggest.d.ts +41 -0
- package/dist/core/fixSuggest.js +327 -0
- package/dist/core/fixSuggest.js.map +1 -0
- package/dist/core/impact.d.ts +27 -0
- package/dist/core/impact.js +169 -0
- package/dist/core/impact.js.map +1 -0
- package/dist/core/indexCache.js +5 -1
- package/dist/core/indexCache.js.map +1 -1
- package/dist/core/issueEngine.js +18 -0
- package/dist/core/issueEngine.js.map +1 -1
- package/dist/core/languages/goAdapter.js +5 -0
- package/dist/core/languages/goAdapter.js.map +1 -1
- package/dist/core/languages/goFunctions.d.ts +24 -0
- package/dist/core/languages/goFunctions.js +99 -0
- package/dist/core/languages/goFunctions.js.map +1 -0
- package/dist/core/languages/javaAdapter.js +5 -0
- package/dist/core/languages/javaAdapter.js.map +1 -1
- package/dist/core/languages/javaFunctions.d.ts +22 -0
- package/dist/core/languages/javaFunctions.js +87 -0
- package/dist/core/languages/javaFunctions.js.map +1 -0
- package/dist/core/languages/pythonAdapter.js +5 -0
- package/dist/core/languages/pythonAdapter.js.map +1 -1
- package/dist/core/languages/pythonFunctions.d.ts +23 -0
- package/dist/core/languages/pythonFunctions.js +87 -0
- package/dist/core/languages/pythonFunctions.js.map +1 -0
- package/dist/core/languages/rubyAdapter.js +5 -0
- package/dist/core/languages/rubyAdapter.js.map +1 -1
- package/dist/core/languages/rubyFunctions.d.ts +22 -0
- package/dist/core/languages/rubyFunctions.js +91 -0
- package/dist/core/languages/rubyFunctions.js.map +1 -0
- package/dist/core/review.d.ts +21 -0
- package/dist/core/review.js +457 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/searchIndex.d.ts +9 -0
- package/dist/core/searchIndex.js.map +1 -1
- package/dist/core/semanticSearch.d.ts +69 -2
- package/dist/core/semanticSearch.js +113 -55
- package/dist/core/semanticSearch.js.map +1 -1
- package/dist/core/watcher.d.ts +37 -0
- package/dist/core/watcher.js +126 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/audit.js +7 -2
- package/dist/mcp/tools/audit.js.map +1 -1
- package/dist/mcp/tools/dependencies.js +11 -5
- package/dist/mcp/tools/dependencies.js.map +1 -1
- package/dist/mcp/tools/explainIssue.d.ts +2 -0
- package/dist/mcp/tools/explainIssue.js +30 -0
- package/dist/mcp/tools/explainIssue.js.map +1 -0
- package/dist/mcp/tools/file.js +1 -1
- package/dist/mcp/tools/file.js.map +1 -1
- package/dist/mcp/tools/fixSuggest.d.ts +2 -0
- package/dist/mcp/tools/fixSuggest.js +57 -0
- package/dist/mcp/tools/fixSuggest.js.map +1 -0
- package/dist/mcp/tools/hotspots.js +40 -1
- package/dist/mcp/tools/hotspots.js.map +1 -1
- package/dist/mcp/tools/impact.d.ts +2 -0
- package/dist/mcp/tools/impact.js +59 -0
- package/dist/mcp/tools/impact.js.map +1 -0
- package/dist/mcp/tools/review.d.ts +2 -0
- package/dist/mcp/tools/review.js +54 -0
- package/dist/mcp/tools/review.js.map +1 -0
- package/dist/mcp/tools/search.js +9 -2
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools.js +8 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/reporters/consoleReporter.d.ts +10 -1
- package/dist/reporters/consoleReporter.js +218 -0
- package/dist/reporters/consoleReporter.js.map +1 -1
- package/dist/reporters/htmlReporter.d.ts +8 -0
- package/dist/reporters/htmlReporter.js +287 -0
- package/dist/reporters/htmlReporter.js.map +1 -0
- package/dist/reporters/jsonReporter.d.ts +10 -1
- package/dist/reporters/jsonReporter.js +12 -0
- package/dist/reporters/jsonReporter.js.map +1 -1
- package/dist/reporters/markdownReporter.d.ts +10 -1
- package/dist/reporters/markdownReporter.js +173 -0
- package/dist/reporters/markdownReporter.js.map +1 -1
- package/dist/tool-manifest.json +133 -8
- package/dist/types.d.ts +276 -1
- package/dist/utils/config.js +27 -0
- package/dist/utils/config.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { calculateScore, badgeMarkdown } from '../utils/scoreCalculator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone HTML report (0.16.0+). Each top-level command has its own
|
|
4
|
+
* renderer; the output is a single self-contained HTML document with
|
|
5
|
+
* inline CSS, no external assets, no JS framework. Suitable for posting
|
|
6
|
+
* as a CI artifact or sharing in a PR comment.
|
|
7
|
+
*
|
|
8
|
+
* Style choices:
|
|
9
|
+
* - System font stack to render readably without webfonts.
|
|
10
|
+
* - Two-tone colour palette (neutral grey + one accent per severity).
|
|
11
|
+
* - Tables for data, no charts. Charts would need JS.
|
|
12
|
+
* - Accessibility: tables get `<th>`s with scope, severity has both
|
|
13
|
+
* colour AND a text glyph so colourblind readers don't lose info.
|
|
14
|
+
*/
|
|
15
|
+
const STYLE = `
|
|
16
|
+
:root {
|
|
17
|
+
color-scheme: light dark;
|
|
18
|
+
}
|
|
19
|
+
* { box-sizing: border-box; }
|
|
20
|
+
body {
|
|
21
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 2rem;
|
|
24
|
+
max-width: 1100px;
|
|
25
|
+
margin-inline: auto;
|
|
26
|
+
line-height: 1.5;
|
|
27
|
+
color: #222;
|
|
28
|
+
background: #fafafa;
|
|
29
|
+
}
|
|
30
|
+
@media (prefers-color-scheme: dark) {
|
|
31
|
+
body { color: #ddd; background: #181818; }
|
|
32
|
+
.card { background: #222; border-color: #333; }
|
|
33
|
+
table { background: #222; }
|
|
34
|
+
th { background: #2a2a2a; }
|
|
35
|
+
tr:nth-child(even) td { background: #1f1f1f; }
|
|
36
|
+
code { background: #2a2a2a; }
|
|
37
|
+
a { color: #6cb6ff; }
|
|
38
|
+
}
|
|
39
|
+
h1 { margin-top: 0; font-size: 1.6rem; }
|
|
40
|
+
h2 { margin-top: 2rem; font-size: 1.2rem; border-bottom: 1px solid #ccc; padding-bottom: 0.3rem; }
|
|
41
|
+
.card {
|
|
42
|
+
background: #fff;
|
|
43
|
+
border: 1px solid #ddd;
|
|
44
|
+
border-radius: 8px;
|
|
45
|
+
padding: 1rem 1.2rem;
|
|
46
|
+
margin-bottom: 1rem;
|
|
47
|
+
}
|
|
48
|
+
.score { font-size: 2.5rem; font-weight: 700; }
|
|
49
|
+
.grade-A, .grade-A\\+ { color: #1a8a4a; }
|
|
50
|
+
.grade-B { color: #4a8a1a; }
|
|
51
|
+
.grade-C { color: #c79100; }
|
|
52
|
+
.grade-D { color: #d6582d; }
|
|
53
|
+
.grade-F { color: #d63333; }
|
|
54
|
+
table { width: 100%; border-collapse: collapse; margin: 0.5rem 0; background: #fff; }
|
|
55
|
+
th, td { padding: 0.4rem 0.6rem; text-align: left; border-bottom: 1px solid #eee; }
|
|
56
|
+
th { background: #f3f3f3; font-weight: 600; }
|
|
57
|
+
tr:nth-child(even) td { background: #f9f9f9; }
|
|
58
|
+
code { background: #f0f0f0; padding: 0.05em 0.4em; border-radius: 3px; font-size: 0.95em; }
|
|
59
|
+
.severity-error { color: #d63333; }
|
|
60
|
+
.severity-warning { color: #c79100; }
|
|
61
|
+
.severity-info { color: #4a6a8a; }
|
|
62
|
+
.muted { color: #888; font-size: 0.9rem; }
|
|
63
|
+
.verdict-ok { color: #1a8a4a; font-weight: 700; }
|
|
64
|
+
.verdict-review { color: #c79100; font-weight: 700; }
|
|
65
|
+
.verdict-block { color: #d63333; font-weight: 700; }
|
|
66
|
+
.right { text-align: right; }
|
|
67
|
+
.tag { display: inline-block; padding: 0.05em 0.5em; border-radius: 3px; background: #e8e8e8; font-size: 0.85em; }
|
|
68
|
+
.tag-new { background: #ffd6d6; color: #8a0000; }
|
|
69
|
+
.tag-expanded { background: #fff3c4; color: #6a4a00; }
|
|
70
|
+
`.trim();
|
|
71
|
+
/** Wrap inner HTML in a complete document. Title is required for accessibility. */
|
|
72
|
+
export function htmlShell(title, bodyHtml) {
|
|
73
|
+
return `<!DOCTYPE html>
|
|
74
|
+
<html lang="en">
|
|
75
|
+
<head>
|
|
76
|
+
<meta charset="UTF-8">
|
|
77
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
78
|
+
<title>${escapeHtml(title)}</title>
|
|
79
|
+
<style>${STYLE}</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
${bodyHtml}
|
|
83
|
+
<footer class="muted" style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ccc;">
|
|
84
|
+
Generated by <a href="https://github.com/abhiyoheswaran1/projscan">projscan</a> at ${new Date().toISOString()}
|
|
85
|
+
</footer>
|
|
86
|
+
</body>
|
|
87
|
+
</html>`;
|
|
88
|
+
}
|
|
89
|
+
export function reportHealthHtml(issues) {
|
|
90
|
+
const { score, grade, errors, warnings, infos } = calculateScore(issues);
|
|
91
|
+
const sevTotals = `<span class="severity-error">${errors}</span> errors · <span class="severity-warning">${warnings}</span> warnings · <span class="severity-info">${infos}</span> info`;
|
|
92
|
+
const issuesHtml = issues.length === 0
|
|
93
|
+
? '<p>No issues detected. Project looks healthy.</p>'
|
|
94
|
+
: `<table>
|
|
95
|
+
<thead><tr><th>Severity</th><th>Title</th><th>Description</th><th>Suggested action</th></tr></thead>
|
|
96
|
+
<tbody>${issues
|
|
97
|
+
.map((i) => `<tr>
|
|
98
|
+
<td class="severity-${i.severity}">${severityIcon(i.severity)} ${i.severity}</td>
|
|
99
|
+
<td><code>${escapeHtml(i.id)}</code><br>${escapeHtml(i.title)}</td>
|
|
100
|
+
<td class="muted">${escapeHtml(i.description)}</td>
|
|
101
|
+
<td>${i.suggestedAction ? `${escapeHtml(i.suggestedAction.summary)}<br><code>projscan fix-suggest ${escapeHtml(i.id)}</code>` : '<span class="muted">—</span>'}</td>
|
|
102
|
+
</tr>`)
|
|
103
|
+
.join('\n')}</tbody>
|
|
104
|
+
</table>`;
|
|
105
|
+
const body = `
|
|
106
|
+
<h1>Project health</h1>
|
|
107
|
+
<div class="card">
|
|
108
|
+
<div class="score grade-${grade.replace('+', '\\+')}">${grade}</div>
|
|
109
|
+
<div>${score} / 100 — ${sevTotals}</div>
|
|
110
|
+
<p class="muted" style="margin-bottom: 0;">${badgeMarkdown(grade)}</p>
|
|
111
|
+
</div>
|
|
112
|
+
<h2>Issues (${issues.length})</h2>
|
|
113
|
+
${issuesHtml}
|
|
114
|
+
`;
|
|
115
|
+
console.log(htmlShell(`Health · ${grade} (${score}/100)`, body));
|
|
116
|
+
}
|
|
117
|
+
export function reportHotspotsHtml(report) {
|
|
118
|
+
if (!report.available) {
|
|
119
|
+
console.log(htmlShell('Hotspots — unavailable', `<h1>Hotspots</h1><p class="muted">${escapeHtml(report.reason ?? 'Hotspots unavailable.')}</p>`));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const rows = report.hotspots
|
|
123
|
+
.map((h, i) => `<tr>
|
|
124
|
+
<td class="right">${i + 1}</td>
|
|
125
|
+
<td class="right">${h.riskScore.toFixed(1)}</td>
|
|
126
|
+
<td><code>${escapeHtml(h.relativePath)}</code></td>
|
|
127
|
+
<td class="right">${h.churn}</td>
|
|
128
|
+
<td class="right">${typeof h.cyclomaticComplexity === 'number' ? h.cyclomaticComplexity : '-'}</td>
|
|
129
|
+
<td class="right">${h.lineCount}</td>
|
|
130
|
+
<td class="right">${h.issueCount}</td>
|
|
131
|
+
<td class="muted">${escapeHtml(h.reasons.join(', '))}</td>
|
|
132
|
+
</tr>`)
|
|
133
|
+
.join('\n');
|
|
134
|
+
const body = `
|
|
135
|
+
<h1>Hotspots</h1>
|
|
136
|
+
<p class="muted">Window: <strong>${escapeHtml(String(report.window.since ?? 'all'))}</strong> · ${report.window.commitsScanned} commit(s) scanned · ${report.totalFilesRanked} file(s) ranked</p>
|
|
137
|
+
<table>
|
|
138
|
+
<thead><tr><th>#</th><th class="right">Score</th><th>File</th><th class="right">Churn</th><th class="right">CC</th><th class="right">Lines</th><th class="right">Issues</th><th>Reasons</th></tr></thead>
|
|
139
|
+
<tbody>${rows}</tbody>
|
|
140
|
+
</table>
|
|
141
|
+
`;
|
|
142
|
+
console.log(htmlShell('Hotspots', body));
|
|
143
|
+
}
|
|
144
|
+
export function reportCouplingHtml(report) {
|
|
145
|
+
const cyclesHtml = report.cycles.length === 0
|
|
146
|
+
? '<p class="muted">No circular imports detected.</p>'
|
|
147
|
+
: `<table>
|
|
148
|
+
<thead><tr><th class="right">Size</th><th>Files</th></tr></thead>
|
|
149
|
+
<tbody>${report.cycles
|
|
150
|
+
.map((c) => `<tr><td class="right">${c.size}</td><td>${c.files.map((f) => `<code>${escapeHtml(f)}</code>`).join(' → ')}</td></tr>`)
|
|
151
|
+
.join('\n')}</tbody>
|
|
152
|
+
</table>`;
|
|
153
|
+
const filesRows = report.files
|
|
154
|
+
.slice(0, 100)
|
|
155
|
+
.map((f) => `<tr><td><code>${escapeHtml(f.relativePath)}</code></td><td class="right">${f.fanIn}</td><td class="right">${f.fanOut}</td><td class="right">${f.instability.toFixed(3)}</td></tr>`)
|
|
156
|
+
.join('\n');
|
|
157
|
+
const body = `
|
|
158
|
+
<h1>Coupling</h1>
|
|
159
|
+
<h2>Cycles (${report.cycles.length})</h2>
|
|
160
|
+
${cyclesHtml}
|
|
161
|
+
<h2>Files (top 100 by fan-in)</h2>
|
|
162
|
+
<table>
|
|
163
|
+
<thead><tr><th>File</th><th class="right">Fan-in</th><th class="right">Fan-out</th><th class="right">Instability</th></tr></thead>
|
|
164
|
+
<tbody>${filesRows}</tbody>
|
|
165
|
+
</table>
|
|
166
|
+
`;
|
|
167
|
+
console.log(htmlShell('Coupling', body));
|
|
168
|
+
}
|
|
169
|
+
export function reportReviewHtml(report) {
|
|
170
|
+
if (!report.available) {
|
|
171
|
+
console.log(htmlShell('PR Review — unavailable', `<h1>PR Review</h1><p class="muted">${escapeHtml(report.reason ?? 'Review unavailable.')}</p>`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const verdictClass = `verdict-${report.verdict}`;
|
|
175
|
+
const verdictLabel = report.verdict === 'block' ? '🚫 BLOCK' : report.verdict === 'review' ? '👀 REVIEW' : '✅ OK';
|
|
176
|
+
const filesHtml = report.changedFiles.length === 0
|
|
177
|
+
? '<p class="muted">No structural changes.</p>'
|
|
178
|
+
: `<table>
|
|
179
|
+
<thead><tr><th>File</th><th>Status</th><th class="right">Risk</th><th class="right">CC</th><th class="right">ΔCC</th></tr></thead>
|
|
180
|
+
<tbody>${report.changedFiles
|
|
181
|
+
.slice(0, 100)
|
|
182
|
+
.map((f) => `<tr>
|
|
183
|
+
<td><code>${escapeHtml(f.relativePath)}</code></td>
|
|
184
|
+
<td class="muted">${f.status}</td>
|
|
185
|
+
<td class="right">${f.riskScore !== null ? f.riskScore.toFixed(1) : '-'}</td>
|
|
186
|
+
<td class="right">${f.cyclomaticComplexity ?? '-'}</td>
|
|
187
|
+
<td class="right">${f.cyclomaticDelta === null ? '-' : signed(f.cyclomaticDelta)}</td>
|
|
188
|
+
</tr>`)
|
|
189
|
+
.join('\n')}</tbody>
|
|
190
|
+
</table>`;
|
|
191
|
+
const cyclesHtml = report.newCycles.length === 0
|
|
192
|
+
? ''
|
|
193
|
+
: `<h2>New / expanded cycles</h2>
|
|
194
|
+
<ul>${report.newCycles
|
|
195
|
+
.map((c) => `<li><span class="tag tag-${c.classification}">${c.classification}</span> (${c.size} files): ${c.files.map((f) => `<code>${escapeHtml(f)}</code>`).join(' → ')}</li>`)
|
|
196
|
+
.join('\n')}</ul>`;
|
|
197
|
+
const fnsHtml = report.riskyFunctions.length === 0
|
|
198
|
+
? ''
|
|
199
|
+
: `<h2>Risky functions</h2>
|
|
200
|
+
<table>
|
|
201
|
+
<thead><tr><th>Function</th><th>File</th><th class="right">CC</th><th>Reason</th><th>Δ</th></tr></thead>
|
|
202
|
+
<tbody>${report.riskyFunctions
|
|
203
|
+
.slice(0, 50)
|
|
204
|
+
.map((fn) => `<tr>
|
|
205
|
+
<td><code>${escapeHtml(fn.name)}</code></td>
|
|
206
|
+
<td><code>${escapeHtml(fn.file)}</code>:L${fn.line}</td>
|
|
207
|
+
<td class="right">${fn.cyclomaticComplexity}</td>
|
|
208
|
+
<td class="muted">${fn.reason}</td>
|
|
209
|
+
<td>${fn.baseCc === null ? 'new' : `${fn.baseCc} → ${fn.cyclomaticComplexity}`}</td>
|
|
210
|
+
</tr>`)
|
|
211
|
+
.join('\n')}</tbody>
|
|
212
|
+
</table>`;
|
|
213
|
+
const depsHtml = report.dependencyChanges.length === 0
|
|
214
|
+
? ''
|
|
215
|
+
: `<h2>Dependency changes</h2>${report.dependencyChanges
|
|
216
|
+
.map((d) => `
|
|
217
|
+
<h3>${escapeHtml(d.manifestFile)}${d.workspace ? ` <span class="muted">(${escapeHtml(d.workspace)})</span>` : ''}</h3>
|
|
218
|
+
<ul>
|
|
219
|
+
${d.added.map((a) => `<li>➕ <code>${escapeHtml(a.name)}@${escapeHtml(a.version)}</code> <span class="muted">(${a.kind})</span></li>`).join('\n')}
|
|
220
|
+
${d.removed.map((r) => `<li>➖ <code>${escapeHtml(r.name)}@${escapeHtml(r.version)}</code> <span class="muted">(${r.kind})</span></li>`).join('\n')}
|
|
221
|
+
${d.bumped.map((b) => `<li>🔄 <code>${escapeHtml(b.name)}</code>: ${escapeHtml(b.from)} → ${escapeHtml(b.to)} <span class="muted">(${b.kind})</span></li>`).join('\n')}
|
|
222
|
+
</ul>`)
|
|
223
|
+
.join('\n')}`;
|
|
224
|
+
const body = `
|
|
225
|
+
<h1>PR Review</h1>
|
|
226
|
+
<div class="card">
|
|
227
|
+
<p class="muted">base <code>${escapeHtml(report.base.ref)}</code> (${report.base.resolvedSha?.slice(0, 7) ?? '?'}) → head <code>${escapeHtml(report.head.ref)}</code> (${report.head.resolvedSha?.slice(0, 7) ?? '?'})</p>
|
|
228
|
+
<p class="${verdictClass}" style="font-size: 1.4rem; margin: 0.5rem 0;">${verdictLabel}</p>
|
|
229
|
+
<ul>${report.summary.map((s) => `<li>${escapeHtml(s)}</li>`).join('\n')}</ul>
|
|
230
|
+
</div>
|
|
231
|
+
<h2>Changed files</h2>
|
|
232
|
+
${filesHtml}
|
|
233
|
+
${cyclesHtml}
|
|
234
|
+
${fnsHtml}
|
|
235
|
+
${depsHtml}
|
|
236
|
+
`;
|
|
237
|
+
console.log(htmlShell(`PR Review (${verdictLabel.replace(/[^A-Za-z]/g, '').trim()})`, body));
|
|
238
|
+
}
|
|
239
|
+
export function reportImpactHtml(report) {
|
|
240
|
+
if (!report.available) {
|
|
241
|
+
console.log(htmlShell('Impact — unavailable', `<h1>Impact</h1><p class="muted">${escapeHtml(report.reason ?? 'Impact unavailable.')}</p>`));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const defs = report.definitionFiles.length === 0
|
|
245
|
+
? ''
|
|
246
|
+
: `<h2>Defined in</h2><ul>${report.definitionFiles.map((f) => `<li><code>${escapeHtml(f)}</code></li>`).join('\n')}</ul>`;
|
|
247
|
+
const reachable = report.reachable.length === 0
|
|
248
|
+
? '<p class="muted">No reachable files.</p>'
|
|
249
|
+
: `<table>
|
|
250
|
+
<thead><tr><th class="right">Distance</th><th>File</th></tr></thead>
|
|
251
|
+
<tbody>${report.reachable
|
|
252
|
+
.slice(0, 200)
|
|
253
|
+
.map((n) => `<tr><td class="right">${n.distance}</td><td><code>${escapeHtml(n.file)}</code></td></tr>`)
|
|
254
|
+
.join('\n')}</tbody>
|
|
255
|
+
</table>`;
|
|
256
|
+
const body = `
|
|
257
|
+
<h1>Impact: ${escapeHtml(report.target.kind)} <code>${escapeHtml(report.target.value)}</code></h1>
|
|
258
|
+
<p class="muted">${report.totalReachable} file(s) reachable within distance ${report.maxDistance}${report.truncated ? ' (truncated; more files exist beyond)' : ''}.</p>
|
|
259
|
+
${defs}
|
|
260
|
+
<h2>Reachable</h2>
|
|
261
|
+
${reachable}
|
|
262
|
+
`;
|
|
263
|
+
console.log(htmlShell(`Impact: ${report.target.value}`, body));
|
|
264
|
+
}
|
|
265
|
+
// ── helpers ───────────────────────────────────────────────
|
|
266
|
+
function escapeHtml(s) {
|
|
267
|
+
return s
|
|
268
|
+
.replace(/&/g, '&')
|
|
269
|
+
.replace(/</g, '<')
|
|
270
|
+
.replace(/>/g, '>')
|
|
271
|
+
.replace(/"/g, '"')
|
|
272
|
+
.replace(/'/g, ''');
|
|
273
|
+
}
|
|
274
|
+
function severityIcon(s) {
|
|
275
|
+
switch (s) {
|
|
276
|
+
case 'error':
|
|
277
|
+
return '✕';
|
|
278
|
+
case 'warning':
|
|
279
|
+
return '!';
|
|
280
|
+
default:
|
|
281
|
+
return 'i';
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function signed(n) {
|
|
285
|
+
return n >= 0 ? `+${n}` : String(n);
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=htmlReporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"htmlReporter.js","sourceRoot":"","sources":["../../src/reporters/htmlReporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, UpgradePreview, WorkspaceInfo } from '../types.js';
|
|
1
|
+
import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, ReviewReport, FixSuggestion, ImpactReport, IssueExplanation, UpgradePreview, WorkspaceInfo } from '../types.js';
|
|
2
2
|
export declare function reportAnalysisJson(report: AnalysisReport): void;
|
|
3
3
|
export declare function reportHealthJson(issues: Issue[]): void;
|
|
4
4
|
export declare function reportCiJson(issues: Issue[], threshold: number): void;
|
|
@@ -15,4 +15,13 @@ export declare function reportUpgradeJson(preview: UpgradePreview): void;
|
|
|
15
15
|
export declare function reportCoverageJson(report: CoverageJoinedReport): void;
|
|
16
16
|
export declare function reportCouplingJson(report: CouplingReport): void;
|
|
17
17
|
export declare function reportPrDiffJson(report: PrDiffReport): void;
|
|
18
|
+
export declare function reportReviewJson(report: ReviewReport): void;
|
|
19
|
+
export declare function reportFixSuggestJson(result: {
|
|
20
|
+
matched: boolean;
|
|
21
|
+
fix?: FixSuggestion;
|
|
22
|
+
reason?: string;
|
|
23
|
+
synthetic?: boolean;
|
|
24
|
+
}): void;
|
|
25
|
+
export declare function reportExplainIssueJson(explanation: IssueExplanation): void;
|
|
26
|
+
export declare function reportImpactJson(report: ImpactReport): void;
|
|
18
27
|
export declare function reportWorkspacesJson(info: WorkspaceInfo): void;
|
|
@@ -71,6 +71,18 @@ export function reportCouplingJson(report) {
|
|
|
71
71
|
export function reportPrDiffJson(report) {
|
|
72
72
|
console.log(JSON.stringify({ prDiff: report }, null, 2));
|
|
73
73
|
}
|
|
74
|
+
export function reportReviewJson(report) {
|
|
75
|
+
console.log(JSON.stringify({ review: report }, null, 2));
|
|
76
|
+
}
|
|
77
|
+
export function reportFixSuggestJson(result) {
|
|
78
|
+
console.log(JSON.stringify({ fixSuggest: result }, null, 2));
|
|
79
|
+
}
|
|
80
|
+
export function reportExplainIssueJson(explanation) {
|
|
81
|
+
console.log(JSON.stringify({ issueExplanation: explanation }, null, 2));
|
|
82
|
+
}
|
|
83
|
+
export function reportImpactJson(report) {
|
|
84
|
+
console.log(JSON.stringify({ impact: report }, null, 2));
|
|
85
|
+
}
|
|
74
86
|
export function reportWorkspacesJson(info) {
|
|
75
87
|
console.log(JSON.stringify({ workspaces: info }, null, 2));
|
|
76
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonReporter.js","sourceRoot":"","sources":["../../src/reporters/jsonReporter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jsonReporter.js","sourceRoot":"","sources":["../../src/reporters/jsonReporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, UpgradePreview, WorkspaceInfo } from '../types.js';
|
|
1
|
+
import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, ReviewReport, FixSuggestion, ImpactReport, IssueExplanation, UpgradePreview, WorkspaceInfo } from '../types.js';
|
|
2
2
|
export declare function reportAnalysisMarkdown(report: AnalysisReport): void;
|
|
3
3
|
export declare function reportHealthMarkdown(issues: Issue[]): void;
|
|
4
4
|
export declare function reportCiMarkdown(issues: Issue[], threshold: number): void;
|
|
@@ -11,6 +11,15 @@ export declare function reportFileMarkdown(insp: FileInspection): void;
|
|
|
11
11
|
export declare function reportHotspotsMarkdown(report: HotspotReport): void;
|
|
12
12
|
export declare function reportCouplingMarkdown(report: CouplingReport): void;
|
|
13
13
|
export declare function reportPrDiffMarkdown(report: PrDiffReport): void;
|
|
14
|
+
export declare function reportImpactMarkdown(report: ImpactReport): void;
|
|
15
|
+
export declare function reportFixSuggestMarkdown(result: {
|
|
16
|
+
matched: boolean;
|
|
17
|
+
fix?: FixSuggestion;
|
|
18
|
+
reason?: string;
|
|
19
|
+
synthetic?: boolean;
|
|
20
|
+
}): void;
|
|
21
|
+
export declare function reportExplainIssueMarkdown(e: IssueExplanation): void;
|
|
22
|
+
export declare function reportReviewMarkdown(report: ReviewReport): void;
|
|
14
23
|
export declare function reportWorkspacesMarkdown(info: WorkspaceInfo): void;
|
|
15
24
|
export declare function reportOutdatedMarkdown(report: OutdatedReport): void;
|
|
16
25
|
export declare function reportAuditMarkdown(report: AuditReport): void;
|
|
@@ -56,6 +56,9 @@ export function reportHealthMarkdown(issues) {
|
|
|
56
56
|
for (const issue of issues) {
|
|
57
57
|
const icon = issue.severity === 'error' ? '❌' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
58
58
|
lines.push(`- ${icon} **${issue.title}** - ${issue.description}`);
|
|
59
|
+
if (issue.suggestedAction) {
|
|
60
|
+
lines.push(` - **Action:** ${issue.suggestedAction.summary} _(\`projscan fix-suggest ${issue.id}\`)_`);
|
|
61
|
+
}
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
console.log(lines.join('\n'));
|
|
@@ -258,6 +261,18 @@ export function reportFileMarkdown(insp) {
|
|
|
258
261
|
lines.push(`- \`${exp.name}\` (${exp.type})`);
|
|
259
262
|
}
|
|
260
263
|
}
|
|
264
|
+
if (insp.functions && insp.functions.length > 0) {
|
|
265
|
+
lines.push('', '## Functions (top by CC)', '');
|
|
266
|
+
lines.push('| CC | Fan-in | Name | Lines |');
|
|
267
|
+
lines.push('| ---: | ---: | --- | --- |');
|
|
268
|
+
for (const fn of insp.functions.slice(0, 20)) {
|
|
269
|
+
const fi = typeof fn.fanIn === 'number' ? String(fn.fanIn) : '-';
|
|
270
|
+
lines.push(`| ${fn.cyclomaticComplexity} | ${fi} | \`${fn.name}\` | L${fn.line}-${fn.endLine} |`);
|
|
271
|
+
}
|
|
272
|
+
if (insp.functions.length > 20) {
|
|
273
|
+
lines.push('', `_... and ${insp.functions.length - 20} more_`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
261
276
|
console.log(lines.join('\n'));
|
|
262
277
|
}
|
|
263
278
|
export function reportHotspotsMarkdown(report) {
|
|
@@ -363,6 +378,164 @@ export function reportPrDiffMarkdown(report) {
|
|
|
363
378
|
function signed(n) {
|
|
364
379
|
return n >= 0 ? `+${n}` : String(n);
|
|
365
380
|
}
|
|
381
|
+
export function reportImpactMarkdown(report) {
|
|
382
|
+
const lines = [`# Impact: ${report.target.kind} \`${report.target.value}\``, ''];
|
|
383
|
+
if (!report.available) {
|
|
384
|
+
lines.push(`> ${report.reason ?? 'Impact unavailable.'}`);
|
|
385
|
+
console.log(lines.join('\n'));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (report.target.kind === 'symbol') {
|
|
389
|
+
lines.push(`_definitions: ${report.definitionFiles.length} · direct callers: ${report.directCallers.length}_`, '');
|
|
390
|
+
if (report.definitionFiles.length > 0) {
|
|
391
|
+
lines.push('## Defined in', '');
|
|
392
|
+
for (const f of report.definitionFiles)
|
|
393
|
+
lines.push(`- \`${f}\``);
|
|
394
|
+
lines.push('');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
lines.push(`**${report.totalReachable}** file(s) reachable within distance ${report.maxDistance}${report.truncated ? ' (truncated; more files exist beyond)' : ''}.`, '');
|
|
398
|
+
if (report.reachable.length === 0) {
|
|
399
|
+
lines.push('_No reachable files._');
|
|
400
|
+
console.log(lines.join('\n'));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
lines.push('| Distance | File |', '| ---: | --- |');
|
|
404
|
+
for (const n of report.reachable.slice(0, 200)) {
|
|
405
|
+
lines.push(`| ${n.distance} | \`${n.file}\` |`);
|
|
406
|
+
}
|
|
407
|
+
if (report.reachable.length > 200) {
|
|
408
|
+
lines.push('', `_... and ${report.reachable.length - 200} more_`);
|
|
409
|
+
}
|
|
410
|
+
console.log(lines.join('\n'));
|
|
411
|
+
}
|
|
412
|
+
export function reportFixSuggestMarkdown(result) {
|
|
413
|
+
const lines = ['# Fix Suggestion', ''];
|
|
414
|
+
if (!result.matched || !result.fix) {
|
|
415
|
+
lines.push(`> ${result.reason ?? 'No suggestion available.'}`);
|
|
416
|
+
console.log(lines.join('\n'));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const fix = result.fix;
|
|
420
|
+
lines.push(`**${fix.headline}**`, '');
|
|
421
|
+
lines.push(`_severity: ${fix.severity} · category: ${fix.category} · issue: \`${fix.issueId}\`${result.synthetic ? ' (synthetic)' : ''}_`, '');
|
|
422
|
+
lines.push('## Why', '', fix.why, '');
|
|
423
|
+
if (fix.where.length > 0) {
|
|
424
|
+
lines.push('## Where', '');
|
|
425
|
+
for (const w of fix.where) {
|
|
426
|
+
const loc = w.line ? `${w.file}:${w.line}` : w.file;
|
|
427
|
+
lines.push(`- \`${loc}\``);
|
|
428
|
+
}
|
|
429
|
+
lines.push('');
|
|
430
|
+
}
|
|
431
|
+
lines.push('## Action', '', fix.instruction, '');
|
|
432
|
+
if (fix.suggestedTest)
|
|
433
|
+
lines.push('## Verify', '', fix.suggestedTest, '');
|
|
434
|
+
if (fix.relatedFiles && fix.relatedFiles.length > 0) {
|
|
435
|
+
lines.push('## Related files', '');
|
|
436
|
+
for (const f of fix.relatedFiles)
|
|
437
|
+
lines.push(`- \`${f}\``);
|
|
438
|
+
lines.push('');
|
|
439
|
+
}
|
|
440
|
+
if (fix.references && fix.references.length > 0) {
|
|
441
|
+
lines.push('## References', '');
|
|
442
|
+
for (const r of fix.references)
|
|
443
|
+
lines.push(`- ${r}`);
|
|
444
|
+
lines.push('');
|
|
445
|
+
}
|
|
446
|
+
console.log(lines.join('\n'));
|
|
447
|
+
}
|
|
448
|
+
export function reportExplainIssueMarkdown(e) {
|
|
449
|
+
const lines = [`# Issue: ${e.title}`, ''];
|
|
450
|
+
lines.push(`_severity: ${e.severity} · category: ${e.category} · id: \`${e.issueId}\`_`, '');
|
|
451
|
+
lines.push(`**${e.headline}**`, '');
|
|
452
|
+
if (e.excerpt) {
|
|
453
|
+
lines.push(`## Code (\`${e.excerpt.file}\` L${e.excerpt.startLine}-${e.excerpt.endLine})`, '', '```');
|
|
454
|
+
for (const l of e.excerpt.lines)
|
|
455
|
+
lines.push(l);
|
|
456
|
+
lines.push('```', '');
|
|
457
|
+
}
|
|
458
|
+
if (e.relatedIssues.length > 0) {
|
|
459
|
+
lines.push('## Related issues in the same area', '');
|
|
460
|
+
for (const r of e.relatedIssues)
|
|
461
|
+
lines.push(`- \`${r.id}\`: ${r.title}`);
|
|
462
|
+
lines.push('');
|
|
463
|
+
}
|
|
464
|
+
if (e.similarFixes.length > 0) {
|
|
465
|
+
lines.push('## Past commits referencing this rule', '');
|
|
466
|
+
for (const f of e.similarFixes)
|
|
467
|
+
lines.push(`- ${f.sha.slice(0, 7)} (${f.date}) ${f.subject}`);
|
|
468
|
+
lines.push('');
|
|
469
|
+
}
|
|
470
|
+
if (e.fix) {
|
|
471
|
+
lines.push('## Suggested action', '', e.fix.instruction, '');
|
|
472
|
+
if (e.fix.suggestedTest)
|
|
473
|
+
lines.push('**Verify:** ' + e.fix.suggestedTest, '');
|
|
474
|
+
}
|
|
475
|
+
console.log(lines.join('\n'));
|
|
476
|
+
}
|
|
477
|
+
export function reportReviewMarkdown(report) {
|
|
478
|
+
const lines = ['# PR Review', ''];
|
|
479
|
+
if (!report.available) {
|
|
480
|
+
lines.push(`> ${report.reason ?? 'Review unavailable.'}`);
|
|
481
|
+
console.log(lines.join('\n'));
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const verdictBadge = report.verdict === 'block' ? '🚫 BLOCK' : report.verdict === 'review' ? '👀 REVIEW' : '✅ OK';
|
|
485
|
+
lines.push(`_base **${report.base.ref}** (${report.base.resolvedSha?.slice(0, 7) ?? '?'}) → head **${report.head.ref}** (${report.head.resolvedSha?.slice(0, 7) ?? '?'})_`, '', `**Verdict:** ${verdictBadge}`, '');
|
|
486
|
+
if (report.summary.length > 0) {
|
|
487
|
+
for (const s of report.summary)
|
|
488
|
+
lines.push(`- ${s}`);
|
|
489
|
+
lines.push('');
|
|
490
|
+
}
|
|
491
|
+
if (report.changedFiles.length > 0) {
|
|
492
|
+
lines.push('## Changed files', '');
|
|
493
|
+
lines.push('| File | Status | Risk | CC | ΔCC |');
|
|
494
|
+
lines.push('| --- | --- | ---: | ---: | ---: |');
|
|
495
|
+
for (const f of report.changedFiles.slice(0, 50)) {
|
|
496
|
+
const risk = f.riskScore !== null ? f.riskScore.toFixed(1) : '-';
|
|
497
|
+
const cc = f.cyclomaticComplexity !== null ? String(f.cyclomaticComplexity) : '-';
|
|
498
|
+
const dcc = f.cyclomaticDelta === null ? '-' : signed(f.cyclomaticDelta);
|
|
499
|
+
lines.push(`| \`${f.relativePath}\` | ${f.status} | ${risk} | ${cc} | ${dcc} |`);
|
|
500
|
+
}
|
|
501
|
+
if (report.changedFiles.length > 50) {
|
|
502
|
+
lines.push('', `_... and ${report.changedFiles.length - 50} more files_`);
|
|
503
|
+
}
|
|
504
|
+
lines.push('');
|
|
505
|
+
}
|
|
506
|
+
if (report.newCycles.length > 0) {
|
|
507
|
+
lines.push('## New / expanded import cycles', '');
|
|
508
|
+
for (const c of report.newCycles) {
|
|
509
|
+
lines.push(`- **${c.classification}** (${c.size} files): ${c.files.map((f) => `\`${f}\``).join(' → ')}`);
|
|
510
|
+
}
|
|
511
|
+
lines.push('');
|
|
512
|
+
}
|
|
513
|
+
if (report.riskyFunctions.length > 0) {
|
|
514
|
+
lines.push('## Risky functions', '');
|
|
515
|
+
lines.push('| Function | File | CC | Reason | Δ from base |');
|
|
516
|
+
lines.push('| --- | --- | ---: | --- | --- |');
|
|
517
|
+
for (const fn of report.riskyFunctions.slice(0, 30)) {
|
|
518
|
+
const baseInfo = fn.baseCc === null ? 'new' : `${fn.baseCc} → ${fn.cyclomaticComplexity}`;
|
|
519
|
+
lines.push(`| \`${fn.name}\` | \`${fn.file}\`:L${fn.line} | ${fn.cyclomaticComplexity} | ${fn.reason} | ${baseInfo} |`);
|
|
520
|
+
}
|
|
521
|
+
lines.push('');
|
|
522
|
+
}
|
|
523
|
+
if (report.dependencyChanges.length > 0) {
|
|
524
|
+
lines.push('## Dependency changes', '');
|
|
525
|
+
for (const d of report.dependencyChanges) {
|
|
526
|
+
const wsLabel = d.workspace ? ` (${d.workspace})` : '';
|
|
527
|
+
lines.push(`### \`${d.manifestFile}\`${wsLabel}`, '');
|
|
528
|
+
for (const a of d.added)
|
|
529
|
+
lines.push(`- ➕ \`${a.name}@${a.version}\` (${a.kind})`);
|
|
530
|
+
for (const r of d.removed)
|
|
531
|
+
lines.push(`- ➖ \`${r.name}@${r.version}\` (${r.kind})`);
|
|
532
|
+
for (const b of d.bumped)
|
|
533
|
+
lines.push(`- 🔄 \`${b.name}\`: \`${b.from}\` → \`${b.to}\` (${b.kind})`);
|
|
534
|
+
lines.push('');
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
console.log(lines.join('\n'));
|
|
538
|
+
}
|
|
366
539
|
export function reportWorkspacesMarkdown(info) {
|
|
367
540
|
const lines = ['# Workspaces', ''];
|
|
368
541
|
lines.push(`_kind: **${info.kind}**${info.source ? ` · source: ${info.source}` : ''} · ${info.packages.length} package(s)_`, '');
|