@jterrazz/typescript 10.2.1 → 10.2.2

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.
@@ -625,6 +625,7 @@ run_checks() {
625
625
  local checker_status=0
626
626
  local checker_dormant=0
627
627
  local checker_ratcheted=false
628
+ local checker_ratchet_broken=false
628
629
  if [ "$FIX_MODE" = false ]; then
629
630
  local checker_index=0
630
631
  while IFS= read -r specs_root; do
@@ -708,12 +709,32 @@ run_checks() {
708
709
  if member_checker .; then
709
710
  "${CHECKER_COMMAND[@]}" --format json > "$tmp_dir/checker.json" 2>/dev/null || true
710
711
  baseline_checker=(--checker "$tmp_dir/checker.json")
711
- [ -f "$BASELINE_FILE" ] && checker_ratcheted=true
712
+ # Zeroing the pass below on the checker's say-so is only honest once
713
+ # the file actually HOLDS its findings — a baseline with none yet is
714
+ # not enrolled, and its debt is not the ratchet's to forgive.
715
+ if [ -f "$BASELINE_FILE" ] && grep -q '"jterrazz-check/' "$BASELINE_FILE"; then
716
+ checker_ratcheted=true
717
+ fi
712
718
  fi
713
719
 
714
- node "$PACKAGE_ROOT/lib/check-baseline.js" "$tmp_dir/lint.json" . \
715
- "${baseline_checker[@]}" >> "$tmp_dir/lint.log" 2>&1
716
- lint_status=$?
720
+ # In fix mode there is no Test Conventions pass to carry a checker
721
+ # breach (the checker itself never runs there), so both reporters
722
+ # still speak through the one verdict this pass already owns. In
723
+ # check mode each reporter's breach is attributed to the pass that
724
+ # judges it: the script prints oxlint's refusals on stdout and the
725
+ # checker's — the enrolment notice among them — on stderr.
726
+ if [ "$FIX_MODE" = true ]; then
727
+ node "$PACKAGE_ROOT/lib/check-baseline.js" "$tmp_dir/lint.json" . \
728
+ "${baseline_checker[@]}" >> "$tmp_dir/lint.log" 2>&1
729
+ lint_status=$?
730
+ else
731
+ node "$PACKAGE_ROOT/lib/check-baseline.js" "$tmp_dir/lint.json" . \
732
+ "${baseline_checker[@]}" > "$tmp_dir/ratchet-oxlint.log" \
733
+ 2> "$tmp_dir/ratchet-checker.log"
734
+ [ -s "$tmp_dir/ratchet-oxlint.log" ] && lint_status=1 || lint_status=0
735
+ cat "$tmp_dir/ratchet-oxlint.log" >> "$tmp_dir/lint.log"
736
+ [ -s "$tmp_dir/ratchet-checker.log" ] && checker_ratchet_broken=true
737
+ fi
717
738
  fi
718
739
 
719
740
  [ -n "$format_pid" ] && { wait $format_pid; format_status=$?; }
@@ -748,6 +769,13 @@ run_checks() {
748
769
  checker_status=0
749
770
  fi
750
771
 
772
+ # But the ratchet still has the last word: a checker id that grew, that
773
+ # nobody recorded, or that only now enrols, is this pass's debt to name —
774
+ # never the oxlint pass's, whichever way the loop above just left it.
775
+ if [ "$checker_ratchet_broken" = true ]; then
776
+ checker_status=1
777
+ fi
778
+
751
779
  local publish_failed_logs=()
752
780
  index=0
753
781
  for pid in "${publish_pids[@]}"; do
@@ -777,6 +805,14 @@ run_checks() {
777
805
  join_logs "$tmp_dir/docs.log" "${docs_failed_logs[@]}"
778
806
  join_logs "$tmp_dir/publish.log" "${publish_failed_logs[@]}"
779
807
 
808
+ # The checker's own ratchet breach — a grown id, one nobody recorded, or
809
+ # the enrolment notice — is this pass's log to carry, never the oxlint
810
+ # pass's; `join_logs` above rebuilds the file from the raw runs alone, so
811
+ # it is appended after, not folded into the block that just ran.
812
+ if [ "$checker_ratchet_broken" = true ] && [ -s "$tmp_dir/ratchet-checker.log" ]; then
813
+ cat "$tmp_dir/ratchet-checker.log" >> "$tmp_dir/checker.log"
814
+ fi
815
+
780
816
  # Two things this pass says even when it is green, so it asks for its log
781
817
  # the way a writer does: which findings the ratchet is holding, and which
782
818
  # member resolves a release too old to answer `--member` — a reader left
@@ -33,6 +33,18 @@
33
33
  * rather than read as debts that reached zero: nothing measured them, and the
34
34
  * third refusal below would otherwise delete a file the next install needs.
35
35
  *
36
+ * A checker report given against a file that holds NO `jterrazz-check/*` entry
37
+ * yet is a second reporter joining an oxlint-only file — the upgrade, not new
38
+ * debt. The ratchet still fails it (a green gate that lies is worse), but
39
+ * names the gesture in ONE line rather than refusing every finding by rule:
40
+ * the per-rule refusals return once `typescript baseline` has recorded at
41
+ * least one `jterrazz-check/*` entry. The oxlint side is judged exactly as
42
+ * before either way.
43
+ *
44
+ * The two reporters' breaches print on two streams, so each is attributed to
45
+ * the pass that judges it: oxlint's on stdout, the checker's — the per-rule
46
+ * refusals and the one-line upgrade notice alike — on stderr.
47
+ *
36
48
  * Usage: node check-baseline.js <oxlint-json> [root] [--checker <json>] [--write]
37
49
  *
38
50
  * `--write` rewrites the file from the current counts — that is `typescript
@@ -42,7 +54,7 @@
42
54
 
43
55
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
44
56
  import { join, resolve } from 'node:path';
45
- import { argv, exit, stdout } from 'node:process';
57
+ import { argv, exit, stderr, stdout } from 'node:process';
46
58
 
47
59
  /** The file, at the project root, tracked beside the config it ratchets. */
48
60
  export const BASELINE = 'oxlint.baseline.json';
@@ -50,6 +62,11 @@ export const BASELINE = 'oxlint.baseline.json';
50
62
  /** The namespace the conventions checker's ids take in the file. */
51
63
  const CHECKER_NAMESPACE = 'jterrazz-check/';
52
64
 
65
+ /** A judge() line names its rule first, so the namespace prefix is enough. */
66
+ function isCheckerReason(reason) {
67
+ return reason.startsWith(CHECKER_NAMESPACE);
68
+ }
69
+
53
70
  /** `eslint(no-debugger)` is how oxlint's JSON spells `eslint/no-debugger`. */
54
71
  function ruleOf(code) {
55
72
  const match = /^(?<plugin>[\w-]+)\((?<rule>[^)]+)\)$/u.exec(code ?? '');
@@ -208,14 +225,54 @@ if (import.meta.main) {
208
225
  exit(rules.length > 0 ? 1 : 0);
209
226
  }
210
227
 
211
- const broken = judge(counts, baseline);
228
+ /*
229
+ * A checker report against a file the checker never entered yet is an
230
+ * upgrade, not a debt list: refusing it rule by rule would print, on a
231
+ * first install, exactly as many refusals as the checker has findings —
232
+ * every one of them the same gesture ('typescript baseline') said once.
233
+ */
234
+ const enrolling =
235
+ checkerPath !== undefined &&
236
+ !Object.keys(baseline).some((rule) => rule.startsWith(CHECKER_NAMESPACE));
237
+
238
+ let oxlintBroken;
239
+ let checkerBroken;
240
+ let enrolNotice;
212
241
 
213
- for (const reason of broken) {
242
+ if (enrolling) {
243
+ const findings = diagnosticsOf(checkerReport).length;
244
+ oxlintBroken = judge(countsOf(report), baseline);
245
+ checkerBroken = [];
246
+ enrolNotice =
247
+ findings > 0
248
+ ? `${BASELINE} records one reporter — run 'typescript baseline' once to enrol @jterrazz/test's ${findings} finding(s)`
249
+ : undefined;
250
+ } else {
251
+ const broken = judge(counts, baseline);
252
+ oxlintBroken = broken.filter((reason) => !isCheckerReason(reason));
253
+ checkerBroken = broken.filter((reason) => isCheckerReason(reason));
254
+ enrolNotice = undefined;
255
+ }
256
+
257
+ for (const reason of oxlintBroken) {
214
258
  stdout.write(`baseline-ratchet ${BASELINE} ${reason}\n`);
215
259
  }
216
- if (broken.length > 0) {
260
+ if (oxlintBroken.length > 0) {
217
261
  stdout.write("Run 'typescript baseline' to record where the project actually stands.\n");
218
262
  }
219
263
 
220
- exit(broken.length > 0 ? 1 : 0);
264
+ for (const reason of checkerBroken) {
265
+ stderr.write(`baseline-ratchet ${BASELINE} ${reason}\n`);
266
+ }
267
+ if (checkerBroken.length > 0) {
268
+ stderr.write("Run 'typescript baseline' to record where the project actually stands.\n");
269
+ }
270
+ if (enrolNotice !== undefined) {
271
+ stderr.write(`${enrolNotice}\n`);
272
+ }
273
+
274
+ const brokenTotal =
275
+ oxlintBroken.length + checkerBroken.length + (enrolNotice === undefined ? 0 : 1);
276
+
277
+ exit(brokenTotal > 0 ? 1 : 0);
221
278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jterrazz/typescript",
3
- "version": "10.2.1",
3
+ "version": "10.2.2",
4
4
  "license": "MIT",
5
5
  "author": "Jean-Baptiste Terrazzoni <contact@jterrazz.com>",
6
6
  "repository": {
@@ -84,7 +84,7 @@
84
84
  "typescript": "^6.0.0"
85
85
  },
86
86
  "devDependencies": {
87
- "@jterrazz/test": "^15.0.0",
87
+ "@jterrazz/test": "^15.3.0",
88
88
  "@types/node": "^26.1.1",
89
89
  "vitest": "^4.1.10"
90
90
  },