@hublo/sentinel 1.5.0-alpha.10 → 1.5.0-alpha.12

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.
@@ -15,7 +15,7 @@ import {
15
15
  palette,
16
16
  registerAdapters,
17
17
  resolve
18
- } from "../chunk-FWGU7QLH.js";
18
+ } from "../chunk-RH7TOWSN.js";
19
19
  import {
20
20
  resolveContext
21
21
  } from "../chunk-KLEXJFJ6.js";
@@ -4864,6 +4864,16 @@ function helpFor(key) {
4864
4864
  }
4865
4865
 
4866
4866
  // src/roles/static-analysis/report.ts
4867
+ var SCRIPTS = { json: "static-analysis:json", html: "static-analysis:html" };
4868
+ function secondSide(finding) {
4869
+ if (!finding.other) return "";
4870
+ const file = (at) => at.split(":")[0] ?? at;
4871
+ const sameFile = file(finding.other) === file(finding.path);
4872
+ const label = sameFile ? "and line" : "and";
4873
+ const shownSide = sameFile ? finding.other.split(":")[1] ?? finding.other : finding.other;
4874
+ return ` <span class="unit">${label}</span> <code>${escape(shownSide)}</code>`;
4875
+ }
4876
+ var HTML_GROUP_LIMIT = 50;
4867
4877
  function shown(row) {
4868
4878
  return row.unit === "%" && typeof row.value === "number" ? row.value.toFixed(2) : String(row.value);
4869
4879
  }
@@ -4900,6 +4910,12 @@ function renderConsole(report) {
4900
4910
  }
4901
4911
  lines.push(" a language is absent when no file of it was long enough to scan");
4902
4912
  }
4913
+ if (report.findings.length > 0) {
4914
+ lines.push(
4915
+ "",
4916
+ ` ${report.findings.length} place(s) to look, listed by \`${SCRIPTS.json}\` and \`${SCRIPTS.html}\``
4917
+ );
4918
+ }
4903
4919
  const caveats = report.rows.map((row) => ({ row, help: helpFor(row.metric) })).filter(
4904
4920
  (entry) => Boolean(entry.help)
4905
4921
  );
@@ -4937,6 +4953,36 @@ function renderHtml(report) {
4937
4953
  <td>${setting.moved ? '<span class="moved">overridden in this module</span>' : ""}</td>
4938
4954
  </tr>`
4939
4955
  ).join("\n");
4956
+ const groups = /* @__PURE__ */ new Map();
4957
+ for (const finding of report.findings) {
4958
+ const bucket = groups.get(finding.metric) ?? [];
4959
+ bucket.push(finding);
4960
+ groups.set(finding.metric, bucket);
4961
+ }
4962
+ const findings = [...groups.entries()].map(([metric, all2]) => {
4963
+ const label = helpFor(metric)?.label ?? metric;
4964
+ const sorted = [...all2].sort((a, b) => (b.lines ?? 0) - (a.lines ?? 0));
4965
+ const shown2 = sorted.slice(0, HTML_GROUP_LIMIT);
4966
+ const rest = sorted.length - shown2.length;
4967
+ const rows2 = shown2.map(
4968
+ (finding) => ` <tr>
4969
+ <td><code>${escape(finding.path)}</code>${secondSide(finding)}</td>
4970
+ <td>${finding.name ? `<code>${escape(finding.name)}</code>` : ""}</td>
4971
+ <td class="num">${finding.lines ?? ""}</td>
4972
+ <td class="num">${finding.confidence ?? ""}</td>
4973
+ </tr>`
4974
+ ).join("\n");
4975
+ return ` <h2>${escape(label)}: where to look</h2>
4976
+ <table>
4977
+ <thead>
4978
+ <tr><th scope="col">File</th><th scope="col">Name</th><th scope="col">Lines</th><th scope="col">Confidence</th></tr>
4979
+ </thead>
4980
+ <tbody>
4981
+ ${rows2}
4982
+ </tbody>
4983
+ </table>
4984
+ ${rest > 0 ? ` <p class="sub">The ${HTML_GROUP_LIMIT} longest are listed; ${rest} more are in <code>${SCRIPTS.json}</code>.</p>` : ""}`;
4985
+ }).join("\n");
4940
4986
  return `<!doctype html>
4941
4987
  <html lang="en">
4942
4988
  <head>
@@ -4985,6 +5031,7 @@ ${rows}
4985
5031
  ${settings}
4986
5032
  </tbody>
4987
5033
  </table>
5034
+ ${findings}
4988
5035
  </main>
4989
5036
  </body>
4990
5037
  </html>
@@ -5403,7 +5450,7 @@ ${result.stderr ?? ""}`
5403
5450
  const category = finding.category ?? "unknown";
5404
5451
  byCategory[category] = (byCategory[category] ?? 0) + 1;
5405
5452
  }
5406
- return { kind: "figures", statistics, byCategory };
5453
+ return { kind: "figures", statistics, byCategory, findings: parsed.findings ?? [] };
5407
5454
  } finally {
5408
5455
  dispose();
5409
5456
  }
@@ -5437,7 +5484,12 @@ ${result.stderr ?? ""}`
5437
5484
  );
5438
5485
  return { kind: "failed", code: 1 };
5439
5486
  }
5440
- return { kind: "figures", total, formats: parsed?.statistics?.formats ?? {} };
5487
+ return {
5488
+ kind: "figures",
5489
+ total,
5490
+ formats: parsed?.statistics?.formats ?? {},
5491
+ duplicates: parsed?.duplicates ?? []
5492
+ };
5441
5493
  } finally {
5442
5494
  dispose();
5443
5495
  }
@@ -5449,6 +5501,32 @@ function safeParse(text) {
5449
5501
  return void 0;
5450
5502
  }
5451
5503
  }
5504
+ function placesToLook(product, tests, dead) {
5505
+ const at = (side) => `${side?.name ?? "(unknown)"}${side?.start === void 0 ? "" : `:${side.start}`}`;
5506
+ const clones = (outcome, metric) => outcome.kind === "figures" ? outcome.duplicates.map((entry) => ({
5507
+ metric,
5508
+ path: at(entry.firstFile),
5509
+ other: at(entry.secondFile),
5510
+ ...entry.lines === void 0 ? {} : { lines: entry.lines }
5511
+ })) : [];
5512
+ const deadMetric = {
5513
+ "unused-file": "unusedFiles",
5514
+ "unused-export": "unusedExports",
5515
+ "unused-symbol": "unusedSymbols",
5516
+ "unused-import": "unusedImports"
5517
+ };
5518
+ return [
5519
+ ...clones(product, "clones"),
5520
+ ...clones(tests, "clonesTests"),
5521
+ ...dead.kind === "figures" ? dead.findings.map((finding) => ({
5522
+ metric: deadMetric[finding.category ?? ""] ?? "deadLinesPercent",
5523
+ path: finding.path ?? "(unknown)",
5524
+ ...finding.name ? { name: finding.name } : {},
5525
+ ...finding.lines === void 0 ? {} : { lines: finding.lines },
5526
+ ...finding.confidence === void 0 ? {} : { confidence: finding.confidence }
5527
+ })) : []
5528
+ ];
5529
+ }
5452
5530
  function assemble(ctx, config, product, tests, dead) {
5453
5531
  const share = (total) => Number((total.percentage ?? 0).toFixed(2));
5454
5532
  const rows = [
@@ -5508,6 +5586,7 @@ function assemble(ctx, config, product, tests, dead) {
5508
5586
  rows,
5509
5587
  notes,
5510
5588
  settings: duplicationSettings(config),
5589
+ findings: placesToLook(product, tests, dead),
5511
5590
  byLanguage
5512
5591
  };
5513
5592
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  registerAdapters,
7
7
  resolve,
8
8
  setDefaultRunner
9
- } from "./chunk-FWGU7QLH.js";
9
+ } from "./chunk-RH7TOWSN.js";
10
10
  import {
11
11
  BaseAdapter
12
12
  } from "./chunk-KLEXJFJ6.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.5.0-alpha.10",
3
+ "version": "1.5.0-alpha.12",
4
4
  "description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
5
5
  "type": "module",
6
6
  "license": "MIT",