@hublo/sentinel 1.1.2 → 1.1.4

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.
@@ -16,7 +16,7 @@ import {
16
16
  registerAdapters,
17
17
  resolve,
18
18
  resolveBin
19
- } from "../chunk-B337JGTW.js";
19
+ } from "../chunk-HNE774F7.js";
20
20
 
21
21
  // bin/sentinel.ts
22
22
  import { program } from "commander";
@@ -348,7 +348,12 @@ var RULE_BLOCKS = [
348
348
  // The format role's equivalent. A rule parked and an option overridden are the same kind of
349
349
  // fact: this module deliberately does not do what the standard says, and someone should be
350
350
  // able to see every such decision in one place, with the reason beside it.
351
- { key: "overrides", label: "formatted differently from the repo standard:" }
351
+ { key: "overrides", label: "formatted differently from the repo standard:" },
352
+ // And the one a default can create rather than a module. `sortImports` ships off, so an
353
+ // adopted module can be missing a check nobody in it ever chose to drop. Reduced coverage
354
+ // has to be readable, or the difference between an accepted cost and a regression is only
355
+ // that nobody looked.
356
+ { key: "unenforced", label: "not checked at all:" }
352
357
  ];
353
358
  var MAX_INLINE_WIDTH = 60;
354
359
  function renderValue(key, value, p) {
@@ -627,25 +627,39 @@ var base_default = {
627
627
  singleQuote: true,
628
628
  trailingComma: "all",
629
629
  sortPackageJson: false,
630
- sortImports: {
631
- groups: ["builtin", "external", "internal", ["parent", "index"], "sibling"],
632
- newlinesBetween: true,
633
- order: "asc",
634
- ignoreCase: false
635
- }
630
+ sortImports: false
631
+ };
632
+
633
+ // src/roles/format/presets/nest-imports.json
634
+ var nest_imports_default = {
635
+ customGroups: [
636
+ {
637
+ groupName: "workspace",
638
+ elementNamePattern: ["@hublo/**", "@shared/**", "@front/**", "@network/**"]
639
+ }
640
+ ],
641
+ groups: ["builtin", "external", "workspace", ["parent", "index"], "sibling"],
642
+ newlinesBetween: true,
643
+ order: "asc",
644
+ ignoreCase: false
636
645
  };
637
646
 
638
647
  // src/roles/format/preset-data.ts
639
- var FORMAT_PRESETS = ["base", "svelte"];
648
+ var FORMAT_PRESETS = ["base", "svelte", "nest"];
640
649
  function hasFormatPreset(name) {
641
650
  return FORMAT_PRESETS.includes(name);
642
651
  }
643
652
  function formatPresetName(declared) {
644
- return declared === "svelte" ? "svelte" : "base";
653
+ if (declared === "svelte") return "svelte";
654
+ if (declared === "nest") return "nest";
655
+ return "base";
645
656
  }
646
657
  function formatPresetFor(preset) {
647
658
  const base = base_default;
648
- return formatPresetName(preset) === "svelte" ? { ...base, svelte: true } : base;
659
+ const name = formatPresetName(preset);
660
+ if (name === "svelte") return { ...base, svelte: true };
661
+ if (name === "nest") return { ...base, sortImports: nest_imports_default };
662
+ return base;
649
663
  }
650
664
 
651
665
  // src/roles/format/prettier-config.ts
@@ -775,6 +789,7 @@ var NOT_ADOPTED = (configFile, unreadable = null) => ({
775
789
  drift: [],
776
790
  local: {},
777
791
  presetVersion: null,
792
+ effective: {},
778
793
  unreadable
779
794
  });
780
795
  function sameValue(a, b) {
@@ -823,11 +838,26 @@ function readFormatAdoption(cwd) {
823
838
  drift,
824
839
  local,
825
840
  presetVersion: typeof provenance.version === "string" ? provenance.version : null,
841
+ effective: parsed,
826
842
  unreadable: null
827
843
  };
828
844
  }
829
845
 
846
+ // src/roles/format/unenforced.ts
847
+ function unenforcedFormatOptions(options) {
848
+ const gaps = [];
849
+ const sortImports = options["sortImports"];
850
+ if (sortImports === void 0 || sortImports === false) {
851
+ gaps.push({
852
+ rule: "sortImports",
853
+ reason: 'import order is not checked here. Enable it with `"sortImports": true` in .oxfmtrc.json, and run `pnpm run format:fix` as its own commit: reordering was 89-96% of the adoption diff on the modules measured'
854
+ });
855
+ }
856
+ return gaps;
857
+ }
858
+
830
859
  // src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
860
+ var FORMATTER_DIFFERENCES = "note: oxfmt is a reimplementation of Prettier, so a few files change even though the settings are identical (it de-indents some multi-line conditions, and drops quotes around enum members). Prettier considered those files correctly formatted; both are valid, the two tools simply disagree. Expect a small tail of such files in the reformat commit.";
831
861
  var DEFAULT_PRESET = "base";
832
862
  var OXFMT_VALUE_FLAGS = ["-c", "--config", "--ignore-path", "--threads", "--stdin-filepath"];
833
863
  var OxfmtAdapter = class extends BaseAdapter {
@@ -937,6 +967,8 @@ var OxfmtAdapter = class extends BaseAdapter {
937
967
  const oxfmt = resolveOxfmt(ctx.cwd);
938
968
  if (!oxfmt) return;
939
969
  const pass = spawnSync(oxfmt, ["--write", "."], { cwd: ctx.cwd, encoding: "utf8" });
970
+ process.stderr.write(` ${palette(process.stderr).dim(FORMATTER_DIFFERENCES)}
971
+ `);
940
972
  if (pass.status === 0) return;
941
973
  const output = `${pass.stdout ?? ""}${pass.stderr ?? ""}`;
942
974
  const unparseable = unparseableFiles(output);
@@ -1040,7 +1072,15 @@ var OxfmtAdapter = class extends BaseAdapter {
1040
1072
  })),
1041
1073
  // Which files this module does not format at all. A long list here is worth a look:
1042
1074
  // it is the quiet way a module stops being covered.
1043
- ignored: Array.isArray(adoption.local.ignorePatterns) ? adoption.local.ignorePatterns.length : 0
1075
+ ignored: Array.isArray(adoption.local.ignorePatterns) ? adoption.local.ignorePatterns.length : 0,
1076
+ // And which CHECKS it does not perform. `overrides` above answers "what is different
1077
+ // here"; this answers "what is not being checked at all", which is the question a gap
1078
+ // has to be readable as. Reported for adopted modules only: an unadopted module is not
1079
+ // missing a check, it has not started.
1080
+ // From `adoption.effective`, NOT the merged `options` above. This answers "what will
1081
+ // oxfmt actually do", and oxfmt reads the file. An undeclared override is still drift
1082
+ // — that is `drift`'s job — but it is not also a missing check.
1083
+ ...adoption.adopted ? { unenforced: unenforcedFormatOptions(adoption.effective) } : {}
1044
1084
  };
1045
1085
  }
1046
1086
  /**
@@ -3870,20 +3910,52 @@ var OxlintAdapter = class extends BaseAdapter {
3870
3910
  };
3871
3911
  }
3872
3912
  /** How many rules the extended preset enforces, read from the preset on disk. */
3913
+ /**
3914
+ * How many rules this module actually enforces.
3915
+ *
3916
+ * Counted from the PRESET it extends, not from its own file: the stub is one line by
3917
+ * design, so counting there would always answer zero and read as "this module lints
3918
+ * nothing".
3919
+ *
3920
+ * Two things this got wrong, both worth stating because both produced the same dangerous
3921
+ * output — a module reporting `rules=0` while `conformant=true`, which the migration page
3922
+ * counts as adopted and checking an empty set:
3923
+ *
3924
+ * - it used `JSON.parse`. The chain is JSONC: the per-module baseline carries a comment
3925
+ * per held rule explaining why. One comment anywhere in the chain threw, the catch
3926
+ * returned 0, and host-admin reported 188 rules as 0 the moment it gained a baseline.
3927
+ * - one unreadable entry zeroed the WHOLE count. An entry that cannot be read is a gap in
3928
+ * the count, not proof the module enforces nothing, and the difference matters most
3929
+ * exactly when something is broken.
3930
+ *
3931
+ * Counted as a SET of rule names, so the baseline re-stating a preset rule at `warn` is
3932
+ * the same rule, not a second one.
3933
+ */
3873
3934
  presetRuleCount(cwd) {
3874
3935
  const target = this.unresolvedPreset(cwd);
3875
3936
  if (target) return 0;
3876
- let total = 0;
3937
+ let stub;
3877
3938
  try {
3878
- const stub = JSON.parse(readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"));
3879
- for (const entry of stub.extends ?? []) {
3880
- const preset = JSON.parse(readFileSync14(join18(cwd, entry), "utf8"));
3881
- total += Object.keys(preset.rules ?? {}).length;
3882
- }
3939
+ stub = parseJsonc(
3940
+ readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"),
3941
+ LINT_CONFIG_FILE
3942
+ );
3883
3943
  } catch {
3884
3944
  return 0;
3885
3945
  }
3886
- return total;
3946
+ const names = /* @__PURE__ */ new Set();
3947
+ for (const entry of stub.extends ?? []) {
3948
+ try {
3949
+ const preset = parseJsonc(
3950
+ readFileSync14(join18(cwd, entry), "utf8"),
3951
+ entry
3952
+ );
3953
+ for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
3954
+ } catch {
3955
+ continue;
3956
+ }
3957
+ }
3958
+ return names.size;
3887
3959
  }
3888
3960
  /** Lint health for `--report`. Deepened in a later step; today it is the run outcome. */
3889
3961
  async report(ctx) {
@@ -3944,7 +4016,10 @@ var OxlintAdapter = class extends BaseAdapter {
3944
4016
  unresolvedPreset(cwd) {
3945
4017
  let parsed;
3946
4018
  try {
3947
- parsed = JSON.parse(readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"));
4019
+ parsed = parseJsonc(
4020
+ readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"),
4021
+ LINT_CONFIG_FILE
4022
+ );
3948
4023
  } catch {
3949
4024
  return void 0;
3950
4025
  }
@@ -5055,4 +5130,4 @@ export {
5055
5130
  detectFramework,
5056
5131
  dispatch
5057
5132
  };
5058
- //# sourceMappingURL=chunk-B337JGTW.js.map
5133
+ //# sourceMappingURL=chunk-HNE774F7.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-B337JGTW.js";
10
+ } from "./chunk-HNE774F7.js";
11
11
  export {
12
12
  BaseAdapter,
13
13
  all,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
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",