@hublo/sentinel 1.1.5 → 1.2.0-alpha.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.
package/README.md CHANGED
@@ -43,8 +43,8 @@ A large monorepo accumulates:
43
43
 
44
44
  sentinel writes **standard config files** into a project (each just `extends` a sentinel preset) and runs the checks. Your editor and the tools read those **normal files natively**, they never call sentinel at runtime, so nothing is coupled to it or brittle.
45
45
 
46
- > **Shipped today:** the **TypeScript**, **lint** and **format** tools. The `--test` / `--build`
47
- > snippets below illustrate the end state and land with their own ticket.
46
+ > **Shipped today:** the **TypeScript**, **lint**, **format**, **build** and **dev** tools. The
47
+ > `--test` snippets below illustrate the end state and land with their own ticket.
48
48
 
49
49
  **Step 1 — put a module on sentinel** (once per module, by a dev; the files are committed). Run from the app dir; `--init` does it all, nothing is hand-edited:
50
50
 
@@ -151,6 +151,7 @@ pnpm dlx @hublo/sentinel@<exact-version> --inspect --typescript --module <name>
151
151
  - [`docs/using-sentinel.md`](docs/using-sentinel.md) — every verb for every role, scoping a run to some files, what a fully adopted module looks like, and how versions move between modules.
152
152
  - [`docs/lint-adoption.md`](docs/lint-adoption.md) — migrating a module from ESLint to Oxlint: the two steps, what `--init` writes and removes, the two react tiers, and the failures worth recognising.
153
153
  - [`docs/format-adoption.md`](docs/format-adoption.md) — migrating a module from Prettier to oxfmt: why this config is materialized rather than a stub, how a module keeps its own formatting, and what did not survive the move.
154
+ - [`docs/build-adoption.md`](docs/build-adoption.md) — moving a React app's Vite toolchain into sentinel: what the config becomes, what sentinel owns versus what stays the app's, why overriding merges instead of spreading, and why an existing config is not rewritten.
154
155
  - [`docs/typescript-adoption.md`](docs/typescript-adoption.md) — the adoption cheat sheet: the two adoption steps, the command model (verb x type x location), options, reading a report, and troubleshooting.
155
156
  - [`docs/typescript-traces.md`](docs/typescript-traces.md) — a **generated, versioned** reference of live command + output traces (every verb, option, config result and edge case) against the mock monorepo. Regenerate after CLI changes with `pnpm docs:traces`.
156
157
 
@@ -376,9 +377,9 @@ VERBS --run execute the target's tool (--json for metrics + diagnosti
376
377
  --report deprecated → --run --json
377
378
  --status deprecated → --inspect
378
379
 
379
- TYPES --lint --format --typescript --build --test
380
+ TYPES --lint --format --typescript --build --dev --test
380
381
  --static-analysis --runtime-analysis --arch
381
- (omit a type → ALL types; or --all)
382
+ (omit a type → every type that TERMINATES; --dev never sweeps)
382
383
 
383
384
  LOCATION in a MODULE dir → that module (do NOT pass --module)
384
385
  at the workspace ROOT → --module <name> (one) · --ci (affected) · else all
@@ -1,14 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRESET_NAMES,
4
+ PresetUnsupportedError,
5
+ SWEEPABLE_TARGETS,
4
6
  TARGETS,
5
7
  VERBS,
6
8
  WORKSPACE_ROOT_MARKER,
7
9
  availableTargets,
10
+ declaredPresetFor,
8
11
  describeFramework,
9
12
  dispatch,
10
13
  ensureWorkspacePrep,
11
14
  findWorkspaceRoot,
15
+ inspectWorkspacePrep,
12
16
  palette,
13
17
  readNxProjectName,
14
18
  readOwnVersion,
@@ -16,7 +20,8 @@ import {
16
20
  registerAdapters,
17
21
  resolve,
18
22
  resolveBin
19
- } from "../chunk-RLVLDC5H.js";
23
+ } from "../chunk-TWL6T237.js";
24
+ import "../chunk-XSCDJZDY.js";
20
25
 
21
26
  // bin/sentinel.ts
22
27
  import { program } from "commander";
@@ -144,6 +149,17 @@ async function runInit(ctx) {
144
149
  });
145
150
  worst = Math.max(worst, code);
146
151
  } catch (error) {
152
+ if (error instanceof PresetUnsupportedError) {
153
+ if (ctx.targetsExplicit) {
154
+ process.stderr.write(
155
+ `
156
+ sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it. Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").
157
+ `
158
+ );
159
+ worst = Math.max(worst, 1);
160
+ }
161
+ continue;
162
+ }
147
163
  process.stderr.write(`
148
164
  sentinel (${type}): ${asMessage(error)}
149
165
  `);
@@ -196,8 +212,9 @@ async function analyse(params) {
196
212
  }
197
213
  for (const target of params.targets) {
198
214
  let adapter;
215
+ const resolutionPreset = params.preset ?? declaredPresetFor(target, module.root) ?? preset2;
199
216
  try {
200
- adapter = resolve(target, preset2, params.runner);
217
+ adapter = resolve(target, resolutionPreset, params.runner);
201
218
  } catch (error) {
202
219
  if (params.targetsExplicit) {
203
220
  const reason = error instanceof Error ? error.message : String(error);
@@ -416,7 +433,7 @@ function statusCoverage(items) {
416
433
  };
417
434
  }
418
435
  function presetShort(preset2) {
419
- return typeof preset2 === "string" ? preset2.replace("@hublo/sentinel/tsconfig/", "") : "?";
436
+ return typeof preset2 === "string" ? preset2.replace(/@hublo\/sentinel\/(typescript|tsconfig)\//, "") : "?";
420
437
  }
421
438
  function renderStatusRow(item, p) {
422
439
  if (item.status === "unsupported") return renderUnsupportedRow(item, p);
@@ -438,6 +455,10 @@ function renderStatusSummary(items, p) {
438
455
  }
439
456
 
440
457
  // src/cli/run-verb.ts
458
+ function workspacePrep(cwd2) {
459
+ const root = findWorkspaceRoot(cwd2);
460
+ return root ? inspectWorkspacePrep(root) : [];
461
+ }
441
462
  async function runVerb(ctx) {
442
463
  const { modules, scope } = resolveContext(ctx.cwd, { module: ctx.module, ci: ctx.ci });
443
464
  const out = palette(process.stdout);
@@ -482,7 +503,15 @@ async function runVerb(ctx) {
482
503
  });
483
504
  const summary = generateSummaries(results, ctx.targets);
484
505
  if (ctx.json) {
485
- const base = ctx.verb === "status" || ctx.verb === "inspect" ? { ...summary, coverage: statusCoverage(summary.results) } : summary;
506
+ const base = ctx.verb === "status" || ctx.verb === "inspect" ? {
507
+ ...summary,
508
+ coverage: statusCoverage(summary.results),
509
+ // The root-level changes sentinel maintains, each with its justification checked
510
+ // NOW rather than described in a comment. They are the only thing sentinel does
511
+ // that nobody can see afterwards, and every one of them is temporary. See
512
+ // `inspectWorkspacePrep`.
513
+ ...ctx.verb === "inspect" ? { workspace: workspacePrep(ctx.cwd) } : {}
514
+ } : summary;
486
515
  const payload = ctx.toolArgs.length > 0 ? { ...base, toolArgs: ctx.toolArgs } : base;
487
516
  process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
488
517
  } else if (ctx.verb === "status" || ctx.verb === "inspect" && modules.length > 1) {
@@ -493,6 +522,19 @@ async function runVerb(ctx) {
493
522
  process.stdout.write(renderSummary(item, out) + "\n");
494
523
  }
495
524
  }
525
+ if (!ctx.json && ctx.verb === "inspect") {
526
+ const prep = workspacePrep(ctx.cwd);
527
+ if (prep.length > 0) {
528
+ process.stdout.write(`
529
+ sentinel maintains these at the workspace root:
530
+ `);
531
+ for (const entry of prep) {
532
+ process.stdout.write(` ${out.strong(entry.rule)}
533
+ ${entry.reason}
534
+ `);
535
+ }
536
+ }
537
+ }
496
538
  process.stderr.write(
497
539
  err.dim(` ${modules.length} module(s) [${scope}] in ${Date.now() - started}ms
498
540
  `)
@@ -624,7 +666,7 @@ if (opts.all && namedTargets.length > 0) {
624
666
  `--all runs every target; drop the specific one(s): ${namedTargets.map((t) => `--${t}`).join(", ")}.`
625
667
  );
626
668
  }
627
- var targets = opts.all || namedTargets.length === 0 ? [...TARGETS] : namedTargets;
669
+ var targets = opts.all || namedTargets.length === 0 ? [...SWEEPABLE_TARGETS] : namedTargets;
628
670
  var targetsExplicit = !(opts.all || namedTargets.length === 0);
629
671
  var maxDiagnostics = Number(opts.maxDiagnostics);
630
672
  if (!Number.isInteger(maxDiagnostics) || maxDiagnostics < 0) {