@hublo/sentinel 1.5.0-alpha.12 → 1.5.0-alpha.13
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/bin/sentinel.js
CHANGED
|
@@ -4934,6 +4934,9 @@ function renderJson(report) {
|
|
|
4934
4934
|
return `${JSON.stringify({ ...report, rows }, null, 2)}
|
|
4935
4935
|
`;
|
|
4936
4936
|
}
|
|
4937
|
+
function prose(text) {
|
|
4938
|
+
return escape(text).replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
4939
|
+
}
|
|
4937
4940
|
function escape(text) {
|
|
4938
4941
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4939
4942
|
}
|
|
@@ -4943,14 +4946,21 @@ function renderHtml(report) {
|
|
|
4943
4946
|
return ` <tr>
|
|
4944
4947
|
<th scope="row">${escape(help?.label ?? row.metric)}</th>
|
|
4945
4948
|
<td class="num">${escape(shown(row))} <span class="unit">${escape(row.unit)}</span></td>
|
|
4946
|
-
<td class="caveat">${escape(help?.caveat ?? "")}</td>
|
|
4947
4949
|
</tr>`;
|
|
4948
4950
|
}).join("\n");
|
|
4951
|
+
const reserves = report.rows.map((row) => ({ row, help: helpFor(row.metric) })).filter((entry) => entry.help).map(
|
|
4952
|
+
({ row, help }) => ` <details>
|
|
4953
|
+
<summary>${escape(help?.label ?? row.metric)}<span class="what">${prose(help?.what ?? "")}</span></summary>
|
|
4954
|
+
<p class="read"><b>How to read it.</b> ${prose(help?.read ?? "")}</p>
|
|
4955
|
+
<p><b>What it does not say.</b> ${prose(help?.caveat ?? "")}</p>
|
|
4956
|
+
<p class="source">Source: ${prose(help?.source ?? "")}</p>
|
|
4957
|
+
</details>`
|
|
4958
|
+
).join("\n");
|
|
4949
4959
|
const settings = report.settings.map(
|
|
4950
4960
|
(setting) => ` <tr>
|
|
4951
4961
|
<th scope="row"><code>${escape(setting.key)}</code></th>
|
|
4952
4962
|
<td class="num">${escape(setting.value)}</td>
|
|
4953
|
-
<td>${setting.moved ? '<span class="moved">overridden
|
|
4963
|
+
<td>${setting.moved ? '<span class="moved">overridden here</span>' : ""}</td>
|
|
4954
4964
|
</tr>`
|
|
4955
4965
|
).join("\n");
|
|
4956
4966
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -4972,16 +4982,19 @@ function renderHtml(report) {
|
|
|
4972
4982
|
<td class="num">${finding.confidence ?? ""}</td>
|
|
4973
4983
|
</tr>`
|
|
4974
4984
|
).join("\n");
|
|
4975
|
-
return `
|
|
4976
|
-
<
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4985
|
+
return `
|
|
4986
|
+
<h2>${escape(label)} · where to look</h2>
|
|
4987
|
+
<section>
|
|
4988
|
+
<table class="places">
|
|
4989
|
+
<thead>
|
|
4990
|
+
<tr><th scope="col">File</th><th scope="col">Name</th><th scope="col">Lines</th><th scope="col">Confidence</th></tr>
|
|
4991
|
+
</thead>
|
|
4992
|
+
<tbody>
|
|
4981
4993
|
${rows2}
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
${rest > 0 ? `
|
|
4994
|
+
</tbody>
|
|
4995
|
+
</table>
|
|
4996
|
+
${rest > 0 ? ` <p class="sub">The ${HTML_GROUP_LIMIT} longest are listed; ${rest} more are in <code>${SCRIPTS.json}</code>.</p>` : ""}
|
|
4997
|
+
</section>`;
|
|
4985
4998
|
}).join("\n");
|
|
4986
4999
|
return `<!doctype html>
|
|
4987
5000
|
<html lang="en">
|
|
@@ -4989,49 +5002,165 @@ ${rest > 0 ? ` <p class="sub">The ${HTML_GROUP_LIMIT} longest are listed; ${res
|
|
|
4989
5002
|
<meta charset="utf-8">
|
|
4990
5003
|
<title>Static analysis: ${escape(report.module)}</title>
|
|
4991
5004
|
<style>
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5005
|
+
/*
|
|
5006
|
+
* The Hublo palette, token by token, read from "libs/front/theme/src/branding/definitions/hublo"
|
|
5007
|
+
* rather than approximated: "commonColors.ts" holds the values and "colorTokens.ts" the semantic
|
|
5008
|
+
* names used in the comments below.
|
|
5009
|
+
*
|
|
5010
|
+
* \u26D4 One theme, and that is a decision rather than an omission: the charter has no dark palette,
|
|
5011
|
+
* zero occurrences of "prefers-color-scheme" in the whole theme package, so a dark variant here
|
|
5012
|
+
* would be charter this page invented. Every colour is painted explicitly instead, so the page
|
|
5013
|
+
* holds on whatever ground opens it.
|
|
5014
|
+
*/
|
|
5015
|
+
:root {
|
|
5016
|
+
color-scheme: light;
|
|
5017
|
+
--page: #f5f8fd; /* neutral5, background.page-header */
|
|
5018
|
+
--surface: #ffffff; /* neutral0, background.surface */
|
|
5019
|
+
--ink: #131415; /* neutral35, text.primary */
|
|
5020
|
+
--ink-soft: #5e6972; /* neutral30, text.secondary */
|
|
5021
|
+
--label: #72808e; /* neutral25, text.label-default */
|
|
5022
|
+
--rule: #ced7e0; /* neutral15, border.page-header */
|
|
5023
|
+
--brand: #3a4392; /* blue15, text.brand */
|
|
5024
|
+
--brand-bg: #e5ecff; /* blue0, background.brand */
|
|
5025
|
+
--moved: #c83c3c; /* red15, text.destructive */
|
|
5026
|
+
}
|
|
5027
|
+
/*
|
|
5028
|
+
* The brand faces first, then Inter, then the system. They are loaded at runtime from a branding
|
|
5029
|
+
* artefact in the applications, so they cannot be embedded here without an external request, and
|
|
5030
|
+
* an external request is the one thing this page must not make. A viewer who has them sees them.
|
|
5031
|
+
*/
|
|
5032
|
+
body {
|
|
5033
|
+
margin: 0;
|
|
5034
|
+
padding: 2.5rem 1rem 4rem;
|
|
5035
|
+
background: var(--page);
|
|
5036
|
+
color: var(--ink);
|
|
5037
|
+
font: 15px/1.6 'Brand Sans', Inter, ui-sans-serif, system-ui, sans-serif;
|
|
5038
|
+
}
|
|
5039
|
+
main { max-width: 64rem; margin: 0 auto; }
|
|
5040
|
+
header { border-left: 3px solid var(--brand); padding-left: 0.9rem; margin-bottom: 2rem; }
|
|
5041
|
+
h1 { font-size: 1.3rem; font-weight: 600; margin: 0 0 0.2rem; letter-spacing: -0.01em; }
|
|
5042
|
+
h2 {
|
|
5043
|
+
font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.08em;
|
|
5044
|
+
color: var(--label); margin: 2.5rem 0 0.6rem; font-weight: 600;
|
|
5045
|
+
}
|
|
5046
|
+
p.sub { color: var(--ink-soft); margin: 0; font-size: 0.9rem; }
|
|
5047
|
+
section {
|
|
5048
|
+
background: var(--surface); border: 1px solid var(--rule); border-radius: 10px;
|
|
5049
|
+
padding: 0.25rem 1.1rem; overflow-x: auto;
|
|
5050
|
+
}
|
|
5002
5051
|
table { width: 100%; border-collapse: collapse; }
|
|
5003
|
-
th, td { text-align: left; padding: 0.
|
|
5004
|
-
|
|
5005
|
-
thead th { font-size: 0.
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5052
|
+
th, td { text-align: left; padding: 0.7rem 0.6rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
|
|
5053
|
+
tr:last-child th, tr:last-child td { border-bottom: 0; }
|
|
5054
|
+
thead th { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--label); font-weight: 600; }
|
|
5055
|
+
tbody th { font-weight: 500; white-space: nowrap; }
|
|
5056
|
+
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 500; }
|
|
5057
|
+
/*
|
|
5058
|
+
* Which column absorbs the slack, per table. One global rule on the shared class got this
|
|
5059
|
+
* wrong in all three at once: the figures read well and the settings table clipped its origin
|
|
5060
|
+
* column while the findings table crushed its file paths into three lines.
|
|
5061
|
+
*/
|
|
5062
|
+
table.figures td.num { width: 100%; }
|
|
5063
|
+
/*
|
|
5064
|
+
* The settings table does NOT force its value column wide. One of its values is the test glob,
|
|
5065
|
+
* long enough to push the table past the card, and the origin column then slid out of view and
|
|
5066
|
+
* was clipped: "overridden in this mo". Long values wrap instead, and the origin stays readable.
|
|
5067
|
+
*/
|
|
5068
|
+
table.settings td.num { white-space: normal; overflow-wrap: anywhere; }
|
|
5069
|
+
table.settings td:last-child { text-align: right; white-space: nowrap; }
|
|
5070
|
+
table.places td:first-child { width: 100%; }
|
|
5071
|
+
table.places td.num { text-align: right; }
|
|
5072
|
+
td.read { color: var(--ink-soft); font-size: 0.88rem; }
|
|
5073
|
+
tbody th { color: var(--ink); }
|
|
5074
|
+
code { font: 0.86em ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink-soft); }
|
|
5075
|
+
.unit { color: var(--label); font-size: 0.82em; font-weight: 400; }
|
|
5076
|
+
.moved {
|
|
5077
|
+
color: var(--moved); font-size: 0.78rem; font-weight: 500;
|
|
5078
|
+
white-space: nowrap;
|
|
5079
|
+
}
|
|
5080
|
+
/* The collapsed reserves. Closed by default: the figures are what the page opens on. */
|
|
5081
|
+
details { border-bottom: 1px solid var(--rule); }
|
|
5082
|
+
details:last-of-type { border-bottom: 0; }
|
|
5083
|
+
summary {
|
|
5084
|
+
cursor: pointer; padding: 0.7rem 0.6rem; font-size: 0.9rem; font-weight: 500;
|
|
5085
|
+
display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline;
|
|
5086
|
+
}
|
|
5087
|
+
/*
|
|
5088
|
+
* The chevron is drawn, not inherited: "display: flex" on a summary removes the native marker,
|
|
5089
|
+
* and a collapsed block with no affordance is a block nobody opens.
|
|
5090
|
+
*/
|
|
5091
|
+
summary { list-style: none; }
|
|
5092
|
+
summary::-webkit-details-marker { display: none; }
|
|
5093
|
+
summary::before {
|
|
5094
|
+
content: ''; flex: none; width: 0.38rem; height: 0.38rem; margin: 0.3rem 0.1rem 0 0;
|
|
5095
|
+
border-right: 1.5px solid var(--label); border-bottom: 1.5px solid var(--label);
|
|
5096
|
+
transform: rotate(-45deg); transition: transform 0.15s ease;
|
|
5097
|
+
}
|
|
5098
|
+
details[open] summary::before { transform: rotate(45deg); border-color: var(--brand); }
|
|
5099
|
+
@media (prefers-reduced-motion: reduce) { summary::before { transition: none; } }
|
|
5100
|
+
summary:hover { color: var(--brand); }
|
|
5101
|
+
/* One line, truncated: the summary is a hint, the body is the text. Opening it shows all. */
|
|
5102
|
+
summary .what {
|
|
5103
|
+
color: var(--label); font-size: 0.82rem; font-weight: 400;
|
|
5104
|
+
flex: 1 1 18rem; min-width: 0;
|
|
5105
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
5106
|
+
}
|
|
5107
|
+
details[open] summary .what { white-space: normal; overflow: visible; }
|
|
5108
|
+
details[open] summary { color: var(--brand); }
|
|
5109
|
+
details p.read { color: var(--ink-soft); }
|
|
5110
|
+
details p b { color: var(--ink); font-weight: 600; }
|
|
5111
|
+
details p { margin: 0 0 0.7rem; padding: 0 0.6rem; color: var(--ink-soft); font-size: 0.88rem; max-width: 52rem; }
|
|
5112
|
+
details p.source { color: var(--label); font-size: 0.8rem; }
|
|
5113
|
+
footer { color: var(--label); font-size: 0.78rem; margin-top: 2.5rem; }
|
|
5114
|
+
@media (max-width: 40rem) {
|
|
5115
|
+
section { padding: 0.25rem 0.6rem; }
|
|
5116
|
+
summary .what { display: none; }
|
|
5117
|
+
}
|
|
5011
5118
|
</style>
|
|
5012
5119
|
</head>
|
|
5013
5120
|
<body>
|
|
5014
5121
|
<main>
|
|
5015
|
-
<
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5122
|
+
<header>
|
|
5123
|
+
<h1>${escape(report.module)}</h1>
|
|
5124
|
+
<p class="sub">Static analysis, ${escape(report.passes.join(" / "))}</p>
|
|
5125
|
+
</header>
|
|
5126
|
+
|
|
5127
|
+
<h2>Measured</h2>
|
|
5128
|
+
<section>
|
|
5129
|
+
<table class="figures">
|
|
5130
|
+
<thead>
|
|
5131
|
+
<tr><th scope="col">Metric</th><th scope="col">Value</th></tr>
|
|
5132
|
+
</thead>
|
|
5133
|
+
<tbody>
|
|
5022
5134
|
${rows}
|
|
5023
|
-
|
|
5024
|
-
|
|
5135
|
+
</tbody>
|
|
5136
|
+
</table>
|
|
5137
|
+
</section>
|
|
5138
|
+
${report.notes.length > 0 ? `
|
|
5139
|
+
<h2>Not measured</h2>
|
|
5140
|
+
<section>
|
|
5141
|
+
<table><tbody>
|
|
5142
|
+
${report.notes.map((note) => ` <tr><td class="read">${escape(note)}</td></tr>`).join("\n")}
|
|
5143
|
+
</tbody></table>
|
|
5144
|
+
</section>
|
|
5145
|
+
` : ""}
|
|
5146
|
+
<h2>What these numbers do not say</h2>
|
|
5147
|
+
<section>
|
|
5148
|
+
${reserves}
|
|
5149
|
+
</section>
|
|
5150
|
+
|
|
5025
5151
|
<h2>Measured with</h2>
|
|
5026
|
-
<
|
|
5027
|
-
<
|
|
5028
|
-
<
|
|
5029
|
-
|
|
5030
|
-
|
|
5152
|
+
<section>
|
|
5153
|
+
<table class="settings">
|
|
5154
|
+
<thead>
|
|
5155
|
+
<tr><th scope="col">Setting</th><th scope="col">Value</th><th scope="col">Origin</th></tr>
|
|
5156
|
+
</thead>
|
|
5157
|
+
<tbody>
|
|
5031
5158
|
${settings}
|
|
5032
|
-
|
|
5033
|
-
|
|
5159
|
+
</tbody>
|
|
5160
|
+
</table>
|
|
5161
|
+
</section>
|
|
5034
5162
|
${findings}
|
|
5163
|
+
<footer>sentinel static-analysis</footer>
|
|
5035
5164
|
</main>
|
|
5036
5165
|
</body>
|
|
5037
5166
|
</html>
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hublo/sentinel",
|
|
3
|
-
"version": "1.5.0-alpha.
|
|
3
|
+
"version": "1.5.0-alpha.13",
|
|
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",
|