@hublo/sentinel 1.5.0-alpha.5 → 1.5.0-alpha.6

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-JNXDJ2QD.js";
18
+ } from "../chunk-JJKFLYHK.js";
19
19
  import {
20
20
  resolveContext
21
21
  } from "../chunk-KLEXJFJ6.js";
@@ -4806,22 +4806,28 @@ function helpFor(key) {
4806
4806
  }
4807
4807
 
4808
4808
  // src/roles/static-analysis/report.ts
4809
- function hasFinding(report) {
4810
- return report.rows.some((row) => row.over);
4811
- }
4812
4809
  function renderConsole(report) {
4813
4810
  const lines = [];
4814
4811
  const width = (pick) => Math.max(...report.rows.map((row) => pick(row).length));
4815
4812
  const label = (row) => helpFor(row.metric)?.label ?? row.metric;
4816
4813
  const value = (row) => `${row.value} ${row.unit}`;
4817
- const limit = (row) => row.threshold === null ? "no threshold" : `max ${row.threshold}${row.moved ? " (module)" : ""}`;
4818
4814
  const labelWidth = width(label);
4819
4815
  const valueWidth = width(value);
4820
4816
  lines.push(`sentinel static-analysis: ${report.module}`);
4821
4817
  for (const row of report.rows) {
4822
- lines.push(
4823
- ` ${row.over ? "!" : " "} ${label(row).padEnd(labelWidth)} ${value(row).padStart(valueWidth)} ${limit(row)}`
4824
- );
4818
+ lines.push(` ${label(row).padEnd(labelWidth)} ${value(row).padStart(valueWidth)}`);
4819
+ }
4820
+ if (report.settings.length > 0) {
4821
+ const keyWidth = Math.max(...report.settings.map((setting) => setting.key.length));
4822
+ lines.push("", " measured with");
4823
+ for (const setting of report.settings) {
4824
+ lines.push(
4825
+ ` ${setting.key.padEnd(keyWidth)} ${setting.value}${setting.moved ? " (module)" : ""}`
4826
+ );
4827
+ }
4828
+ if (report.settings.some((setting) => setting.moved)) {
4829
+ lines.push(` (module) = overridden in ${report.module}'s static-analysis.config.mjs`);
4830
+ }
4825
4831
  }
4826
4832
  if (report.byLanguage && Object.keys(report.byLanguage).length > 0) {
4827
4833
  lines.push("", " per language, where a clone was found");
@@ -4856,13 +4862,19 @@ function escape(text) {
4856
4862
  function renderHtml(report) {
4857
4863
  const rows = report.rows.map((row) => {
4858
4864
  const help = helpFor(row.metric);
4859
- return ` <tr class="${row.over ? "over" : ""}">
4865
+ return ` <tr>
4860
4866
  <th scope="row">${escape(help?.label ?? row.metric)}</th>
4861
4867
  <td class="num">${escape(String(row.value))} <span class="unit">${escape(row.unit)}</span></td>
4862
- <td class="num">${row.threshold === null ? '<span class="unit">none</span>' : escape(String(row.threshold))}${row.moved ? ' <span class="moved">module</span>' : ""}</td>
4863
4868
  <td class="caveat">${escape(help?.caveat ?? "")}</td>
4864
4869
  </tr>`;
4865
4870
  }).join("\n");
4871
+ const settings = report.settings.map(
4872
+ (setting) => ` <tr>
4873
+ <th scope="row"><code>${escape(setting.key)}</code></th>
4874
+ <td class="num">${escape(setting.value)}</td>
4875
+ <td>${setting.moved ? '<span class="moved">overridden in this module</span>' : ""}</td>
4876
+ </tr>`
4877
+ ).join("\n");
4866
4878
  return `<!doctype html>
4867
4879
  <html lang="en">
4868
4880
  <head>
@@ -4878,14 +4890,16 @@ function renderHtml(report) {
4878
4890
  main { max-width: 60rem; margin: 0 auto; }
4879
4891
  h1 { font-size: 1.25rem; margin: 0 0 0.25rem; }
4880
4892
  p.sub { color: var(--dim); margin: 0 0 1.5rem; }
4893
+ h2 { font-size: 0.95rem; margin: 2.5rem 0 0.5rem; }
4881
4894
  table { width: 100%; border-collapse: collapse; }
4882
4895
  th, td { text-align: left; padding: 0.6rem 0.75rem; border-bottom: 1px solid var(--rule);
4883
4896
  vertical-align: top; }
4884
4897
  thead th { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--dim); }
4885
4898
  td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
4886
- .unit, .moved { color: var(--dim); font-size: 0.85em; }
4899
+ code { font: 0.9em ui-monospace, SFMono-Regular, Menlo, monospace; }
4900
+ .unit { color: var(--dim); font-size: 0.85em; }
4901
+ .moved { color: var(--warn); font-size: 0.85em; }
4887
4902
  .caveat { color: var(--dim); font-size: 0.9em; }
4888
- tr.over th, tr.over td.num { color: var(--warn); font-weight: 600; }
4889
4903
  </style>
4890
4904
  </head>
4891
4905
  <body>
@@ -4894,12 +4908,21 @@ function renderHtml(report) {
4894
4908
  <p class="sub">Passes: ${escape(report.passes.join(", "))}</p>
4895
4909
  <table>
4896
4910
  <thead>
4897
- <tr><th scope="col">Metric</th><th scope="col">Value</th><th scope="col">Threshold</th><th scope="col">What it does not say</th></tr>
4911
+ <tr><th scope="col">Metric</th><th scope="col">Value</th><th scope="col">What it does not say</th></tr>
4898
4912
  </thead>
4899
4913
  <tbody>
4900
4914
  ${rows}
4901
4915
  </tbody>
4902
4916
  </table>
4917
+ <h2>Measured with</h2>
4918
+ <table>
4919
+ <thead>
4920
+ <tr><th scope="col">Setting</th><th scope="col">Value</th><th scope="col">Origin</th></tr>
4921
+ </thead>
4922
+ <tbody>
4923
+ ${settings}
4924
+ </tbody>
4925
+ </table>
4903
4926
  </main>
4904
4927
  </body>
4905
4928
  </html>
@@ -4984,6 +5007,14 @@ export default defineStaticAnalysis({
4984
5007
  // Named product journeys, as { 'the name product uses': ['src/some/path/**'] }. Paths are
4985
5008
  // relative to this module, because one run measures one module.
4986
5009
  journeys: {},
5010
+
5011
+ // Everything else comes from the role, and this is the door when a module must move one of its
5012
+ // values. Anything set here is printed under "measured with" and marked \`(module)\`, so nobody
5013
+ // compares this module's figures with its neighbour's without seeing that they differ:
5014
+ //
5015
+ // overrides: {
5016
+ // duplication: { minTokens: 100 },
5017
+ // },
4987
5018
  })
4988
5019
  `;
4989
5020
  var JscpdAdapter = class extends BaseAdapter {
@@ -5087,7 +5118,7 @@ ${result.stderr ?? ""}`
5087
5118
  );
5088
5119
  return { ok: false, code: result.status ?? 1 };
5089
5120
  }
5090
- const report = buildReport(ctx, rawReportDir);
5121
+ const report = buildReport(ctx, rawReportDir, config);
5091
5122
  if (!report) {
5092
5123
  process.stderr.write(`sentinel static-analysis(jscpd): jscpd wrote no report
5093
5124
  `);
@@ -5135,7 +5166,18 @@ ${result.stderr ?? ""}`
5135
5166
  function analysedNothing(stderr) {
5136
5167
  return typeof stderr === "string" && stderr.includes("--fail-on-empty");
5137
5168
  }
5138
- function buildReport(ctx, reportDir) {
5169
+ function duplicationSettings(config) {
5170
+ const moved = (key) => config.moved.some((path) => path === key || path.startsWith(`${key}.`));
5171
+ const row = (key, value) => ({ key, value, moved: moved(key) });
5172
+ return [
5173
+ row("duplication.minTokens", String(config.duplication.minTokens)),
5174
+ row("duplication.minLines", String(config.duplication.minLines)),
5175
+ row("duplication.kinds", config.duplication.kinds.join(", ")),
5176
+ row("languages", `${config.languages.length}: ${config.languages.join(", ")}`),
5177
+ row("ignore", `${config.ignore.length} patterns`)
5178
+ ];
5179
+ }
5180
+ function buildReport(ctx, reportDir, config) {
5139
5181
  const path = join19(reportDir, "jscpd-report.json");
5140
5182
  if (!existsSync14(path)) return void 0;
5141
5183
  let parsed;
@@ -5148,25 +5190,8 @@ function buildReport(ctx, reportDir) {
5148
5190
  if (!total) return void 0;
5149
5191
  const percent = Number((total.percentage ?? 0).toFixed(2));
5150
5192
  const rows = [
5151
- {
5152
- metric: "duplicatedLinesPercent",
5153
- value: percent,
5154
- unit: "%",
5155
- // No threshold, and none is coming from here: see `preset-data.ts`. The figure is reported as
5156
- // measured, and what makes it readable is the distribution it sits in, which one module
5157
- // cannot know.
5158
- threshold: null,
5159
- over: false,
5160
- moved: false
5161
- },
5162
- {
5163
- metric: "clones",
5164
- value: total.clones ?? 0,
5165
- unit: "entries",
5166
- threshold: null,
5167
- over: false,
5168
- moved: false
5169
- }
5193
+ { metric: "duplicatedLinesPercent", value: percent, unit: "%" },
5194
+ { metric: "clones", value: total.clones ?? 0, unit: "entries" }
5170
5195
  ];
5171
5196
  const formats = parsed.statistics?.formats ?? {};
5172
5197
  const byLanguage = Object.fromEntries(
@@ -5179,7 +5204,13 @@ function buildReport(ctx, reportDir) {
5179
5204
  }
5180
5205
  ])
5181
5206
  );
5182
- return { module: ctx.module, passes: ["duplication"], rows, byLanguage };
5207
+ return {
5208
+ module: ctx.module,
5209
+ passes: ["duplication"],
5210
+ rows,
5211
+ settings: duplicationSettings(config),
5212
+ byLanguage
5213
+ };
5183
5214
  }
5184
5215
  function emit(report, shape, cwd) {
5185
5216
  if (shape === "console") {
@@ -5197,8 +5228,12 @@ function emit(report, shape, cwd) {
5197
5228
  function metricsOf(report) {
5198
5229
  return {
5199
5230
  passes: report.passes,
5200
- finding: hasFinding(report),
5201
5231
  ...Object.fromEntries(report.rows.map((row) => [row.metric, row.value])),
5232
+ // Carried into `--json` and the CI comment for the same reason the console prints them: a
5233
+ // figure whose settings are unknown cannot be compared with the next module's.
5234
+ settings: Object.fromEntries(
5235
+ report.settings.map((s) => [s.key, { value: s.value, moved: s.moved }])
5236
+ ),
5202
5237
  byLanguage: report.byLanguage
5203
5238
  };
5204
5239
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  registerAdapters,
7
7
  resolve,
8
8
  setDefaultRunner
9
- } from "./chunk-JNXDJ2QD.js";
9
+ } from "./chunk-JJKFLYHK.js";
10
10
  import {
11
11
  BaseAdapter
12
12
  } from "./chunk-KLEXJFJ6.js";
@@ -1171,6 +1171,14 @@ $ sentinel --init --dry-run
1171
1171
  + // Named product journeys, as { 'the name product uses': ['src/some/path/**'] }. Paths are
1172
1172
  + // relative to this module, because one run measures one module.
1173
1173
  + journeys: {},
1174
+ +
1175
+ + // Everything else comes from the role, and this is the door when a module must move one of its
1176
+ + // values. Anything set here is printed under "measured with" and marked `(module)`, so nobody
1177
+ + // compares this module's figures with its neighbour's without seeing that they differ:
1178
+ + //
1179
+ + // overrides: {
1180
+ + // duplication: { minTokens: 100 },
1181
+ + // },
1174
1182
  + })
1175
1183
 
1176
1184
  update package.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.5.0-alpha.5",
3
+ "version": "1.5.0-alpha.6",
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",