headlamp 0.1.6 → 0.1.9

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/dist/index.js CHANGED
@@ -52,6 +52,7 @@ var init_args = __esm({
52
52
  coverage: (coverageValue) => ({ type: "coverage", coverageValue }),
53
53
  coverageUi: (value) => ({ type: "coverageUi", value }),
54
54
  coverageAbortOnFailure: (value) => ({ type: "coverageAbortOnFailure", value }),
55
+ onlyFailures: (value) => ({ type: "onlyFailures", value }),
55
56
  jestArg: (value) => ({ type: "jestArg", value }),
56
57
  jestArgs: (values) => ({ type: "jestArgs", values }),
57
58
  vitestArg: (value) => ({ type: "vitestArg", value }),
@@ -67,7 +68,8 @@ var init_args = __esm({
67
68
  coverageMode: (value) => ({ type: "coverageMode", value }),
68
69
  coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
69
70
  coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
70
- coveragePageFit: (value) => ({ type: "coveragePageFit", value })
71
+ coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
72
+ changed: (value) => ({ type: "changed", value })
71
73
  };
72
74
  Some = (value) => ({ _tag: "some", value });
73
75
  None = { _tag: "none" };
@@ -204,6 +206,18 @@ var init_args = __esm({
204
206
  "--coverage.pageFit",
205
207
  (_flag, lookahead) => step([ActionBuilders.coveragePageFit(isTruthy(String(lookahead)))], true)
206
208
  ),
209
+ // --onlyFailures flag (boolean)
210
+ rule.eq("--onlyFailures", () => step([ActionBuilders.onlyFailures(true)])),
211
+ rule.startsWith(
212
+ "--onlyFailures=",
213
+ (value) => step([
214
+ ActionBuilders.onlyFailures(isTruthy((value.split("=")[1] ?? "").trim().toLowerCase()))
215
+ ])
216
+ ),
217
+ rule.withLookahead(
218
+ "--onlyFailures",
219
+ (_flag, lookahead) => step([ActionBuilders.onlyFailures(isTruthy(String(lookahead)))], true)
220
+ ),
207
221
  rule.withLookahead(
208
222
  "--testPathPattern",
209
223
  (flag, lookahead) => step([ActionBuilders.jestArgs([flag, lookahead])], true)
@@ -264,6 +278,18 @@ var init_args = __esm({
264
278
  "--coverage.root=",
265
279
  (value) => step([ActionBuilders.coverageRoot((value.split("=")[1] ?? "").trim())])
266
280
  ),
281
+ // --changed flag: selects changed files via git (all|staged|unstaged)
282
+ rule.eq("--changed", () => step([ActionBuilders.changed("all")])),
283
+ rule.startsWith("--changed=", (value) => {
284
+ const raw = (value.split("=")[1] ?? "").trim().toLowerCase();
285
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
286
+ return step([ActionBuilders.changed(mode)]);
287
+ }),
288
+ rule.withLookahead("--changed", (_flag, lookahead) => {
289
+ const raw = String(lookahead).trim().toLowerCase();
290
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
291
+ return step([ActionBuilders.changed(mode)], true);
292
+ }),
267
293
  rule.withLookahead(
268
294
  "-t",
269
295
  (flag, lookahead) => step(
@@ -338,6 +364,8 @@ var init_args = __esm({
338
364
  return { vitest: [], jest: [], coverage: false, coverageUi: action.value };
339
365
  case "coverageAbortOnFailure":
340
366
  return { vitest: [], jest: [], coverage: false, coverageAbortOnFailure: action.value };
367
+ case "onlyFailures":
368
+ return { vitest: [], jest: [], coverage: false, onlyFailures: action.value };
341
369
  case "jestArgs":
342
370
  return { vitest: [], jest: action.values, coverage: false };
343
371
  case "selectionHint":
@@ -366,6 +394,8 @@ var init_args = __esm({
366
394
  return { vitest: [], jest: [], coverage: false, coverageMaxHotspots: action.value };
367
395
  case "coveragePageFit":
368
396
  return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
397
+ case "changed":
398
+ return { vitest: [], jest: [], coverage: false, changed: action.value };
369
399
  case "jestArg":
370
400
  return { vitest: [], jest: [action.value], coverage: false };
371
401
  case "vitestArg":
@@ -405,7 +435,9 @@ var init_args = __esm({
405
435
  }
406
436
  return {
407
437
  ...next,
438
+ ...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
408
439
  ...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
440
+ ...right.onlyFailures !== void 0 || left.onlyFailures !== void 0 ? { onlyFailures: right.onlyFailures ?? left.onlyFailures } : {},
409
441
  ...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
410
442
  ...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
411
443
  ...right.coverageMode !== void 0 || left.coverageMode !== void 0 ? { coverageMode: right.coverageMode ?? left.coverageMode } : {},
@@ -429,6 +461,7 @@ var init_args = __esm({
429
461
  let collectCoverage = false;
430
462
  let coverageUi = "both";
431
463
  let coverageAbortOnFailure = false;
464
+ let onlyFailures = false;
432
465
  let coverageShowCode = Boolean(process.stdout.isTTY);
433
466
  let coverageMode = "auto";
434
467
  const coverageMaxFilesLocalInit = void 0;
@@ -446,6 +479,7 @@ var init_args = __esm({
446
479
  collectCoverage ||= contrib.coverage;
447
480
  coverageUi = contrib.coverageUi ?? coverageUi;
448
481
  coverageAbortOnFailure = contrib.coverageAbortOnFailure ?? coverageAbortOnFailure;
482
+ onlyFailures = contrib.onlyFailures ?? onlyFailures;
449
483
  coverageShowCode = contrib.coverageShowCode ?? coverageShowCode;
450
484
  const coverageDetailComputed = contrib.coverageDetail ?? (contrib.selection ? "auto" : void 0);
451
485
  coverageMode = contrib.coverageMode ?? (contrib.selection ? "compact" : "auto");
@@ -479,6 +513,7 @@ var init_args = __esm({
479
513
  collectCoverage,
480
514
  coverageUi,
481
515
  coverageAbortOnFailure,
516
+ onlyFailures,
482
517
  selectionSpecified: Boolean(contrib.selection),
483
518
  selectionPaths: [...contrib.selectionPaths ?? []],
484
519
  includeGlobs,
@@ -490,7 +525,8 @@ var init_args = __esm({
490
525
  ...coverageMaxHotspotsLocal !== void 0 ? { coverageMaxHotspots: coverageMaxHotspotsLocal } : {},
491
526
  coveragePageFit,
492
527
  ...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
493
- ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {}
528
+ ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
529
+ ...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
494
530
  };
495
531
  return out;
496
532
  };
@@ -557,7 +593,7 @@ var init_TimeoutError = __esm({
557
593
 
558
594
  // node_modules/es-toolkit/dist/promise/delay.mjs
559
595
  function delay(ms, { signal } = {}) {
560
- return new Promise((resolve8, reject) => {
596
+ return new Promise((resolve9, reject) => {
561
597
  const abortError = () => {
562
598
  reject(new AbortError());
563
599
  };
@@ -570,7 +606,7 @@ function delay(ms, { signal } = {}) {
570
606
  }
571
607
  const timeoutId = setTimeout(() => {
572
608
  signal?.removeEventListener("abort", abortHandler);
573
- resolve8();
609
+ resolve9();
574
610
  }, ms);
575
611
  signal?.addEventListener("abort", abortHandler, { once: true });
576
612
  });
@@ -635,11 +671,11 @@ var init_exec = __esm({
635
671
  child.stderr?.on("data", (chunk) => {
636
672
  stderr += String(chunk);
637
673
  });
638
- const exec = new Promise((resolve8, reject) => {
674
+ const exec = new Promise((resolve9, reject) => {
639
675
  child.on("error", reject);
640
676
  child.on(
641
677
  "close",
642
- (code) => Number(code) === 0 ? resolve8(stdout) : reject(new Error(stderr || `exit ${code}`))
678
+ (code) => Number(code) === 0 ? resolve9(stdout) : reject(new Error(stderr || `exit ${code}`))
643
679
  );
644
680
  });
645
681
  try {
@@ -659,7 +695,7 @@ var init_exec = __esm({
659
695
  throw caughtError;
660
696
  }
661
697
  };
662
- runExitCode = async (cmd, args, opts = {}) => new Promise((resolve8, reject) => {
698
+ runExitCode = async (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
663
699
  const child = spawn(cmd, [...args], {
664
700
  cwd: opts.cwd,
665
701
  env: opts.env,
@@ -668,9 +704,9 @@ var init_exec = __esm({
668
704
  windowsHide: true
669
705
  });
670
706
  child.on("error", reject);
671
- child.on("close", (code) => resolve8(Number(code)));
707
+ child.on("close", (code) => resolve9(Number(code)));
672
708
  });
673
- runWithCapture = async (cmd, args, opts) => new Promise((resolve8, reject) => {
709
+ runWithCapture = async (cmd, args, opts) => new Promise((resolve9, reject) => {
674
710
  const child = spawn(cmd, [...args], {
675
711
  cwd: opts.cwd,
676
712
  env: opts.env,
@@ -686,7 +722,7 @@ var init_exec = __esm({
686
722
  buf += String(chunk);
687
723
  });
688
724
  child.on("error", reject);
689
- child.on("close", (code) => resolve8({ code: Number(code), output: buf }));
725
+ child.on("close", (code) => resolve9({ code: Number(code), output: buf }));
690
726
  });
691
727
  }
692
728
  });
@@ -1097,8 +1133,8 @@ var require_utils = __commonJS({
1097
1133
  }
1098
1134
  return output;
1099
1135
  };
1100
- exports.basename = (path10, { windows } = {}) => {
1101
- const segs = path10.split(windows ? /[\\/]/ : "/");
1136
+ exports.basename = (path11, { windows } = {}) => {
1137
+ const segs = path11.split(windows ? /[\\/]/ : "/");
1102
1138
  const last = segs[segs.length - 1];
1103
1139
  if (last === "") {
1104
1140
  return segs[segs.length - 2];
@@ -4688,8 +4724,75 @@ var compositeBarPct = (summary, hotspots) => {
4688
4724
  // src/lib/coverage-print.ts
4689
4725
  init_env_utils();
4690
4726
  init_exec();
4691
- import * as path7 from "node:path";
4727
+ import * as path8 from "node:path";
4692
4728
  import * as fsSync2 from "node:fs";
4729
+
4730
+ // src/lib/relevance.ts
4731
+ init_args();
4732
+ init_fast_related();
4733
+ import * as path7 from "node:path";
4734
+ var normalizeAbs = (inputPath) => path7.resolve(inputPath).replace(/\\/g, "/");
4735
+ var compareBooleanDesc = (left, right) => {
4736
+ if (left === right) {
4737
+ return 0;
4738
+ }
4739
+ return right ? 1 : -1;
4740
+ };
4741
+ var compareNumberAsc = (left, right) => left - right;
4742
+ var compareStringAsc = (left, right) => left.localeCompare(right);
4743
+ var fileFailed = (file) => Boolean(
4744
+ (file.status ?? "") === "failed" || (file.testResults ?? []).some((assertion) => (assertion.status ?? "") === "failed")
4745
+ );
4746
+ var composeComparators = (...comparators) => (left, right) => {
4747
+ for (const cmp of comparators) {
4748
+ const result = cmp(left, right);
4749
+ if (result !== 0) {
4750
+ return result;
4751
+ }
4752
+ }
4753
+ return 0;
4754
+ };
4755
+ var comparatorForRank = (rankByPath) => {
4756
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4757
+ return composeComparators(
4758
+ (left, right) => compareBooleanDesc(fileFailed(left), fileFailed(right)),
4759
+ (left, right) => compareNumberAsc(
4760
+ rankOrInf(normalizeAbs(left.testFilePath)),
4761
+ rankOrInf(normalizeAbs(right.testFilePath))
4762
+ ),
4763
+ (left, right) => compareStringAsc(normalizeAbs(left.testFilePath), normalizeAbs(right.testFilePath))
4764
+ );
4765
+ };
4766
+ var computeDirectnessRank = async (opts) => {
4767
+ const selectionKey = opts.productionSeeds.map((abs) => path7.relative(opts.repoRoot, abs).replace(/\\/g, "/")).sort((left, right) => left.localeCompare(right)).join("|");
4768
+ const related = await cachedRelated({
4769
+ repoRoot: opts.repoRoot,
4770
+ selectionKey,
4771
+ compute: () => findRelatedTestsFast({
4772
+ repoRoot: opts.repoRoot,
4773
+ productionPaths: opts.productionSeeds,
4774
+ testGlobs: DEFAULT_TEST_GLOBS,
4775
+ excludeGlobs: opts.excludeGlobs ?? DEFAULT_EXCLUDE,
4776
+ timeoutMs: 1500
4777
+ })
4778
+ });
4779
+ const out = /* @__PURE__ */ new Map();
4780
+ related.forEach((abs, index) => {
4781
+ out.set(normalizeAbs(abs), index);
4782
+ });
4783
+ return out;
4784
+ };
4785
+ var sortTestResultsWithRank = (rankByPath, results) => results.slice().sort(comparatorForRank(rankByPath));
4786
+ var comparatorForPathRank = (rankByPath) => {
4787
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4788
+ return composeComparators(
4789
+ (left, right) => compareNumberAsc(rankOrInf(normalizeAbs(left)), rankOrInf(normalizeAbs(right))),
4790
+ (left, right) => compareStringAsc(normalizeAbs(left), normalizeAbs(right))
4791
+ );
4792
+ };
4793
+ var sortPathsWithRank = (rankByPath, paths) => paths.slice().sort(comparatorForPathRank(rankByPath));
4794
+
4795
+ // src/lib/coverage-print.ts
4693
4796
  var printDetailedCoverage = async (opts) => {
4694
4797
  const files = opts.map.files().sort((fileA, fileB) => {
4695
4798
  const summaryA = opts.map.fileCoverageFor(fileA).toSummary();
@@ -4699,7 +4802,7 @@ var printDetailedCoverage = async (opts) => {
4699
4802
  for (const abs of files) {
4700
4803
  const fc = opts.map.fileCoverageFor(abs);
4701
4804
  const sum = fc.toSummary();
4702
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4805
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4703
4806
  const blocks = computeUncoveredBlocks(fc);
4704
4807
  const misses = missedBranches(fc);
4705
4808
  const missFns = missedFunctions(fc);
@@ -4708,9 +4811,9 @@ var printDetailedCoverage = async (opts) => {
4708
4811
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4709
4812
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4710
4813
  linesPctText
4711
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4712
- sum.functions.pct
4713
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4814
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4815
+ funcsPctText
4816
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4714
4817
  console.info(header);
4715
4818
  const max = opts.limitPerFile === "all" ? Number.POSITIVE_INFINITY : opts.limitPerFile ?? 5;
4716
4819
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
@@ -4737,10 +4840,8 @@ var printDetailedCoverage = async (opts) => {
4737
4840
  abs,
4738
4841
  block.start,
4739
4842
  opts.editorCmd
4740
- )}\x07${path7.basename(abs)}:${block.start}\x1B]8;;\x07`;
4741
- const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(
4742
- `L${block.end}`
4743
- )} ${link}`;
4843
+ )}\x07${path8.basename(abs)}:${block.start}\x1B]8;;\x07`;
4844
+ const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(`L${block.end}`)} ${link}`;
4744
4845
  console.info(label);
4745
4846
  if (opts.showCode && src.length) {
4746
4847
  const lines = src.split(/\r?\n/);
@@ -4760,7 +4861,7 @@ var printDetailedCoverage = async (opts) => {
4760
4861
  abs,
4761
4862
  fn.line,
4762
4863
  opts.editorCmd
4763
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4864
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4764
4865
  console.info(` - ${fn.name} @ ${link}`);
4765
4866
  }
4766
4867
  }
@@ -4771,12 +4872,8 @@ var printDetailedCoverage = async (opts) => {
4771
4872
  abs,
4772
4873
  br.line,
4773
4874
  opts.editorCmd
4774
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`;
4775
- console.info(
4776
- ` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(
4777
- ", "
4778
- )}]`
4779
- );
4875
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`;
4876
+ console.info(` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(", ")}]`);
4780
4877
  }
4781
4878
  }
4782
4879
  console.info("");
@@ -4796,7 +4893,7 @@ var printCompactCoverage = async (opts) => {
4796
4893
  for (const abs of files.slice(0, fileCap)) {
4797
4894
  const fc = opts.map.fileCoverageFor(abs);
4798
4895
  const sum = fc.toSummary();
4799
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4896
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4800
4897
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
4801
4898
  const secondLength = secondRange.end - secondRange.start;
4802
4899
  const firstLength = firstRange.end - firstRange.start;
@@ -4810,9 +4907,9 @@ var printCompactCoverage = async (opts) => {
4810
4907
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4811
4908
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4812
4909
  linesPctText
4813
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4814
- sum.functions.pct
4815
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4910
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4911
+ funcsPctText
4912
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4816
4913
  console.info(header);
4817
4914
  const hotspots = blocks.slice(0, maxHotspotsDerived);
4818
4915
  if (hotspots.length) {
@@ -4823,10 +4920,8 @@ var printCompactCoverage = async (opts) => {
4823
4920
  abs,
4824
4921
  hotspot.start,
4825
4922
  opts.editorCmd
4826
- )}\x07${path7.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
4827
- console.info(
4828
- ` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`
4829
- );
4923
+ )}\x07${path8.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
4924
+ console.info(` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`);
4830
4925
  }
4831
4926
  }
4832
4927
  const functionsList = missFns.slice(0, maxFunctionsDerived);
@@ -4838,7 +4933,7 @@ var printCompactCoverage = async (opts) => {
4838
4933
  abs,
4839
4934
  fn.line,
4840
4935
  opts.editorCmd
4841
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`
4936
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`
4842
4937
  );
4843
4938
  }
4844
4939
  }
@@ -4853,7 +4948,7 @@ var printCompactCoverage = async (opts) => {
4853
4948
  abs,
4854
4949
  br.line,
4855
4950
  opts.editorCmd
4856
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`
4951
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`
4857
4952
  );
4858
4953
  }
4859
4954
  }
@@ -4862,9 +4957,7 @@ var printCompactCoverage = async (opts) => {
4862
4957
  const restBrs = Math.max(0, misses.length - branchesList.length);
4863
4958
  if (restHs + restFns + restBrs > 0) {
4864
4959
  console.info(
4865
- ansi.dim(
4866
- ` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`
4867
- )
4960
+ ansi.dim(` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`)
4868
4961
  );
4869
4962
  }
4870
4963
  console.info("");
@@ -4901,7 +4994,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4901
4994
  return { stem: base.slice(0, -ending.length), ext: ending };
4902
4995
  }
4903
4996
  }
4904
- const ext2 = path7.extname(base);
4997
+ const ext2 = path8.extname(base);
4905
4998
  return { stem: base.slice(0, -ext2.length), ext: ext2 };
4906
4999
  };
4907
5000
  const sliceBalanced = (input, width) => {
@@ -4984,12 +5077,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4984
5077
  const tailParts = tailSrc.map((segment) => segment);
4985
5078
  let hidAny = false;
4986
5079
  const build = () => {
4987
- const label2 = joinParts(
4988
- headParts,
4989
- tailParts,
4990
- hideMiddle2 || hidAny,
4991
- baseLabel
4992
- );
5080
+ const label2 = joinParts(headParts, tailParts, hideMiddle2 || hidAny, baseLabel);
4993
5081
  return { label: label2, width: visibleWidth(label2) };
4994
5082
  };
4995
5083
  let { label, width } = build();
@@ -5082,13 +5170,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5082
5170
  return { headRaw: headRaw2, tailRaw: tailRaw2, hideMiddle: hideMiddle2 };
5083
5171
  };
5084
5172
  let { headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tailCount);
5085
- let candidate = tryTrimDirsToFit(
5086
- headRaw,
5087
- tailRaw,
5088
- hideMiddle,
5089
- baseFull,
5090
- maxWidth
5091
- );
5173
+ let candidate = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5092
5174
  if (!candidate) {
5093
5175
  return baseFull;
5094
5176
  }
@@ -5097,13 +5179,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5097
5179
  if (headCount + tailCount < total) {
5098
5180
  const tryTail = Math.min(tailCount + 1, total - headCount);
5099
5181
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tryTail));
5100
- const candTail = tryTrimDirsToFit(
5101
- headRaw,
5102
- tailRaw,
5103
- hideMiddle,
5104
- baseFull,
5105
- maxWidth
5106
- );
5182
+ const candTail = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5107
5183
  if (candTail) {
5108
5184
  tailCount = tryTail;
5109
5185
  candidate = candTail;
@@ -5113,13 +5189,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5113
5189
  if (!advanced && headCount + tailCount < total) {
5114
5190
  const tryHead = Math.min(headCount + 1, total - tailCount);
5115
5191
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(tryHead, tailCount));
5116
- const candHead = tryTrimDirsToFit(
5117
- headRaw,
5118
- tailRaw,
5119
- hideMiddle,
5120
- baseFull,
5121
- maxWidth
5122
- );
5192
+ const candHead = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5123
5193
  if (candHead) {
5124
5194
  headCount = tryHead;
5125
5195
  candidate = candHead;
@@ -5181,7 +5251,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5181
5251
  const queue = [];
5182
5252
  const seen = /* @__PURE__ */ new Set();
5183
5253
  for (const testAbs of executedTestsAbs) {
5184
- const testPathNormalized = path7.resolve(testAbs).replace(/\\/g, "/");
5254
+ const testPathNormalized = path8.resolve(testAbs).replace(/\\/g, "/");
5185
5255
  dist.set(testPathNormalized, 0);
5186
5256
  queue.push([testPathNormalized, 0]);
5187
5257
  }
@@ -5200,12 +5270,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5200
5270
  const specs = await extractImportSpecs2(currentFile, specsCache);
5201
5271
  const nextDistance = currentDistance + 1;
5202
5272
  for (const spec of specs) {
5203
- const resolved = resolveImportWithRoot(
5204
- currentFile,
5205
- spec,
5206
- rootDir,
5207
- resolutionCache
5208
- );
5273
+ const resolved = resolveImportWithRoot(currentFile, spec, rootDir, resolutionCache);
5209
5274
  const usable = resolved && !resolved.includes("/node_modules/");
5210
5275
  if (usable) {
5211
5276
  const existing = dist.get(resolved);
@@ -5220,13 +5285,10 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5220
5285
  return dist;
5221
5286
  };
5222
5287
  var renderPerFileCompositeTable = async (opts) => {
5223
- const rel = path7.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5288
+ const rel = path8.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5224
5289
  const sum = opts.file.toSummary();
5225
5290
  const rowsAvail = typeof process.stdout.rows === "number" && process.stdout.rows > 10 ? process.stdout.rows : 40;
5226
- const tableBudget = Math.max(
5227
- 14,
5228
- Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8)
5229
- );
5291
+ const tableBudget = Math.max(14, Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8));
5230
5292
  const rowBudget = Math.max(6, tableBudget - 6);
5231
5293
  const blocks = computeUncoveredBlocks(opts.file).slice().sort((firstRange, secondRange) => {
5232
5294
  const firstLength = firstRange.end - firstRange.start;
@@ -5270,9 +5332,7 @@ var renderPerFileCompositeTable = async (opts) => {
5270
5332
  rows.push([
5271
5333
  cell(
5272
5334
  rel,
5273
- (padded) => ansi.dim(
5274
- shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length)
5275
- )
5335
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5276
5336
  ),
5277
5337
  cell("Totals", ansi.dim),
5278
5338
  cell("\u2014", ansi.dim),
@@ -5291,11 +5351,7 @@ var renderPerFileCompositeTable = async (opts) => {
5291
5351
  rows.push([
5292
5352
  cell(
5293
5353
  rel,
5294
- (padded) => ansi.dim(
5295
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5296
- padded.length
5297
- )
5298
- )
5354
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5299
5355
  ),
5300
5356
  cell("Hotspots", ansi.dim),
5301
5357
  cell("", ansi.dim),
@@ -5309,14 +5365,8 @@ var renderPerFileCompositeTable = async (opts) => {
5309
5365
  rows.push([
5310
5366
  cell(rel, (padded) => {
5311
5367
  const width = padded.length;
5312
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5313
- width
5314
- );
5315
- return linkifyPadded(
5316
- opts.absPath,
5317
- hotspotRange.start,
5318
- opts.editorCmd
5319
- )(display);
5368
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5369
+ return linkifyPadded(opts.absPath, hotspotRange.start, opts.editorCmd)(display);
5320
5370
  }),
5321
5371
  cell("Hotspot"),
5322
5372
  cell(`L${hotspotRange.start}\u2013L${hotspotRange.end}`),
@@ -5333,11 +5383,7 @@ var renderPerFileCompositeTable = async (opts) => {
5333
5383
  rows.push([
5334
5384
  cell(
5335
5385
  rel,
5336
- (padded) => ansi.dim(
5337
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5338
- padded.length
5339
- )
5340
- )
5386
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5341
5387
  ),
5342
5388
  cell("Functions", ansi.dim),
5343
5389
  cell("", ansi.dim),
@@ -5351,14 +5397,8 @@ var renderPerFileCompositeTable = async (opts) => {
5351
5397
  rows.push([
5352
5398
  cell(rel, (padded) => {
5353
5399
  const width = padded.length;
5354
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5355
- width
5356
- );
5357
- return linkifyPadded(
5358
- opts.absPath,
5359
- missedFunction.line,
5360
- opts.editorCmd
5361
- )(display);
5400
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5401
+ return linkifyPadded(opts.absPath, missedFunction.line, opts.editorCmd)(display);
5362
5402
  }),
5363
5403
  cell("Func"),
5364
5404
  cell(`L${missedFunction.line}`),
@@ -5375,11 +5415,7 @@ var renderPerFileCompositeTable = async (opts) => {
5375
5415
  rows.push([
5376
5416
  cell(
5377
5417
  rel,
5378
- (padded) => ansi.dim(
5379
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5380
- padded.length
5381
- )
5382
- )
5418
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5383
5419
  ),
5384
5420
  cell("Branches", ansi.dim),
5385
5421
  cell("", ansi.dim),
@@ -5393,14 +5429,8 @@ var renderPerFileCompositeTable = async (opts) => {
5393
5429
  rows.push([
5394
5430
  cell(rel, (padded) => {
5395
5431
  const width = padded.length;
5396
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5397
- width
5398
- );
5399
- return linkifyPadded(
5400
- opts.absPath,
5401
- missedBranch.line,
5402
- opts.editorCmd
5403
- )(display);
5432
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5433
+ return linkifyPadded(opts.absPath, missedBranch.line, opts.editorCmd)(display);
5404
5434
  }),
5405
5435
  cell("Branch"),
5406
5436
  cell(`L${missedBranch.line}`),
@@ -5408,9 +5438,7 @@ var renderPerFileCompositeTable = async (opts) => {
5408
5438
  cell(""),
5409
5439
  cell(""),
5410
5440
  cell(""),
5411
- cell(
5412
- `#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`
5413
- )
5441
+ cell(`#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`)
5414
5442
  ]);
5415
5443
  }
5416
5444
  }
@@ -5430,9 +5458,7 @@ var renderPerFileCompositeTable = async (opts) => {
5430
5458
  rows.push([
5431
5459
  cell(rel, (padded) => {
5432
5460
  const width = padded.length;
5433
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5434
- width
5435
- );
5461
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5436
5462
  return linkifyPadded(opts.absPath, ln, opts.editorCmd)(display);
5437
5463
  }),
5438
5464
  cell("Line"),
@@ -5445,16 +5471,7 @@ var renderPerFileCompositeTable = async (opts) => {
5445
5471
  ]);
5446
5472
  }
5447
5473
  while (rows.length < target) {
5448
- rows.push([
5449
- cell(""),
5450
- cell(""),
5451
- cell(""),
5452
- cell(""),
5453
- cell(""),
5454
- cell(""),
5455
- cell(""),
5456
- cell("")
5457
- ]);
5474
+ rows.push([cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell("")]);
5458
5475
  }
5459
5476
  }
5460
5477
  }
@@ -5462,20 +5479,17 @@ var renderPerFileCompositeTable = async (opts) => {
5462
5479
  console.info(table);
5463
5480
  const sep = ansi.gray(
5464
5481
  "\u2500".repeat(
5465
- Math.max(
5466
- 20,
5467
- typeof process.stdout.columns === "number" ? process.stdout.columns : 100
5468
- )
5482
+ Math.max(20, typeof process.stdout.columns === "number" ? process.stdout.columns : 100)
5469
5483
  )
5470
5484
  );
5471
5485
  console.info(sep);
5472
5486
  };
5473
5487
  var printPerFileCompositeTables = async (opts) => {
5474
5488
  const selectionAbs = (opts.selectionPaths ?? []).map(
5475
- (selPath) => path7.resolve(selPath).replace(/\\/g, "/")
5489
+ (selPath) => path8.resolve(selPath).replace(/\\/g, "/")
5476
5490
  );
5477
5491
  const changedAbs = (opts.changedFiles ?? []).map(
5478
- (chgPath) => path7.resolve(chgPath).replace(/\\/g, "/")
5492
+ (chgPath) => path8.resolve(chgPath).replace(/\\/g, "/")
5479
5493
  );
5480
5494
  const tokenizeForSimilarity = (filePathForTokens) => new Set(
5481
5495
  filePathForTokens.toLowerCase().replace(/[^a-z0-9/_\-.]/g, " ").split(/[/_.-]+/).filter(Boolean)
@@ -5491,15 +5505,15 @@ var printPerFileCompositeTables = async (opts) => {
5491
5505
  return intersectionCount / unionSize;
5492
5506
  };
5493
5507
  const isSameDirOrChild = (firstAbs, secondAbs) => {
5494
- const dirA = path7.dirname(firstAbs).replace(/\\/g, "/");
5495
- const dirB = path7.dirname(secondAbs).replace(/\\/g, "/");
5508
+ const dirA = path8.dirname(firstAbs).replace(/\\/g, "/");
5509
+ const dirB = path8.dirname(secondAbs).replace(/\\/g, "/");
5496
5510
  return dirA === dirB || dirB.startsWith(`${dirA}/`) || dirA.startsWith(`${dirB}/`);
5497
5511
  };
5498
5512
  const selectionTokens = selectionAbs.map(tokenizeForSimilarity);
5499
5513
  const changedTokens = changedAbs.map(tokenizeForSimilarity);
5500
- const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path7.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5514
+ const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path8.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5501
5515
  const testTokens = executedTestsAbs.map(tokenizeForSimilarity);
5502
- const allMapFilesAbs = opts.map.files().map((absPath) => path7.resolve(absPath).replace(/\\/g, "/"));
5516
+ const allMapFilesAbs = opts.map.files().map((absPath) => path8.resolve(absPath).replace(/\\/g, "/"));
5503
5517
  const uncoveredCandidates = allMapFilesAbs.filter((absPath) => {
5504
5518
  const sum = opts.map.fileCoverageFor(absPath).toSummary();
5505
5519
  return !(sum.lines.pct >= 100 && sum.functions.pct >= 100 && sum.branches.pct >= 100);
@@ -5516,33 +5530,24 @@ var printPerFileCompositeTables = async (opts) => {
5516
5530
  const selectionSetAbs = new Set(selectionAbs);
5517
5531
  const scored = await Promise.all(
5518
5532
  candidates.map(async (abs) => {
5519
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
5533
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
5520
5534
  const sum = opts.map.fileCoverageFor(abs).toSummary();
5521
5535
  const pct = Number.isFinite(sum.lines.pct) ? sum.lines.pct : 0;
5522
- const absNorm = path7.resolve(abs).replace(/\\/g, "/");
5536
+ const absNorm = path8.resolve(abs).replace(/\\/g, "/");
5523
5537
  const selfTokens = tokenizeForSimilarity(absNorm);
5524
5538
  const selSim = Math.max(
5525
5539
  0,
5526
- ...selectionTokens.map(
5527
- (selectionTokenSet) => jaccard(selfTokens, selectionTokenSet)
5528
- )
5540
+ ...selectionTokens.map((selectionTokenSet) => jaccard(selfTokens, selectionTokenSet))
5529
5541
  );
5530
5542
  const chgSim = Math.max(
5531
5543
  0,
5532
- ...changedTokens.map(
5533
- (changedTokenSet) => jaccard(selfTokens, changedTokenSet)
5534
- )
5535
- );
5536
- const tstSim = Math.max(
5537
- 0,
5538
- ...testTokens.map((tset) => jaccard(selfTokens, tset))
5544
+ ...changedTokens.map((changedTokenSet) => jaccard(selfTokens, changedTokenSet))
5539
5545
  );
5546
+ const tstSim = Math.max(0, ...testTokens.map((tset) => jaccard(selfTokens, tset)));
5540
5547
  const nearSelection = selectionAbs.some(
5541
5548
  (selectionPath) => isSameDirOrChild(absNorm, selectionPath)
5542
5549
  );
5543
- const nearChanged = changedAbs.some(
5544
- (changedPath) => isSameDirOrChild(absNorm, changedPath)
5545
- );
5550
+ const nearChanged = changedAbs.some((changedPath) => isSameDirOrChild(absNorm, changedPath));
5546
5551
  const related = selSim > 0 || chgSim > 0 || nearSelection || nearChanged;
5547
5552
  const distance = selectionSetAbs.has(absNorm) ? 0 : distFromTests.get(absNorm) ?? INF;
5548
5553
  let group = 6;
@@ -5566,9 +5571,12 @@ var printPerFileCompositeTables = async (opts) => {
5566
5571
  return { abs: absNorm, rel, linesPct: pct, group, score, distance };
5567
5572
  })
5568
5573
  );
5569
- let files = scored.sort(
5570
- (left, right) => left.group - right.group || left.distance - right.distance || right.score - left.score || right.linesPct - left.linesPct || left.rel.localeCompare(right.rel)
5571
- ).map((scoredItem) => scoredItem.abs);
5574
+ const prodSeeds = selectionAbs.length > 0 ? selectionAbs : changedAbs;
5575
+ const rank = await computeDirectnessRank({ repoRoot: opts.root, productionSeeds: prodSeeds });
5576
+ let files = sortPathsWithRank(
5577
+ rank,
5578
+ scored.map((s) => s.abs)
5579
+ );
5572
5580
  if (selectionAbs.length > 0) {
5573
5581
  const selectionSet = new Set(selectionAbs);
5574
5582
  const selectedHead = files.filter((filePath) => selectionSet.has(filePath));
@@ -5592,7 +5600,7 @@ var printPerFileCompositeTables = async (opts) => {
5592
5600
 
5593
5601
  // src/lib/jest-bridge.ts
5594
5602
  var import_json5 = __toESM(require_lib(), 1);
5595
- import * as path8 from "node:path";
5603
+ import * as path9 from "node:path";
5596
5604
  import * as fs4 from "node:fs";
5597
5605
  import * as util from "node:util";
5598
5606
  var extractBridgePath = (raw, cwd) => {
@@ -5603,7 +5611,7 @@ var extractBridgePath = (raw, cwd) => {
5603
5611
  return null;
5604
5612
  }
5605
5613
  const jsonPath = matches[matches.length - 1][1].trim().replace(/^["'`]|["'`]$/g, "");
5606
- return path8.isAbsolute(jsonPath) ? jsonPath : path8.resolve(cwd, jsonPath);
5614
+ return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
5607
5615
  };
5608
5616
  var drawRule = (label) => {
5609
5617
  const width = Math.max(
@@ -6251,7 +6259,7 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
6251
6259
  const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
6252
6260
  if (loc) {
6253
6261
  const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
6254
- out.push(` ${ansi.dim("at")} ${osc8(`${path8.basename(loc.file)}:${loc.line}`, href)}`);
6262
+ out.push(` ${ansi.dim("at")} ${osc8(`${path9.basename(loc.file)}:${loc.line}`, href)}`);
6255
6263
  }
6256
6264
  out.push("");
6257
6265
  return out;
@@ -6291,6 +6299,7 @@ var formatJestOutputVitest = (raw, opts) => {
6291
6299
  const projectHint = new RegExp(
6292
6300
  `(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
6293
6301
  );
6302
+ const onlyFailures = Boolean(opts?.onlyFailures);
6294
6303
  const lines = raw.split(/\r?\n/);
6295
6304
  const out = [];
6296
6305
  const seenFailures = /* @__PURE__ */ new Set();
@@ -6378,8 +6387,10 @@ var formatJestOutputVitest = (raw, opts) => {
6378
6387
  continue;
6379
6388
  }
6380
6389
  seenFiles.add(rel);
6381
- const pill = badge === "PASS" ? colorTokens.passPill("PASS") : colorTokens.failPill("FAIL");
6382
- out.push(`${pill} ${ansi.white(rel)}`);
6390
+ if (!(onlyFailures && badge === "PASS")) {
6391
+ const pill = badge === "PASS" ? colorTokens.passPill("PASS") : colorTokens.failPill("FAIL");
6392
+ out.push(`${pill} ${ansi.white(rel)}`);
6393
+ }
6383
6394
  lineIndex += 1;
6384
6395
  continue;
6385
6396
  }
@@ -6492,14 +6503,21 @@ function renderVitestFromJestJSON(data, opts) {
6492
6503
  `(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
6493
6504
  );
6494
6505
  const ctx = { projectHint, editorCmd: opts?.editorCmd, showStacks: true };
6506
+ const onlyFailures = Boolean(opts?.onlyFailures);
6495
6507
  const out = [];
6496
- out.push(renderRunLine(cwd));
6497
- out.push("");
6508
+ if (!onlyFailures) {
6509
+ out.push(renderRunLine(cwd));
6510
+ out.push("");
6511
+ }
6498
6512
  for (const file of data.testResults) {
6499
6513
  const rel = file.testFilePath.replace(/\\/g, "/").replace(`${cwd}/`, "");
6500
6514
  const failed = file.testResults.filter((assertion) => assertion.status === "failed");
6501
- out.push(...buildPerFileOverview(rel, file.testResults));
6502
- out.push(buildFileBadgeLine(rel, failed.length));
6515
+ if (!onlyFailures) {
6516
+ out.push(...buildPerFileOverview(rel, file.testResults));
6517
+ }
6518
+ if (!(onlyFailures && failed.length === 0)) {
6519
+ out.push(buildFileBadgeLine(rel, failed.length));
6520
+ }
6503
6521
  if (file.failureMessage && failed.length === 0) {
6504
6522
  const lines = file.failureMessage.split(/\r?\n/);
6505
6523
  const details = linesFromDetails(file.failureDetails);
@@ -6530,7 +6548,7 @@ function renderVitestFromJestJSON(data, opts) {
6530
6548
  const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
6531
6549
  const locLink = deepestLoc && (() => {
6532
6550
  const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, opts?.editorCmd);
6533
- const base = `${path8.basename(deepestLoc.file)}:${deepestLoc.line}`;
6551
+ const base = `${path9.basename(deepestLoc.file)}:${deepestLoc.line}`;
6534
6552
  return osc8(base, href);
6535
6553
  })();
6536
6554
  const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
@@ -6578,7 +6596,7 @@ ${footer}`;
6578
6596
  init_env_utils();
6579
6597
  init_exec();
6580
6598
  init_args();
6581
- import * as path9 from "node:path";
6599
+ import * as path10 from "node:path";
6582
6600
  import * as os2 from "node:os";
6583
6601
  import * as fsSync3 from "node:fs";
6584
6602
  import * as fs5 from "node:fs/promises";
@@ -6587,10 +6605,6 @@ import * as Reports from "istanbul-reports";
6587
6605
  import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
6588
6606
  var jestBin = "./node_modules/.bin/jest";
6589
6607
  var babelNodeBin = "./node_modules/.bin/babel-node";
6590
- var moduleSpecifierForRequire = (
6591
- // @ts-ignore
6592
- typeof __filename !== "undefined" ? __filename : import.meta.url
6593
- );
6594
6608
  var registerSignalHandlersOnce = () => {
6595
6609
  let handled = false;
6596
6610
  const on = (sig) => {
@@ -6608,7 +6622,6 @@ Received ${sig}, exiting...
6608
6622
  };
6609
6623
  var isDebug = () => Boolean(process.env.TEST_CLI_DEBUG);
6610
6624
  var mergeLcov = async () => {
6611
- const jestLcovPath = "coverage/jest/lcov.info";
6612
6625
  const vitestLcovPath = "coverage/vitest/lcov.info";
6613
6626
  const mergedOutPath = "coverage/lcov.info";
6614
6627
  const readOrEmpty = async (filePath) => {
@@ -6619,7 +6632,7 @@ var mergeLcov = async () => {
6619
6632
  }
6620
6633
  };
6621
6634
  let vitestContent = "";
6622
- let jestContent = "";
6635
+ const jestParts = [];
6623
6636
  try {
6624
6637
  vitestContent = await readOrEmpty(vitestLcovPath);
6625
6638
  } catch (readVitestError) {
@@ -6627,20 +6640,46 @@ var mergeLcov = async () => {
6627
6640
  console.info(`read vitest lcov failed: ${String(readVitestError)}`);
6628
6641
  }
6629
6642
  }
6643
+ const collectLcovs = (dir) => {
6644
+ const out = [];
6645
+ try {
6646
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6647
+ for (const entry of entries) {
6648
+ const full = path10.join(dir, entry.name);
6649
+ if (entry.isDirectory()) {
6650
+ out.push(...collectLcovs(full));
6651
+ } else if (entry.isFile() && entry.name === "lcov.info") {
6652
+ out.push(full);
6653
+ }
6654
+ }
6655
+ } catch {
6656
+ }
6657
+ return out;
6658
+ };
6630
6659
  try {
6631
- jestContent = await readOrEmpty(jestLcovPath);
6660
+ const jestRoot = path10.join("coverage", "jest");
6661
+ const candidates = [path10.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
6662
+ for (const filePath of candidates) {
6663
+ try {
6664
+ const content = await readOrEmpty(filePath);
6665
+ if (content.trim()) {
6666
+ jestParts.push(content.trim());
6667
+ }
6668
+ } catch {
6669
+ }
6670
+ }
6632
6671
  } catch (readJestError) {
6633
6672
  if (isDebug()) {
6634
- console.info(`read jest lcov failed: ${String(readJestError)}`);
6673
+ console.info(`scan jest lcov failed: ${String(readJestError)}`);
6635
6674
  }
6636
6675
  }
6637
- if (!vitestContent && !jestContent) {
6676
+ if (!vitestContent && jestParts.length === 0) {
6638
6677
  if (isDebug()) {
6639
6678
  console.info("No coverage outputs found to merge.");
6640
6679
  }
6641
6680
  return;
6642
6681
  }
6643
- const merged = [vitestContent.trim(), jestContent.trim()].filter(Boolean).join("\n");
6682
+ const merged = [vitestContent.trim(), ...jestParts].filter(Boolean).join("\n");
6644
6683
  if (merged.length > 0) {
6645
6684
  await (await import("node:fs/promises")).mkdir("coverage", { recursive: true });
6646
6685
  await (await import("node:fs/promises")).writeFile(mergedOutPath, `${merged}
@@ -6653,23 +6692,40 @@ var mergeLcov = async () => {
6653
6692
  }
6654
6693
  };
6655
6694
  var emitMergedCoverage = async (ui, opts) => {
6656
- const jestJson = path9.join("coverage", "jest", "coverage-final.json");
6657
- const jSize = fsSync3.existsSync(jestJson) ? fsSync3.statSync(jestJson).size : -1;
6658
- const jestSizeLabel = jSize >= 0 ? `${jSize} bytes` : "missing";
6659
- if (isDebug()) {
6660
- console.info(`Coverage JSON probe \u2192 jest: ${jestSizeLabel}`);
6661
- }
6662
- const jestData = await readCoverageJson(jestJson);
6663
- const jestFilesCount = Object.keys(jestData).length;
6664
- if (isDebug()) {
6665
- console.info(`Decoded coverage entries \u2192 jest: ${jestFilesCount}`);
6666
- }
6667
6695
  const map = createCoverageMap2({});
6668
- if (jestFilesCount > 0) {
6696
+ const listJsons = (dir) => {
6697
+ const out = [];
6669
6698
  try {
6670
- map.merge(jestData);
6699
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6700
+ for (const entry of entries) {
6701
+ const full = path10.join(dir, entry.name);
6702
+ if (entry.isDirectory()) {
6703
+ out.push(...listJsons(full));
6704
+ } else if (entry.isFile() && entry.name === "coverage-final.json") {
6705
+ out.push(full);
6706
+ }
6707
+ }
6708
+ } catch {
6709
+ }
6710
+ return out;
6711
+ };
6712
+ const coverageRoot = path10.join("coverage", "jest");
6713
+ const jsonCandidates = [
6714
+ path10.join(coverageRoot, "coverage-final.json"),
6715
+ ...listJsons(coverageRoot)
6716
+ ].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => {
6717
+ const isFirst = arr.indexOf(absolutePath) === index;
6718
+ const exists = fsSync3.existsSync(absolutePath);
6719
+ return isFirst && exists;
6720
+ });
6721
+ for (const jsonPath of jsonCandidates) {
6722
+ try {
6723
+ const data = await readCoverageJson(jsonPath);
6724
+ if (Object.keys(data).length) {
6725
+ map.merge(data);
6726
+ }
6671
6727
  } catch (mergeJestError) {
6672
- console.warn(`Failed merging jest coverage JSON: ${String(mergeJestError)}`);
6728
+ console.warn(`Failed merging jest coverage JSON @ ${jsonPath}: ${String(mergeJestError)}`);
6673
6729
  }
6674
6730
  }
6675
6731
  if (map.files().length === 0) {
@@ -6719,7 +6775,7 @@ var emitMergedCoverage = async (ui, opts) => {
6719
6775
  executedTests: opts.executedTests ?? []
6720
6776
  });
6721
6777
  const context = LibReport.createContext({
6722
- dir: path9.resolve("coverage", "merged"),
6778
+ dir: path10.resolve("coverage", "merged"),
6723
6779
  coverageMap: filteredMap,
6724
6780
  defaultSummarizer: "nested"
6725
6781
  });
@@ -6787,8 +6843,8 @@ var emitMergedCoverage = async (ui, opts) => {
6787
6843
  for (const reporter of reporters) {
6788
6844
  reporter.execute(context);
6789
6845
  }
6790
- const textPath = path9.resolve("coverage", "merged", "coverage.txt");
6791
- const summaryPath = path9.resolve("coverage", "merged", "coverage-summary.txt");
6846
+ const textPath = path10.resolve("coverage", "merged", "coverage.txt");
6847
+ const summaryPath = path10.resolve("coverage", "merged", "coverage-summary.txt");
6792
6848
  const filesToPrint = [];
6793
6849
  if (fsSync3.existsSync(textPath)) {
6794
6850
  filesToPrint.push(textPath);
@@ -6859,6 +6915,7 @@ var program = async () => {
6859
6915
  collectCoverage,
6860
6916
  coverageUi,
6861
6917
  coverageAbortOnFailure,
6918
+ onlyFailures,
6862
6919
  selectionSpecified,
6863
6920
  selectionPaths,
6864
6921
  includeGlobs,
@@ -6870,17 +6927,43 @@ var program = async () => {
6870
6927
  coverageMode,
6871
6928
  coverageMaxFiles: coverageMaxFilesArg,
6872
6929
  coverageMaxHotspots: coverageMaxHotspotsArg,
6873
- coveragePageFit
6930
+ coveragePageFit,
6931
+ changed
6874
6932
  } = deriveArgs(argv);
6875
- console.info(`Selection \u2192 specified=${selectionSpecified} paths=${selectionPaths.length}`);
6933
+ const getChangedFiles = async (mode, cwd) => {
6934
+ const collect = async (cmd, args) => {
6935
+ try {
6936
+ const out = await runText(cmd, args, {
6937
+ cwd,
6938
+ env: safeEnv(process.env, {}),
6939
+ timeoutMs: 4e3
6940
+ });
6941
+ return out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6942
+ } catch {
6943
+ return [];
6944
+ }
6945
+ };
6946
+ const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
6947
+ const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
6948
+ const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
6949
+ const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
6950
+ return rels.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
6951
+ };
6952
+ const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
6953
+ const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
6954
+ const selectionPathsAugmented = changedSelectionAbs.length ? Array.from(/* @__PURE__ */ new Set([...selectionPaths, ...changedSelectionAbs])) : selectionPaths;
6955
+ const selectionSpecifiedAugmented = Boolean(selectionSpecified || changedSelectionAbs.length > 0);
6956
+ console.info(
6957
+ `Selection \u2192 specified=${selectionSpecifiedAugmented} paths=${selectionPathsAugmented.length}`
6958
+ );
6876
6959
  const { jest } = argsForDiscovery(["run"], jestArgs);
6877
- const selectionLooksLikeTest = selectionPaths.some(
6960
+ const selectionLooksLikeTest = selectionPathsAugmented.some(
6878
6961
  (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText)
6879
6962
  );
6880
- const selectionLooksLikePath = selectionPaths.some(
6963
+ const selectionLooksLikePath = selectionPathsAugmented.some(
6881
6964
  (pathText) => /[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)
6882
6965
  );
6883
- const selectionHasPaths = selectionPaths.length > 0;
6966
+ const selectionHasPaths = selectionPathsAugmented.length > 0;
6884
6967
  const repoRootForDiscovery = workspaceRoot ?? await findRepoRoot();
6885
6968
  const expandProductionSelections = async (tokens, repoRoot) => {
6886
6969
  const results = /* @__PURE__ */ new Set();
@@ -6889,18 +6972,18 @@ var program = async () => {
6889
6972
  if (!token) {
6890
6973
  continue;
6891
6974
  }
6892
- const isAbs = path9.isAbsolute(token);
6975
+ const isAbs = path10.isAbsolute(token);
6893
6976
  const looksLikeRelPath = /[\\/]/.test(token);
6894
6977
  let candidateFromRoot;
6895
6978
  if (token.startsWith("/")) {
6896
- candidateFromRoot = path9.join(repoRoot, token.slice(1));
6979
+ candidateFromRoot = path10.join(repoRoot, token.slice(1));
6897
6980
  } else if (looksLikeRelPath) {
6898
- candidateFromRoot = path9.join(repoRoot, token);
6981
+ candidateFromRoot = path10.join(repoRoot, token);
6899
6982
  } else {
6900
6983
  candidateFromRoot = void 0;
6901
6984
  }
6902
6985
  const tryPushIfProd = (absPath) => {
6903
- const norm = path9.resolve(absPath).replace(/\\/g, "/");
6986
+ const norm = path10.resolve(absPath).replace(/\\/g, "/");
6904
6987
  const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
6905
6988
  if (!isTest && fsSync3.existsSync(norm)) {
6906
6989
  results.add(norm);
@@ -6922,7 +7005,7 @@ var program = async () => {
6922
7005
  }),
6923
7006
  timeoutMs: 4e3
6924
7007
  });
6925
- const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path9.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
7008
+ const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path10.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6926
7009
  (abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
6927
7010
  );
6928
7011
  matches.forEach((abs) => results.add(abs));
@@ -6931,20 +7014,20 @@ var program = async () => {
6931
7014
  }
6932
7015
  return Array.from(results);
6933
7016
  };
6934
- const initialProdSelections = selectionPaths.filter(
7017
+ const initialProdSelections = selectionPathsAugmented.filter(
6935
7018
  (pathText) => (/[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)) && !/(^|\/)tests?\//i.test(pathText) && !/\.(test|spec)\.[tj]sx?$/i.test(pathText)
6936
7019
  );
6937
- const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPaths, repoRootForDiscovery);
7020
+ const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPathsAugmented, repoRootForDiscovery);
6938
7021
  const selectionIncludesProdPaths = expandedProdSelections.length > 0;
6939
7022
  console.info(
6940
7023
  `Selection classify \u2192 looksLikePath=${selectionLooksLikePath} looksLikeTest=${selectionLooksLikeTest} prodPaths=${selectionIncludesProdPaths}`
6941
7024
  );
6942
- const stripPathTokens = (args) => args.filter((token) => !selectionPaths.includes(token));
7025
+ const stripPathTokens = (args) => args.filter((token) => !selectionPathsAugmented.includes(token));
6943
7026
  const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
6944
7027
  const projectConfigs = [];
6945
7028
  try {
6946
- const baseCfg = path9.resolve("jest.config.js");
6947
- const tsCfg = path9.resolve("jest.ts.config.js");
7029
+ const baseCfg = path10.resolve("jest.config.js");
7030
+ const tsCfg = path10.resolve("jest.ts.config.js");
6948
7031
  if (fsSync3.existsSync(baseCfg)) {
6949
7032
  projectConfigs.push(baseCfg);
6950
7033
  }
@@ -6961,7 +7044,7 @@ var program = async () => {
6961
7044
  );
6962
7045
  const prodSelections2 = expandedProdSelections;
6963
7046
  for (const cfg of projectConfigs) {
6964
- const cfgCwd = path9.dirname(cfg);
7047
+ const cfgCwd = path10.dirname(cfg);
6965
7048
  const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6966
7049
  cwd: cfgCwd
6967
7050
  });
@@ -6974,7 +7057,7 @@ var program = async () => {
6974
7057
  });
6975
7058
  } catch (err) {
6976
7059
  if (isDebug()) {
6977
- console.warn(`direct selection failed for project ${path9.basename(cfg)}: ${String(err)}`);
7060
+ console.warn(`direct selection failed for project ${path10.basename(cfg)}: ${String(err)}`);
6978
7061
  }
6979
7062
  }
6980
7063
  perProjectFiles.set(cfg, directPerProject);
@@ -6986,7 +7069,7 @@ var program = async () => {
6986
7069
  )} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
6987
7070
  );
6988
7071
  for (const cfg of projectConfigs) {
6989
- const cfgCwd = path9.dirname(cfg);
7072
+ const cfgCwd = path10.dirname(cfg);
6990
7073
  const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6991
7074
  cwd: cfgCwd
6992
7075
  });
@@ -6996,18 +7079,18 @@ var program = async () => {
6996
7079
  const perProjectFiltered = /* @__PURE__ */ new Map();
6997
7080
  for (const cfg of projectConfigs) {
6998
7081
  const files = perProjectFiles.get(cfg) ?? [];
6999
- const selectionTestPaths = selectionPaths.filter(
7082
+ const selectionTestPaths = selectionPathsAugmented.filter(
7000
7083
  (pathToken) => /\.(test|spec)\.[tj]sx?$/i.test(pathToken) || /(^|\/)tests?\//i.test(pathToken)
7001
7084
  );
7002
7085
  const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
7003
7086
  const absFiles = candidates.map(
7004
- (candidatePath) => path9.isAbsolute(candidatePath) ? candidatePath : path9.join(repoRootForDiscovery, candidatePath)
7087
+ (candidatePath) => path10.isAbsolute(candidatePath) ? candidatePath : path10.join(repoRootForDiscovery, candidatePath)
7005
7088
  ).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
7006
7089
  const onlyOwned = await filterCandidatesForProject(
7007
7090
  cfg,
7008
7091
  jestDiscoveryArgs,
7009
7092
  absFiles,
7010
- path9.dirname(cfg)
7093
+ path10.dirname(cfg)
7011
7094
  );
7012
7095
  perProjectFiltered.set(cfg, onlyOwned);
7013
7096
  }
@@ -7019,7 +7102,7 @@ var program = async () => {
7019
7102
  if (selectionHasPaths && prodSelections.length > 0) {
7020
7103
  console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
7021
7104
  const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
7022
- const selectionKey = prodSelections.map((absPath) => path9.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((a, b) => a.localeCompare(b)).join("|");
7105
+ const selectionKey = prodSelections.map((absPath) => path10.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
7023
7106
  const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
7024
7107
  const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
7025
7108
  const rgMatches = await cachedRelated2({
@@ -7049,7 +7132,7 @@ var program = async () => {
7049
7132
  cfg,
7050
7133
  jestDiscoveryArgs,
7051
7134
  rgCandidates,
7052
- path9.dirname(cfg)
7135
+ path10.dirname(cfg)
7053
7136
  );
7054
7137
  perProjectFromRg.set(cfg, owned);
7055
7138
  }
@@ -7064,9 +7147,9 @@ var program = async () => {
7064
7147
  } else {
7065
7148
  const repoRootForScan = repoRootForDiscovery;
7066
7149
  const toSeeds = (abs) => {
7067
- const rel = path9.relative(repoRootForScan, abs).replace(/\\/g, "/");
7150
+ const rel = path10.relative(repoRootForScan, abs).replace(/\\/g, "/");
7068
7151
  const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
7069
- const base = path9.basename(withoutExt);
7152
+ const base = path10.basename(withoutExt);
7070
7153
  const segs = withoutExt.split("/");
7071
7154
  const tail2 = segs.slice(-2).join("/");
7072
7155
  return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
@@ -7081,8 +7164,8 @@ var program = async () => {
7081
7164
  }
7082
7165
  };
7083
7166
  const resolveLocalImport = (fromFile, spec) => {
7084
- const baseDir = path9.dirname(fromFile);
7085
- const cand = path9.resolve(baseDir, spec);
7167
+ const baseDir = path10.dirname(fromFile);
7168
+ const cand = path10.resolve(baseDir, spec);
7086
7169
  const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
7087
7170
  for (const ext of exts) {
7088
7171
  const full = ext ? `${cand}${ext}` : cand;
@@ -7091,7 +7174,7 @@ var program = async () => {
7091
7174
  }
7092
7175
  }
7093
7176
  for (const ext of exts) {
7094
- const full = path9.join(cand, `index${ext}`);
7177
+ const full = path10.join(cand, `index${ext}`);
7095
7178
  if (fsSync3.existsSync(full)) {
7096
7179
  return full;
7097
7180
  }
@@ -7145,7 +7228,7 @@ var program = async () => {
7145
7228
  cfg,
7146
7229
  jestDiscoveryArgs,
7147
7230
  keptCandidates,
7148
- path9.dirname(cfg)
7231
+ path10.dirname(cfg)
7149
7232
  );
7150
7233
  perProjectFromScan.set(cfg, owned);
7151
7234
  }
@@ -7172,9 +7255,9 @@ var program = async () => {
7172
7255
  if (effectiveJestFiles.length === 0) {
7173
7256
  const repoRoot = repoRootForRefinement;
7174
7257
  const seeds = prodSelections.map(
7175
- (abs) => path9.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7258
+ (abs) => path10.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7176
7259
  ).flatMap((rel) => {
7177
- const base = path9.basename(rel);
7260
+ const base = path10.basename(rel);
7178
7261
  const segments = rel.split("/");
7179
7262
  return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
7180
7263
  });
@@ -7187,8 +7270,8 @@ var program = async () => {
7187
7270
  }
7188
7271
  };
7189
7272
  const resolveLocalImport = (fromFile, spec) => {
7190
- const baseDir = path9.dirname(fromFile);
7191
- const candidate = path9.resolve(baseDir, spec);
7273
+ const baseDir = path10.dirname(fromFile);
7274
+ const candidate = path10.resolve(baseDir, spec);
7192
7275
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
7193
7276
  for (const ext of extensions) {
7194
7277
  const fullPath = ext ? `${candidate}${ext}` : candidate;
@@ -7197,7 +7280,7 @@ var program = async () => {
7197
7280
  }
7198
7281
  }
7199
7282
  for (const ext of extensions) {
7200
- const fullPath = path9.join(candidate, `index${ext}`);
7283
+ const fullPath = path10.join(candidate, `index${ext}`);
7201
7284
  if (fsSync3.existsSync(fullPath)) {
7202
7285
  return fullPath;
7203
7286
  }
@@ -7316,8 +7399,8 @@ var program = async () => {
7316
7399
  }
7317
7400
  }
7318
7401
  const jestDecision = decideShouldRunJest([], effectiveJestFiles, {
7319
- selectionSpecified,
7320
- selectionPaths
7402
+ selectionSpecified: selectionSpecifiedAugmented,
7403
+ selectionPaths: selectionPathsAugmented
7321
7404
  });
7322
7405
  const { shouldRunJest } = jestDecision;
7323
7406
  const jestCount = effectiveJestFiles.length;
@@ -7335,45 +7418,63 @@ var program = async () => {
7335
7418
  }
7336
7419
  console.info(`Run plan \u2192 Jest maybe=${shouldRunJest} (projects=${projectConfigs.length})`);
7337
7420
  let jestExitCode = 0;
7421
+ const allBridgeJson = [];
7338
7422
  const executedTestFilesSet = /* @__PURE__ */ new Set();
7339
7423
  if (shouldRunJest) {
7340
7424
  console.info("Starting Jest (no Vitest targets)\u2026");
7341
7425
  await runJestBootstrap();
7342
7426
  const jestRunArgs = selectionIncludesProdPaths ? stripPathTokens(jestArgs) : jestArgs;
7427
+ const sanitizedJestRunArgs = jestRunArgs.filter(
7428
+ (arg) => !/^--coverageDirectory(?:=|$)/.test(String(arg))
7429
+ );
7343
7430
  const projectsToRun = projectConfigs.filter(
7344
7431
  (cfg) => (perProjectFiltered.get(cfg) ?? []).length > 0
7345
7432
  );
7346
- const totalProjectsToRun = projectsToRun.length;
7347
7433
  const stripFooter = (text) => {
7348
7434
  const lines = text.split("\n");
7349
7435
  const idx = lines.findIndex((ln) => /^Test Files\s/.test(stripAnsiSimple(ln)));
7350
7436
  return idx >= 0 ? lines.slice(0, idx).join("\n").trimEnd() : text;
7351
7437
  };
7438
+ const prodSeedsForRun = (() => {
7439
+ const changedAbs = (changedSelectionAbs ?? []).map(
7440
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7441
+ );
7442
+ const selAbs = selectionPathsAugmented.map(
7443
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7444
+ );
7445
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7446
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7447
+ );
7448
+ })();
7449
+ const repoRootForRank = repoRootForDiscovery;
7450
+ const fileRank = await computeDirectnessRank({
7451
+ repoRoot: repoRootForRank,
7452
+ productionSeeds: prodSeedsForRun
7453
+ });
7352
7454
  for (let projIndex = 0; projIndex < projectsToRun.length; projIndex += 1) {
7353
7455
  const cfg = projectsToRun[projIndex];
7354
- const isLastProject = projIndex === totalProjectsToRun - 1;
7355
7456
  const files = perProjectFiltered.get(cfg) ?? [];
7356
7457
  if (files.length === 0) {
7357
- console.info(`Project ${path9.basename(cfg)}: 0 matching tests after filter; skipping.`);
7458
+ console.info(`Project ${path10.basename(cfg)}: 0 matching tests after filter; skipping.`);
7358
7459
  continue;
7359
7460
  }
7360
7461
  files.forEach(
7361
- (absTestPath) => executedTestFilesSet.add(path9.resolve(absTestPath).replace(/\\/g, "/"))
7462
+ (absTestPath) => executedTestFilesSet.add(path10.resolve(absTestPath).replace(/\\/g, "/"))
7362
7463
  );
7363
- const outJson = path9.join(
7464
+ const outJson = path10.join(
7364
7465
  os2.tmpdir(),
7365
7466
  `jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
7366
7467
  );
7367
- const reporterPath = path9.resolve("scripts/jest-vitest-bridge.cjs");
7468
+ const reporterPath = path10.resolve("scripts/jest-vitest-bridge.cjs");
7368
7469
  try {
7369
7470
  if (!fsSync3.existsSync(reporterPath)) {
7370
- fsSync3.mkdirSync(path9.dirname(reporterPath), { recursive: true });
7471
+ fsSync3.mkdirSync(path10.dirname(reporterPath), { recursive: true });
7371
7472
  fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
7372
7473
  }
7373
7474
  } catch (ensureReporterError) {
7374
7475
  console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
7375
7476
  }
7376
- const selectedFilesForCoverage = selectionPaths.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path9.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
7477
+ const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path10.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
7377
7478
  const coverageFromArgs = [];
7378
7479
  for (const relPath of selectedFilesForCoverage) {
7379
7480
  coverageFromArgs.push("--collectCoverageFrom", relPath);
@@ -7387,14 +7488,17 @@ var program = async () => {
7387
7488
  "--config",
7388
7489
  cfg,
7389
7490
  "--runTestsByPath",
7390
- "--reporters",
7391
- reporterPath,
7491
+ `--reporters=${reporterPath}`,
7392
7492
  "--silent",
7393
7493
  "--colors",
7394
7494
  "--json",
7395
7495
  "--outputFile",
7396
7496
  outJson,
7397
- ...jestRunArgs,
7497
+ ...sanitizedJestRunArgs,
7498
+ ...collectCoverage ? [
7499
+ "--coverageDirectory",
7500
+ path10.join("coverage", "jest", path10.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
7501
+ ] : [],
7398
7502
  ...coverageFromArgs,
7399
7503
  "--passWithNoTests",
7400
7504
  ...files
@@ -7420,10 +7524,24 @@ var program = async () => {
7420
7524
  const jsonText = fsSync3.readFileSync(outJson, "utf8");
7421
7525
  const parsed = JSON.parse(jsonText);
7422
7526
  const bridge = coerceJestJsonToBridge(parsed);
7423
- pretty = renderVitestFromJestJSON(bridge, {
7424
- cwd: repoRootForDiscovery,
7425
- ...editorCmd !== void 0 ? { editorCmd } : {}
7426
- });
7527
+ allBridgeJson.push(bridge);
7528
+ try {
7529
+ const reordered = {
7530
+ ...bridge,
7531
+ testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
7532
+ };
7533
+ pretty = renderVitestFromJestJSON(reordered, {
7534
+ cwd: repoRootForDiscovery,
7535
+ ...editorCmd !== void 0 ? { editorCmd } : {},
7536
+ onlyFailures
7537
+ });
7538
+ } catch {
7539
+ pretty = renderVitestFromJestJSON(bridge, {
7540
+ cwd: repoRootForDiscovery,
7541
+ ...editorCmd !== void 0 ? { editorCmd } : {},
7542
+ onlyFailures
7543
+ });
7544
+ }
7427
7545
  if (debug) {
7428
7546
  const preview = pretty.split("\n").slice(0, 3).join("\n");
7429
7547
  console.info(`pretty preview (json):
@@ -7438,7 +7556,8 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7438
7556
  }
7439
7557
  const renderOpts = {
7440
7558
  cwd: repoRootForDiscovery,
7441
- ...editorCmd !== void 0 ? { editorCmd } : {}
7559
+ ...editorCmd !== void 0 ? { editorCmd } : {},
7560
+ onlyFailures
7442
7561
  };
7443
7562
  pretty = formatJestOutputVitest(output, renderOpts);
7444
7563
  if (debug) {
@@ -7447,9 +7566,7 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7447
7566
  ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7448
7567
  }
7449
7568
  }
7450
- if (!isLastProject) {
7451
- pretty = stripFooter(pretty);
7452
- }
7569
+ pretty = stripFooter(pretty);
7453
7570
  if (pretty.trim().length > 0) {
7454
7571
  process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
7455
7572
  `);
@@ -7461,15 +7578,70 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7461
7578
  } else {
7462
7579
  console.info("Jest run skipped based on selection and thresholds.");
7463
7580
  }
7464
- if (collectCoverage && shouldRunJest && coverageAbortOnFailure && jestExitCode !== 0) {
7465
- process.exit(jestExitCode);
7581
+ if (allBridgeJson.length > 0) {
7582
+ const agg = allBridgeJson.map((bridge) => bridge.aggregated);
7583
+ const sum = (select) => agg.reduce((total, item) => total + (select(item) || 0), 0);
7584
+ const startTime = Math.min(
7585
+ ...allBridgeJson.map((bridge) => Number(bridge.startTime || Date.now()))
7586
+ );
7587
+ const unified = {
7588
+ startTime,
7589
+ testResults: allBridgeJson.flatMap((bridge) => bridge.testResults),
7590
+ aggregated: {
7591
+ numTotalTestSuites: sum((item) => item.numTotalTestSuites),
7592
+ numPassedTestSuites: sum((item) => item.numPassedTestSuites),
7593
+ numFailedTestSuites: sum((item) => item.numFailedTestSuites),
7594
+ numTotalTests: sum((item) => item.numTotalTests),
7595
+ numPassedTests: sum((item) => item.numPassedTests),
7596
+ numFailedTests: sum((item) => item.numFailedTests),
7597
+ numPendingTests: sum((item) => item.numPendingTests),
7598
+ numTodoTests: sum((item) => item.numTodoTests),
7599
+ startTime,
7600
+ success: agg.every((item) => Boolean(item.success)),
7601
+ runTimeMs: sum((item) => Number(item.runTimeMs ?? 0))
7602
+ }
7603
+ };
7604
+ try {
7605
+ const prodSeeds = (() => {
7606
+ const changedAbs = (changedSelectionAbs ?? []).map(
7607
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7608
+ );
7609
+ const selAbs = selectionPathsAugmented.map(
7610
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7611
+ );
7612
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7613
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7614
+ );
7615
+ })();
7616
+ const unifiedRank = await computeDirectnessRank({
7617
+ repoRoot: repoRootForDiscovery,
7618
+ productionSeeds: prodSeeds
7619
+ });
7620
+ const ordered = sortTestResultsWithRank(unifiedRank, unified.testResults).reverse();
7621
+ unified.testResults = ordered;
7622
+ } catch {
7623
+ }
7624
+ const text = renderVitestFromJestJSON(unified, {
7625
+ cwd: repoRootForDiscovery,
7626
+ ...editorCmd !== void 0 ? { editorCmd } : {},
7627
+ onlyFailures
7628
+ });
7629
+ if (text.trim().length > 0) {
7630
+ process.stdout.write(text.endsWith("\n") ? text : `${text}
7631
+ `);
7632
+ }
7633
+ }
7634
+ const finalExitCode = jestExitCode;
7635
+ if (collectCoverage && shouldRunJest && coverageAbortOnFailure && finalExitCode !== 0) {
7636
+ process.exit(finalExitCode);
7637
+ return;
7466
7638
  }
7467
7639
  if (collectCoverage && shouldRunJest) {
7468
7640
  await mergeLcov();
7469
7641
  const repoRoot = workspaceRoot ?? await findRepoRoot();
7470
7642
  const mergedOptsBase = {
7471
- selectionSpecified,
7472
- selectionPaths,
7643
+ selectionSpecified: selectionSpecifiedAugmented,
7644
+ selectionPaths: selectionPathsAugmented,
7473
7645
  includeGlobs,
7474
7646
  excludeGlobs,
7475
7647
  workspaceRoot: repoRoot,
@@ -7484,7 +7656,6 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7484
7656
  };
7485
7657
  await emitMergedCoverage(coverageUi, mergedOptsBase);
7486
7658
  }
7487
- const finalExitCode = jestExitCode;
7488
7659
  process.exit(finalExitCode);
7489
7660
  };
7490
7661
  export {