@hublo/sentinel 1.5.0-alpha.27 → 1.5.0-alpha.28

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,10 +15,10 @@ import {
15
15
  palette,
16
16
  registerAdapters,
17
17
  resolve
18
- } from "../chunk-LDW6VLUY.js";
18
+ } from "../chunk-BYV4XD5W.js";
19
19
  import {
20
20
  resolveContext
21
- } from "../chunk-ZMVI65FF.js";
21
+ } from "../chunk-OS5TMEKH.js";
22
22
  import {
23
23
  ensureWorkspacePrep,
24
24
  findWorkspaceRoot,
@@ -148,7 +148,7 @@ async function runValidate(ctx) {
148
148
  );
149
149
  }
150
150
  if (!ctx.targets.includes("test")) return 1;
151
- const { validateModuleTests } = await import("../validate-AOJGDBHA.js");
151
+ const { validateModuleTests } = await import("../validate-3KEHAU66.js");
152
152
  const outcomes = modules.flatMap(
153
153
  (module) => validateModuleTests(module.name, module.root)
154
154
  );
@@ -52,8 +52,9 @@ import {
52
52
  rewriteToolchainImports,
53
53
  selfCommand,
54
54
  splitToolArgs,
55
- walk
56
- } from "./chunk-ZMVI65FF.js";
55
+ walk,
56
+ writtenFileFormatter
57
+ } from "./chunk-OS5TMEKH.js";
57
58
  import {
58
59
  DEFAULT_MAX_DIAGNOSTICS,
59
60
  WORKSPACE_ROOT_MARKER,
@@ -7284,6 +7285,59 @@ var JscpdAdapter = class extends BaseAdapter {
7284
7285
  if (emitting) await emit(report, shape, ctx.cwd, graph, coverage);
7285
7286
  return { ok: true, code: 0, metrics: metricsOf(report) };
7286
7287
  }
7288
+ /**
7289
+ * Format what `--init` wrote, with the MODULE's own formatter.
7290
+ *
7291
+ * ⛔ Found by adopting the role on `libs/front/ui`, where the module's `lint` target is
7292
+ * `prettier --check . && eslint .`. Adoption turned it red on the two files adoption itself had
7293
+ * just written:
7294
+ *
7295
+ * [warn] package.json
7296
+ * [warn] static-analysis.config.mjs
7297
+ *
7298
+ * So a team adopting this role is handed a red lint on a target the adoption never touched, for
7299
+ * something the adoption caused. That is what the test role already refuses in its own words:
7300
+ * "the migration handing its own consequence to whoever adopts, which is the one thing this role
7301
+ * must not do". It had the mechanism and this role did not use it.
7302
+ *
7303
+ * ⚠️ The blast radius is small and measured: **2 modules of 243** run `prettier --check` in their
7304
+ * own lint without having adopted our format role, `apps/front/host-admin` and `libs/front/ui`.
7305
+ * Small is not a reason to leave it: the two are exactly the modules a team would adopt by hand.
7306
+ *
7307
+ * The formatter is borrowed from the test role rather than written again, the same way the size
7308
+ * gauge borrows `resolveOxlint` from the lint role: it already knows how a module declares a
7309
+ * style, including the case where that style IS ours.
7310
+ *
7311
+ * ⛔ Never fatal. Adoption succeeded; only the tidy-up did not. Reverting an adoption over a
7312
+ * formatting pass would be the wrong trade, so a failure here is reported as what it is.
7313
+ *
7314
+ * No batching, unlike the test role: `--init` writes two files, and the OOM that forced batches
7315
+ * there came from handing a formatter 273 at once.
7316
+ */
7317
+ async afterInit(ctx) {
7318
+ const written = ctx.written ?? [];
7319
+ if (written.length === 0) return Promise.resolve();
7320
+ const formatter = writtenFileFormatter(ctx.cwd);
7321
+ if (formatter === void 0) {
7322
+ this.say(
7323
+ `no style is declared here, by this module or its workspace, so the ${written.length} file(s) this wrote keep the shape they were written in. Run your own formatter over them before committing.`
7324
+ );
7325
+ return Promise.resolve();
7326
+ }
7327
+ const pass = spawnSync6(formatter.bin, [...formatter.args, ...written], {
7328
+ cwd: ctx.cwd,
7329
+ encoding: "utf8"
7330
+ });
7331
+ this.say(
7332
+ pass.status === 0 ? `formatted the ${written.length} file(s) this wrote with ${formatter.origin}, so a format check passes on files it did not write itself.` : `${formatter.origin} could not run over the ${written.length} file(s) this wrote. Adoption itself is applied; run your formatter before committing.`
7333
+ );
7334
+ return Promise.resolve();
7335
+ }
7336
+ /** One line, prefixed like every other message this role writes. */
7337
+ say(message) {
7338
+ process.stderr.write(`sentinel static-analysis(jscpd): ${message}
7339
+ `);
7340
+ }
7287
7341
  /** `--run`: measure and show it. */
7288
7342
  async run(ctx) {
7289
7343
  return this.measure(ctx, true);
@@ -7518,7 +7572,7 @@ function safeParseDeadCode(text) {
7518
7572
  return void 0;
7519
7573
  }
7520
7574
  }
7521
- function placesToLook(product, tests, dead, complexity, graph, size) {
7575
+ function placesToLook(product, tests, dead, complexity, graph, size, unreadable) {
7522
7576
  const at = (side) => `${side?.name ?? "(unknown)"}${side?.start === void 0 ? "" : `:${side.start}`}`;
7523
7577
  const clones = (outcome, metric) => outcome.kind === "figures" ? outcome.duplicates.map((entry) => ({
7524
7578
  metric,
@@ -7543,7 +7597,7 @@ function placesToLook(product, tests, dead, complexity, graph, size) {
7543
7597
  name: fn.name,
7544
7598
  value: fn.complexity
7545
7599
  })),
7546
- ...dead.kind === "figures" ? dead.findings.map((finding) => ({
7600
+ ...dead.kind === "figures" && !unreadable ? dead.findings.map((finding) => ({
7547
7601
  metric: deadMetric[finding.category ?? ""] ?? "deadLinesPercent",
7548
7602
  path: finding.path ?? "(unknown)",
7549
7603
  ...finding.name ? { name: finding.name } : {},
@@ -7555,7 +7609,7 @@ function placesToLook(product, tests, dead, complexity, graph, size) {
7555
7609
  // One finding per GROUP of mutually importing files, not per cycle path: see `graph/scan.ts`.
7556
7610
  // The first file names the finding and the rest go in `other`, which is the shape the report
7557
7611
  // already renders for a clone's two sides.
7558
- ...graph.cycles.map((cycle) => ({
7612
+ ...(unreadable ? [] : graph.cycles).map((cycle) => ({
7559
7613
  metric: "cycles",
7560
7614
  path: cycle.files[0] ?? "(unknown)",
7561
7615
  other: cycle.files.slice(1).join(", "),
@@ -7593,7 +7647,7 @@ function placesToLook(product, tests, dead, complexity, graph, size) {
7593
7647
  })),
7594
7648
  // ⛔ Context, not defects. Every module has a most-imported file, and nothing here says the
7595
7649
  // count is too high: the caveat on the metric says so in the reader's own words.
7596
- ...graph.mostImported.map((file) => ({
7650
+ ...(unreadable ? [] : graph.mostImported).map((file) => ({
7597
7651
  metric: "mostImported",
7598
7652
  path: file.path,
7599
7653
  value: file.importedBy,
@@ -7779,17 +7833,22 @@ function assembleReport(ctx, config, product, tests, dead, complexity, graph, co
7779
7833
  * 2% of the files, so the reader looks for a big generated or seed file instead of concluding
7780
7834
  * anything about the code they write. Nothing is computed that the pass did not hand over.
7781
7835
  *
7782
- * ⚠️ "`;` ... unreachable IN FULL" and not "in N files", which is what this said first. The
7836
+ * ⚠️ "N files UNREACHABLE IN FULL" and not "in N files", which is what this said first. The
7783
7837
  * two numbers do NOT contain one another: dead lines also come from exports and symbols that
7784
7838
  * are dead inside files the walk DOES reach. Measured on `apps/front/console`, 0 files are
7785
7839
  * unreachable and 0.64% of lines are still dead, which read as a contradiction. On `agency`
7786
7840
  * the wording looked right only by coincidence, because 13 files carried 53 233 of its
7787
7841
  * 53 242 dead lines.
7842
+ *
7843
+ * ⛔ And a COMMA, not a semicolon. The markdown recap truncates a unit at its first `;`,
7844
+ * because the bundle units carry a second comparison that pushes a table row past readable
7845
+ * width. A semicolon here meant the recap silently dropped the very denominator this comment
7846
+ * exists for, which is the shape of defect the denominator was added to prevent.
7788
7847
  */
7789
7848
  {
7790
7849
  metric: "deadLinesPercent",
7791
7850
  value: share(stats),
7792
- unit: `% of lines; ${(stats.files ?? 0) - (stats.reachableFiles ?? 0)} of ${stats.files ?? 0} files are unreachable in full`
7851
+ unit: `% of lines, ${(stats.files ?? 0) - (stats.reachableFiles ?? 0)} of ${stats.files ?? 0} files unreachable in full`
7793
7852
  },
7794
7853
  // ⛔ Minus the files the TOOL itself labels `used-only-by-tests`. They were counted as
7795
7854
  // unreachable while the tool knew better: measured on `libs/nest/auth`, 5 of the 20. A file a
@@ -7897,7 +7956,7 @@ function assembleReport(ctx, config, product, tests, dead, complexity, graph, co
7897
7956
  rows,
7898
7957
  notes,
7899
7958
  settings,
7900
- findings: placesToLook(product, tests, dead, complexity, graph, size),
7959
+ findings: placesToLook(product, tests, dead, complexity, graph, size, unreadable),
7901
7960
  byLanguage
7902
7961
  };
7903
7962
  }
@@ -6553,6 +6553,7 @@ export {
6553
6553
  oxlintPath,
6554
6554
  oxlintSearchPath,
6555
6555
  walk,
6556
+ writtenFileFormatter,
6556
6557
  readTestAdoption,
6557
6558
  resolveContext,
6558
6559
  moduleEnvironment,
package/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  registerAdapters,
7
7
  resolve,
8
8
  setDefaultRunner
9
- } from "./chunk-LDW6VLUY.js";
9
+ } from "./chunk-BYV4XD5W.js";
10
10
  import {
11
11
  BaseAdapter
12
- } from "./chunk-ZMVI65FF.js";
12
+ } from "./chunk-OS5TMEKH.js";
13
13
  import "./chunk-JEYGPPSL.js";
14
14
  import "./chunk-GCHCLR7P.js";
15
15
  import "./chunk-O7REVMOC.js";
@@ -7,7 +7,7 @@ import {
7
7
  readTestAdoption,
8
8
  resolveVitest,
9
9
  suiteOf
10
- } from "./chunk-ZMVI65FF.js";
10
+ } from "./chunk-OS5TMEKH.js";
11
11
  import "./chunk-JEYGPPSL.js";
12
12
  import "./chunk-O7REVMOC.js";
13
13
  import "./chunk-QXFCZON7.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.5.0-alpha.27",
3
+ "version": "1.5.0-alpha.28",
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",