@hublo/sentinel 1.2.0 → 1.3.0

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.
@@ -9,6 +9,7 @@ import {
9
9
  WORKSPACE_ROOT_MARKER,
10
10
  availableTargets,
11
11
  buildConfigFile,
12
+ correctingSignal,
12
13
  declaredPresetFor,
13
14
  describeFramework,
14
15
  dispatch,
@@ -22,7 +23,7 @@ import {
22
23
  registerAdapters,
23
24
  resolve,
24
25
  resolveBin
25
- } from "../chunk-Z5I6SEKF.js";
26
+ } from "../chunk-676GBPMS.js";
26
27
 
27
28
  // bin/sentinel.ts
28
29
  import { program } from "commander";
@@ -235,6 +236,7 @@ async function analyse(params) {
235
236
  ) + "\n"
236
237
  );
237
238
  }
239
+ preset2 = correctingSignal(preset2, module.root)?.preset ?? preset2;
238
240
  }
239
241
  for (const target of params.targets) {
240
242
  let adapter;
@@ -655,7 +657,10 @@ program.name("sentinel").description("One CLI that guards code health: presets,
655
657
  " sentinel --run --json # from root \u2192 all types, machine output",
656
658
  " sentinel --run --ci # from root \u2192 affected only",
657
659
  " sentinel --inspect --typescript # from root \u2192 adoption + what is deferred",
658
- " sentinel --init --preset react # adopt EVERY role: lint, format, typescript",
660
+ // Deliberately not enumerated here. A hand-written list drifts, and this one did: it
661
+ // still said "lint, format, typescript" after the build role shipped, so the help told
662
+ // adopters the build was not covered when it was. "Available now", below, is derived.
663
+ " sentinel --init --preset react # adopt every available role (see below)",
659
664
  " sentinel --init --typescript --preset react # set up the current module + workspace",
660
665
  " sentinel --run --typescript -- --noImplicitAny # ask the tool a question of your own"
661
666
  ].join("\n")
@@ -1503,6 +1503,7 @@ var NATIVE_TS_ALIAS = "@typescript/native";
1503
1503
  var WORKSPACE_YAML = "pnpm-workspace.yaml";
1504
1504
  var RELEASE_AGE_KEY = "minimumReleaseAgeExclude";
1505
1505
  var LIST_ITEM = /^(\s*)-\s+(.*?)\s*$/;
1506
+ var MAP_ENTRY = /^(\s+)(.*?):\s+(.*?)\s*$/;
1506
1507
  function findWorkspaceRoot(startDir) {
1507
1508
  let dir = startDir;
1508
1509
  for (; ; ) {
@@ -1524,14 +1525,50 @@ function ensureI18nextSingleton(root, dryRun) {
1524
1525
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
1525
1526
  const want = declaredNativeTs(pkg);
1526
1527
  if (!want) return void 0;
1527
- const have = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
1528
- if (have === want) return void 0;
1529
- if (dryRun) return `would pin ${OVERRIDE_KEY} to ${want} (i18next singleton)`;
1530
- pkg.pnpm ??= {};
1531
- pkg.pnpm.overrides ??= {};
1532
- pkg.pnpm.overrides[OVERRIDE_KEY] = want;
1533
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1534
- return `pinned ${OVERRIDE_KEY} to ${want} in package.json (i18next singleton)`;
1528
+ const yamlPath = join12(root, WORKSPACE_YAML);
1529
+ if (!existsSync12(yamlPath)) return void 0;
1530
+ const lines = readFileSync10(yamlPath, "utf8").split("\n");
1531
+ const entry = findOverrideEntry(lines);
1532
+ if (entry.valueIdx !== void 0 && entry.value === want) return void 0;
1533
+ if (dryRun) return `would pin ${OVERRIDE_KEY} to ${want} in ${WORKSPACE_YAML} (i18next singleton)`;
1534
+ applyOverride(lines, entry, want);
1535
+ writeFileSync(yamlPath, lines.join("\n"));
1536
+ return `pinned ${OVERRIDE_KEY} to ${want} in ${WORKSPACE_YAML} (i18next singleton)`;
1537
+ }
1538
+ function findOverrideEntry(lines) {
1539
+ const blockIdx = lines.findIndex((line) => line.replace(/\s+$/, "") === "overrides:");
1540
+ if (blockIdx === -1) return { indent: " " };
1541
+ let lastEntryIdx = blockIdx;
1542
+ let indent = " ";
1543
+ for (let i = blockIdx + 1; i < lines.length; i++) {
1544
+ const match = lines[i]?.match(MAP_ENTRY);
1545
+ if (!match) break;
1546
+ indent = match[1] ?? indent;
1547
+ lastEntryIdx = i;
1548
+ if ((match[2] ?? "").replace(/^['"]|['"]$/g, "") === OVERRIDE_KEY) {
1549
+ return {
1550
+ valueIdx: i,
1551
+ value: (match[3] ?? "").replace(/^['"]|['"]$/g, ""),
1552
+ blockIdx,
1553
+ lastEntryIdx,
1554
+ indent
1555
+ };
1556
+ }
1557
+ }
1558
+ return { blockIdx, lastEntryIdx, indent };
1559
+ }
1560
+ function applyOverride(lines, entry, want) {
1561
+ const line = `${entry.indent}'${OVERRIDE_KEY}': ${want}`;
1562
+ if (entry.valueIdx !== void 0) {
1563
+ lines[entry.valueIdx] = line;
1564
+ return;
1565
+ }
1566
+ if (entry.lastEntryIdx !== void 0) {
1567
+ lines.splice(entry.lastEntryIdx + 1, 0, line);
1568
+ return;
1569
+ }
1570
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
1571
+ lines.push("overrides:", line, "");
1535
1572
  }
1536
1573
  function ensureReleaseAgeAllowList(root, dryRun) {
1537
1574
  const yamlPath = join12(root, WORKSPACE_YAML);
@@ -1620,18 +1657,25 @@ function inspectWorkspacePrep(root) {
1620
1657
  pkg = {};
1621
1658
  }
1622
1659
  }
1623
- const pinned = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
1660
+ const yamlPath = join12(root, WORKSPACE_YAML);
1661
+ const yaml = existsSync12(yamlPath) ? readFileSync10(yamlPath, "utf8") : void 0;
1662
+ const pinned = yaml === void 0 ? void 0 : findOverrideEntry(yaml.split("\n")).value;
1624
1663
  if (pinned !== void 0) {
1625
1664
  const stillForks = declaredNativeTs(pkg);
1626
1665
  entries.push({
1627
- rule: `package.json \u2192 pnpm.overrides.${OVERRIDE_KEY}`,
1666
+ rule: `${WORKSPACE_YAML} \u2192 overrides.${OVERRIDE_KEY}`,
1628
1667
  reason: stillForks === void 0 ? `pinned to ${pinned}, but the root no longer declares ${NATIVE_TS_ALIAS}, so nothing forks any more. This override is now dead config and can be removed.` : `pinned to ${pinned}, matching the second TypeScript the root declares (${NATIVE_TS_ALIAS}). It re-resolves i18next for EVERY module that depends on it, not only adopted ones. It retires with the second TypeScript.`
1629
1668
  });
1630
1669
  }
1670
+ const legacy = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
1671
+ if (legacy !== void 0) {
1672
+ entries.push({
1673
+ rule: `package.json \u2192 pnpm.overrides.${OVERRIDE_KEY}`,
1674
+ reason: `pinned to ${legacy}, but pnpm 11 no longer reads the \`pnpm\` field of package.json and says nothing about it, so this override is not applied. Move it to ${WORKSPACE_YAML} under \`overrides:\`, which pnpm 10 and 11 both read.`
1675
+ });
1676
+ }
1631
1677
  const own = readOwnPackage().name;
1632
- const yamlPath = join12(root, WORKSPACE_YAML);
1633
- if (existsSync12(yamlPath)) {
1634
- const yaml = readFileSync10(yamlPath, "utf8");
1678
+ if (yaml !== void 0) {
1635
1679
  const listed = new RegExp(`^\\s*-\\s*['"]?${own.replace("/", "\\/")}['"]?\\s*$`, "m").test(yaml);
1636
1680
  if (listed) {
1637
1681
  const gated = new RegExp(`^\\s*minimumReleaseAge\\s*:`, "m").test(yaml);
@@ -1951,6 +1995,7 @@ function toModulePattern(pattern) {
1951
1995
  const trimmed = pattern.trim();
1952
1996
  if (trimmed.startsWith("!")) return void 0;
1953
1997
  if (trimmed.startsWith("/**/")) return trimmed.slice(1);
1998
+ if (trimmed.startsWith("**/")) return trimmed;
1954
1999
  if (trimmed.startsWith("/")) return void 0;
1955
2000
  return trimmed.replace(/\/$/, "").includes("/") ? void 0 : trimmed;
1956
2001
  }
@@ -6475,7 +6520,11 @@ var PATH_SIGNALS = [
6475
6520
  }
6476
6521
  ];
6477
6522
  var DEPENDENCY_SIGNALS = [
6478
- { preset: "react", pattern: /^(react|react-dom|@types\/react)$|^eslint-plugin-react/ },
6523
+ // Runtime dependencies only. `eslint-plugin-react*` used to count here, but a lint plugin is
6524
+ // TOOLING: this very repo depends on `eslint-plugin-react-refresh` to ship React rules to
6525
+ // other projects, and was therefore read as a React app itself. No module in the monorepo
6526
+ // carries an `eslint-plugin-react*` without also carrying react, so nothing loses detection.
6527
+ { preset: "react", pattern: /^(react|react-dom|@types\/react)$/ },
6479
6528
  { preset: "svelte", pattern: /^svelte$|^@sveltejs\// },
6480
6529
  { preset: "nest", pattern: /^@nestjs\// }
6481
6530
  ];
@@ -6534,6 +6583,10 @@ function presetSignals(cwd) {
6534
6583
  }
6535
6584
  return signals;
6536
6585
  }
6586
+ function correctingSignal(detected, cwd) {
6587
+ const [signal] = presetSignals(cwd);
6588
+ return signal !== void 0 && signal.preset !== detected ? signal : void 0;
6589
+ }
6537
6590
  function presetContradiction(declared, cwd) {
6538
6591
  const signals = presetSignals(cwd);
6539
6592
  if (signals.length === 0) return void 0;
@@ -6614,8 +6667,8 @@ async function dispatch(opts) {
6614
6667
  }
6615
6668
  let effective = preset;
6616
6669
  if (opts.preset === void 0 && adapter.declaredPreset?.(opts.cwd) === void 0) {
6617
- const [signal] = presetSignals(opts.cwd);
6618
- if (signal !== void 0 && signal.preset !== preset) {
6670
+ const signal = correctingSignal(preset, opts.cwd);
6671
+ if (signal !== void 0) {
6619
6672
  effective = signal.preset;
6620
6673
  process.stderr.write(
6621
6674
  ` detected "${preset}" from dependencies, but ${signal.evidence}, so adopting as "${signal.preset}". Pass --preset to override.
@@ -6683,8 +6736,9 @@ export {
6683
6736
  PRESET_NAMES,
6684
6737
  palette,
6685
6738
  registerAdapters,
6739
+ correctingSignal,
6686
6740
  describeFramework,
6687
6741
  detectFramework,
6688
6742
  dispatch
6689
6743
  };
6690
- //# sourceMappingURL=chunk-Z5I6SEKF.js.map
6744
+ //# sourceMappingURL=chunk-676GBPMS.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-Z5I6SEKF.js";
10
+ } from "./chunk-676GBPMS.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.2.0",
3
+ "version": "1.3.0",
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",