headlamp 0.1.5 → 0.1.8

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
@@ -67,7 +67,8 @@ var init_args = __esm({
67
67
  coverageMode: (value) => ({ type: "coverageMode", value }),
68
68
  coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
69
69
  coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
70
- coveragePageFit: (value) => ({ type: "coveragePageFit", value })
70
+ coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
71
+ changed: (value) => ({ type: "changed", value })
71
72
  };
72
73
  Some = (value) => ({ _tag: "some", value });
73
74
  None = { _tag: "none" };
@@ -264,6 +265,18 @@ var init_args = __esm({
264
265
  "--coverage.root=",
265
266
  (value) => step([ActionBuilders.coverageRoot((value.split("=")[1] ?? "").trim())])
266
267
  ),
268
+ // --changed flag: selects changed files via git (all|staged|unstaged)
269
+ rule.eq("--changed", () => step([ActionBuilders.changed("all")])),
270
+ rule.startsWith("--changed=", (value) => {
271
+ const raw = (value.split("=")[1] ?? "").trim().toLowerCase();
272
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
273
+ return step([ActionBuilders.changed(mode)]);
274
+ }),
275
+ rule.withLookahead("--changed", (_flag, lookahead) => {
276
+ const raw = String(lookahead).trim().toLowerCase();
277
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
278
+ return step([ActionBuilders.changed(mode)], true);
279
+ }),
267
280
  rule.withLookahead(
268
281
  "-t",
269
282
  (flag, lookahead) => step(
@@ -366,6 +379,8 @@ var init_args = __esm({
366
379
  return { vitest: [], jest: [], coverage: false, coverageMaxHotspots: action.value };
367
380
  case "coveragePageFit":
368
381
  return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
382
+ case "changed":
383
+ return { vitest: [], jest: [], coverage: false, changed: action.value };
369
384
  case "jestArg":
370
385
  return { vitest: [], jest: [action.value], coverage: false };
371
386
  case "vitestArg":
@@ -405,6 +420,7 @@ var init_args = __esm({
405
420
  }
406
421
  return {
407
422
  ...next,
423
+ ...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
408
424
  ...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
409
425
  ...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
410
426
  ...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
@@ -490,7 +506,8 @@ var init_args = __esm({
490
506
  ...coverageMaxHotspotsLocal !== void 0 ? { coverageMaxHotspots: coverageMaxHotspotsLocal } : {},
491
507
  coveragePageFit,
492
508
  ...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
493
- ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {}
509
+ ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
510
+ ...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
494
511
  };
495
512
  return out;
496
513
  };
@@ -557,7 +574,7 @@ var init_TimeoutError = __esm({
557
574
 
558
575
  // node_modules/es-toolkit/dist/promise/delay.mjs
559
576
  function delay(ms, { signal } = {}) {
560
- return new Promise((resolve8, reject) => {
577
+ return new Promise((resolve9, reject) => {
561
578
  const abortError = () => {
562
579
  reject(new AbortError());
563
580
  };
@@ -570,7 +587,7 @@ function delay(ms, { signal } = {}) {
570
587
  }
571
588
  const timeoutId = setTimeout(() => {
572
589
  signal?.removeEventListener("abort", abortHandler);
573
- resolve8();
590
+ resolve9();
574
591
  }, ms);
575
592
  signal?.addEventListener("abort", abortHandler, { once: true });
576
593
  });
@@ -635,11 +652,11 @@ var init_exec = __esm({
635
652
  child.stderr?.on("data", (chunk) => {
636
653
  stderr += String(chunk);
637
654
  });
638
- const exec = new Promise((resolve8, reject) => {
655
+ const exec = new Promise((resolve9, reject) => {
639
656
  child.on("error", reject);
640
657
  child.on(
641
658
  "close",
642
- (code) => Number(code) === 0 ? resolve8(stdout) : reject(new Error(stderr || `exit ${code}`))
659
+ (code) => Number(code) === 0 ? resolve9(stdout) : reject(new Error(stderr || `exit ${code}`))
643
660
  );
644
661
  });
645
662
  try {
@@ -659,7 +676,7 @@ var init_exec = __esm({
659
676
  throw caughtError;
660
677
  }
661
678
  };
662
- runExitCode = async (cmd, args, opts = {}) => new Promise((resolve8, reject) => {
679
+ runExitCode = async (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
663
680
  const child = spawn(cmd, [...args], {
664
681
  cwd: opts.cwd,
665
682
  env: opts.env,
@@ -668,9 +685,9 @@ var init_exec = __esm({
668
685
  windowsHide: true
669
686
  });
670
687
  child.on("error", reject);
671
- child.on("close", (code) => resolve8(Number(code)));
688
+ child.on("close", (code) => resolve9(Number(code)));
672
689
  });
673
- runWithCapture = async (cmd, args, opts) => new Promise((resolve8, reject) => {
690
+ runWithCapture = async (cmd, args, opts) => new Promise((resolve9, reject) => {
674
691
  const child = spawn(cmd, [...args], {
675
692
  cwd: opts.cwd,
676
693
  env: opts.env,
@@ -686,7 +703,7 @@ var init_exec = __esm({
686
703
  buf += String(chunk);
687
704
  });
688
705
  child.on("error", reject);
689
- child.on("close", (code) => resolve8({ code: Number(code), output: buf }));
706
+ child.on("close", (code) => resolve9({ code: Number(code), output: buf }));
690
707
  });
691
708
  }
692
709
  });
@@ -1097,8 +1114,8 @@ var require_utils = __commonJS({
1097
1114
  }
1098
1115
  return output;
1099
1116
  };
1100
- exports.basename = (path10, { windows } = {}) => {
1101
- const segs = path10.split(windows ? /[\\/]/ : "/");
1117
+ exports.basename = (path11, { windows } = {}) => {
1118
+ const segs = path11.split(windows ? /[\\/]/ : "/");
1102
1119
  const last = segs[segs.length - 1];
1103
1120
  if (last === "") {
1104
1121
  return segs[segs.length - 2];
@@ -2168,7 +2185,7 @@ var require_parse = __commonJS({
2168
2185
  if (opts2.noglobstar === true) return star;
2169
2186
  return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
2170
2187
  };
2171
- const create = (str) => {
2188
+ const create2 = (str) => {
2172
2189
  switch (str) {
2173
2190
  case "*":
2174
2191
  return `${nodot}${ONE_CHAR}${star}`;
@@ -2189,14 +2206,14 @@ var require_parse = __commonJS({
2189
2206
  default: {
2190
2207
  const match = /^(.*?)\.(\w+)$/.exec(str);
2191
2208
  if (!match) return;
2192
- const source2 = create(match[1]);
2209
+ const source2 = create2(match[1]);
2193
2210
  if (!source2) return;
2194
2211
  return source2 + DOT_LITERAL + match[2];
2195
2212
  }
2196
2213
  }
2197
2214
  };
2198
2215
  const output = utils.removePrefix(input, state);
2199
- let source = create(output);
2216
+ let source = create2(output);
2200
2217
  if (source && opts.strictSlashes !== true) {
2201
2218
  source += `${SLASH_LITERAL}?`;
2202
2219
  }
@@ -4688,8 +4705,75 @@ var compositeBarPct = (summary, hotspots) => {
4688
4705
  // src/lib/coverage-print.ts
4689
4706
  init_env_utils();
4690
4707
  init_exec();
4691
- import * as path7 from "node:path";
4708
+ import * as path8 from "node:path";
4692
4709
  import * as fsSync2 from "node:fs";
4710
+
4711
+ // src/lib/relevance.ts
4712
+ init_args();
4713
+ init_fast_related();
4714
+ import * as path7 from "node:path";
4715
+ var normalizeAbs = (inputPath) => path7.resolve(inputPath).replace(/\\/g, "/");
4716
+ var compareBooleanDesc = (left, right) => {
4717
+ if (left === right) {
4718
+ return 0;
4719
+ }
4720
+ return right ? 1 : -1;
4721
+ };
4722
+ var compareNumberAsc = (left, right) => left - right;
4723
+ var compareStringAsc = (left, right) => left.localeCompare(right);
4724
+ var fileFailed = (file) => Boolean(
4725
+ (file.status ?? "") === "failed" || (file.testResults ?? []).some((assertion) => (assertion.status ?? "") === "failed")
4726
+ );
4727
+ var composeComparators = (...comparators) => (left, right) => {
4728
+ for (const cmp of comparators) {
4729
+ const result = cmp(left, right);
4730
+ if (result !== 0) {
4731
+ return result;
4732
+ }
4733
+ }
4734
+ return 0;
4735
+ };
4736
+ var comparatorForRank = (rankByPath) => {
4737
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4738
+ return composeComparators(
4739
+ (left, right) => compareBooleanDesc(fileFailed(left), fileFailed(right)),
4740
+ (left, right) => compareNumberAsc(
4741
+ rankOrInf(normalizeAbs(left.testFilePath)),
4742
+ rankOrInf(normalizeAbs(right.testFilePath))
4743
+ ),
4744
+ (left, right) => compareStringAsc(normalizeAbs(left.testFilePath), normalizeAbs(right.testFilePath))
4745
+ );
4746
+ };
4747
+ var computeDirectnessRank = async (opts) => {
4748
+ const selectionKey = opts.productionSeeds.map((abs) => path7.relative(opts.repoRoot, abs).replace(/\\/g, "/")).sort((left, right) => left.localeCompare(right)).join("|");
4749
+ const related = await cachedRelated({
4750
+ repoRoot: opts.repoRoot,
4751
+ selectionKey,
4752
+ compute: () => findRelatedTestsFast({
4753
+ repoRoot: opts.repoRoot,
4754
+ productionPaths: opts.productionSeeds,
4755
+ testGlobs: DEFAULT_TEST_GLOBS,
4756
+ excludeGlobs: opts.excludeGlobs ?? DEFAULT_EXCLUDE,
4757
+ timeoutMs: 1500
4758
+ })
4759
+ });
4760
+ const out = /* @__PURE__ */ new Map();
4761
+ related.forEach((abs, index) => {
4762
+ out.set(normalizeAbs(abs), index);
4763
+ });
4764
+ return out;
4765
+ };
4766
+ var sortTestResultsWithRank = (rankByPath, results) => results.slice().sort(comparatorForRank(rankByPath));
4767
+ var comparatorForPathRank = (rankByPath) => {
4768
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4769
+ return composeComparators(
4770
+ (left, right) => compareNumberAsc(rankOrInf(normalizeAbs(left)), rankOrInf(normalizeAbs(right))),
4771
+ (left, right) => compareStringAsc(normalizeAbs(left), normalizeAbs(right))
4772
+ );
4773
+ };
4774
+ var sortPathsWithRank = (rankByPath, paths) => paths.slice().sort(comparatorForPathRank(rankByPath));
4775
+
4776
+ // src/lib/coverage-print.ts
4693
4777
  var printDetailedCoverage = async (opts) => {
4694
4778
  const files = opts.map.files().sort((fileA, fileB) => {
4695
4779
  const summaryA = opts.map.fileCoverageFor(fileA).toSummary();
@@ -4699,7 +4783,7 @@ var printDetailedCoverage = async (opts) => {
4699
4783
  for (const abs of files) {
4700
4784
  const fc = opts.map.fileCoverageFor(abs);
4701
4785
  const sum = fc.toSummary();
4702
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4786
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4703
4787
  const blocks = computeUncoveredBlocks(fc);
4704
4788
  const misses = missedBranches(fc);
4705
4789
  const missFns = missedFunctions(fc);
@@ -4708,9 +4792,9 @@ var printDetailedCoverage = async (opts) => {
4708
4792
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4709
4793
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4710
4794
  linesPctText
4711
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4712
- sum.functions.pct
4713
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4795
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4796
+ funcsPctText
4797
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4714
4798
  console.info(header);
4715
4799
  const max = opts.limitPerFile === "all" ? Number.POSITIVE_INFINITY : opts.limitPerFile ?? 5;
4716
4800
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
@@ -4737,10 +4821,8 @@ var printDetailedCoverage = async (opts) => {
4737
4821
  abs,
4738
4822
  block.start,
4739
4823
  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}`;
4824
+ )}\x07${path8.basename(abs)}:${block.start}\x1B]8;;\x07`;
4825
+ const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(`L${block.end}`)} ${link}`;
4744
4826
  console.info(label);
4745
4827
  if (opts.showCode && src.length) {
4746
4828
  const lines = src.split(/\r?\n/);
@@ -4760,7 +4842,7 @@ var printDetailedCoverage = async (opts) => {
4760
4842
  abs,
4761
4843
  fn.line,
4762
4844
  opts.editorCmd
4763
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4845
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4764
4846
  console.info(` - ${fn.name} @ ${link}`);
4765
4847
  }
4766
4848
  }
@@ -4771,12 +4853,8 @@ var printDetailedCoverage = async (opts) => {
4771
4853
  abs,
4772
4854
  br.line,
4773
4855
  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
- );
4856
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`;
4857
+ console.info(` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(", ")}]`);
4780
4858
  }
4781
4859
  }
4782
4860
  console.info("");
@@ -4796,7 +4874,7 @@ var printCompactCoverage = async (opts) => {
4796
4874
  for (const abs of files.slice(0, fileCap)) {
4797
4875
  const fc = opts.map.fileCoverageFor(abs);
4798
4876
  const sum = fc.toSummary();
4799
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4877
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4800
4878
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
4801
4879
  const secondLength = secondRange.end - secondRange.start;
4802
4880
  const firstLength = firstRange.end - firstRange.start;
@@ -4810,9 +4888,9 @@ var printCompactCoverage = async (opts) => {
4810
4888
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4811
4889
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4812
4890
  linesPctText
4813
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4814
- sum.functions.pct
4815
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4891
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4892
+ funcsPctText
4893
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4816
4894
  console.info(header);
4817
4895
  const hotspots = blocks.slice(0, maxHotspotsDerived);
4818
4896
  if (hotspots.length) {
@@ -4823,10 +4901,8 @@ var printCompactCoverage = async (opts) => {
4823
4901
  abs,
4824
4902
  hotspot.start,
4825
4903
  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
- );
4904
+ )}\x07${path8.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
4905
+ console.info(` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`);
4830
4906
  }
4831
4907
  }
4832
4908
  const functionsList = missFns.slice(0, maxFunctionsDerived);
@@ -4838,7 +4914,7 @@ var printCompactCoverage = async (opts) => {
4838
4914
  abs,
4839
4915
  fn.line,
4840
4916
  opts.editorCmd
4841
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`
4917
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`
4842
4918
  );
4843
4919
  }
4844
4920
  }
@@ -4853,7 +4929,7 @@ var printCompactCoverage = async (opts) => {
4853
4929
  abs,
4854
4930
  br.line,
4855
4931
  opts.editorCmd
4856
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`
4932
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`
4857
4933
  );
4858
4934
  }
4859
4935
  }
@@ -4862,9 +4938,7 @@ var printCompactCoverage = async (opts) => {
4862
4938
  const restBrs = Math.max(0, misses.length - branchesList.length);
4863
4939
  if (restHs + restFns + restBrs > 0) {
4864
4940
  console.info(
4865
- ansi.dim(
4866
- ` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`
4867
- )
4941
+ ansi.dim(` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`)
4868
4942
  );
4869
4943
  }
4870
4944
  console.info("");
@@ -4901,7 +4975,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4901
4975
  return { stem: base.slice(0, -ending.length), ext: ending };
4902
4976
  }
4903
4977
  }
4904
- const ext2 = path7.extname(base);
4978
+ const ext2 = path8.extname(base);
4905
4979
  return { stem: base.slice(0, -ext2.length), ext: ext2 };
4906
4980
  };
4907
4981
  const sliceBalanced = (input, width) => {
@@ -4984,12 +5058,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4984
5058
  const tailParts = tailSrc.map((segment) => segment);
4985
5059
  let hidAny = false;
4986
5060
  const build = () => {
4987
- const label2 = joinParts(
4988
- headParts,
4989
- tailParts,
4990
- hideMiddle2 || hidAny,
4991
- baseLabel
4992
- );
5061
+ const label2 = joinParts(headParts, tailParts, hideMiddle2 || hidAny, baseLabel);
4993
5062
  return { label: label2, width: visibleWidth(label2) };
4994
5063
  };
4995
5064
  let { label, width } = build();
@@ -5082,13 +5151,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5082
5151
  return { headRaw: headRaw2, tailRaw: tailRaw2, hideMiddle: hideMiddle2 };
5083
5152
  };
5084
5153
  let { headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tailCount);
5085
- let candidate = tryTrimDirsToFit(
5086
- headRaw,
5087
- tailRaw,
5088
- hideMiddle,
5089
- baseFull,
5090
- maxWidth
5091
- );
5154
+ let candidate = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5092
5155
  if (!candidate) {
5093
5156
  return baseFull;
5094
5157
  }
@@ -5097,13 +5160,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5097
5160
  if (headCount + tailCount < total) {
5098
5161
  const tryTail = Math.min(tailCount + 1, total - headCount);
5099
5162
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tryTail));
5100
- const candTail = tryTrimDirsToFit(
5101
- headRaw,
5102
- tailRaw,
5103
- hideMiddle,
5104
- baseFull,
5105
- maxWidth
5106
- );
5163
+ const candTail = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5107
5164
  if (candTail) {
5108
5165
  tailCount = tryTail;
5109
5166
  candidate = candTail;
@@ -5113,13 +5170,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5113
5170
  if (!advanced && headCount + tailCount < total) {
5114
5171
  const tryHead = Math.min(headCount + 1, total - tailCount);
5115
5172
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(tryHead, tailCount));
5116
- const candHead = tryTrimDirsToFit(
5117
- headRaw,
5118
- tailRaw,
5119
- hideMiddle,
5120
- baseFull,
5121
- maxWidth
5122
- );
5173
+ const candHead = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5123
5174
  if (candHead) {
5124
5175
  headCount = tryHead;
5125
5176
  candidate = candHead;
@@ -5181,7 +5232,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5181
5232
  const queue = [];
5182
5233
  const seen = /* @__PURE__ */ new Set();
5183
5234
  for (const testAbs of executedTestsAbs) {
5184
- const testPathNormalized = path7.resolve(testAbs).replace(/\\/g, "/");
5235
+ const testPathNormalized = path8.resolve(testAbs).replace(/\\/g, "/");
5185
5236
  dist.set(testPathNormalized, 0);
5186
5237
  queue.push([testPathNormalized, 0]);
5187
5238
  }
@@ -5200,12 +5251,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5200
5251
  const specs = await extractImportSpecs2(currentFile, specsCache);
5201
5252
  const nextDistance = currentDistance + 1;
5202
5253
  for (const spec of specs) {
5203
- const resolved = resolveImportWithRoot(
5204
- currentFile,
5205
- spec,
5206
- rootDir,
5207
- resolutionCache
5208
- );
5254
+ const resolved = resolveImportWithRoot(currentFile, spec, rootDir, resolutionCache);
5209
5255
  const usable = resolved && !resolved.includes("/node_modules/");
5210
5256
  if (usable) {
5211
5257
  const existing = dist.get(resolved);
@@ -5220,13 +5266,10 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5220
5266
  return dist;
5221
5267
  };
5222
5268
  var renderPerFileCompositeTable = async (opts) => {
5223
- const rel = path7.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5269
+ const rel = path8.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5224
5270
  const sum = opts.file.toSummary();
5225
5271
  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
- );
5272
+ const tableBudget = Math.max(14, Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8));
5230
5273
  const rowBudget = Math.max(6, tableBudget - 6);
5231
5274
  const blocks = computeUncoveredBlocks(opts.file).slice().sort((firstRange, secondRange) => {
5232
5275
  const firstLength = firstRange.end - firstRange.start;
@@ -5270,9 +5313,7 @@ var renderPerFileCompositeTable = async (opts) => {
5270
5313
  rows.push([
5271
5314
  cell(
5272
5315
  rel,
5273
- (padded) => ansi.dim(
5274
- shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length)
5275
- )
5316
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5276
5317
  ),
5277
5318
  cell("Totals", ansi.dim),
5278
5319
  cell("\u2014", ansi.dim),
@@ -5291,11 +5332,7 @@ var renderPerFileCompositeTable = async (opts) => {
5291
5332
  rows.push([
5292
5333
  cell(
5293
5334
  rel,
5294
- (padded) => ansi.dim(
5295
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5296
- padded.length
5297
- )
5298
- )
5335
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5299
5336
  ),
5300
5337
  cell("Hotspots", ansi.dim),
5301
5338
  cell("", ansi.dim),
@@ -5309,14 +5346,8 @@ var renderPerFileCompositeTable = async (opts) => {
5309
5346
  rows.push([
5310
5347
  cell(rel, (padded) => {
5311
5348
  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);
5349
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5350
+ return linkifyPadded(opts.absPath, hotspotRange.start, opts.editorCmd)(display);
5320
5351
  }),
5321
5352
  cell("Hotspot"),
5322
5353
  cell(`L${hotspotRange.start}\u2013L${hotspotRange.end}`),
@@ -5333,11 +5364,7 @@ var renderPerFileCompositeTable = async (opts) => {
5333
5364
  rows.push([
5334
5365
  cell(
5335
5366
  rel,
5336
- (padded) => ansi.dim(
5337
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5338
- padded.length
5339
- )
5340
- )
5367
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5341
5368
  ),
5342
5369
  cell("Functions", ansi.dim),
5343
5370
  cell("", ansi.dim),
@@ -5351,14 +5378,8 @@ var renderPerFileCompositeTable = async (opts) => {
5351
5378
  rows.push([
5352
5379
  cell(rel, (padded) => {
5353
5380
  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);
5381
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5382
+ return linkifyPadded(opts.absPath, missedFunction.line, opts.editorCmd)(display);
5362
5383
  }),
5363
5384
  cell("Func"),
5364
5385
  cell(`L${missedFunction.line}`),
@@ -5375,11 +5396,7 @@ var renderPerFileCompositeTable = async (opts) => {
5375
5396
  rows.push([
5376
5397
  cell(
5377
5398
  rel,
5378
- (padded) => ansi.dim(
5379
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5380
- padded.length
5381
- )
5382
- )
5399
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5383
5400
  ),
5384
5401
  cell("Branches", ansi.dim),
5385
5402
  cell("", ansi.dim),
@@ -5393,14 +5410,8 @@ var renderPerFileCompositeTable = async (opts) => {
5393
5410
  rows.push([
5394
5411
  cell(rel, (padded) => {
5395
5412
  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);
5413
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5414
+ return linkifyPadded(opts.absPath, missedBranch.line, opts.editorCmd)(display);
5404
5415
  }),
5405
5416
  cell("Branch"),
5406
5417
  cell(`L${missedBranch.line}`),
@@ -5408,9 +5419,7 @@ var renderPerFileCompositeTable = async (opts) => {
5408
5419
  cell(""),
5409
5420
  cell(""),
5410
5421
  cell(""),
5411
- cell(
5412
- `#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`
5413
- )
5422
+ cell(`#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`)
5414
5423
  ]);
5415
5424
  }
5416
5425
  }
@@ -5430,9 +5439,7 @@ var renderPerFileCompositeTable = async (opts) => {
5430
5439
  rows.push([
5431
5440
  cell(rel, (padded) => {
5432
5441
  const width = padded.length;
5433
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5434
- width
5435
- );
5442
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5436
5443
  return linkifyPadded(opts.absPath, ln, opts.editorCmd)(display);
5437
5444
  }),
5438
5445
  cell("Line"),
@@ -5445,16 +5452,7 @@ var renderPerFileCompositeTable = async (opts) => {
5445
5452
  ]);
5446
5453
  }
5447
5454
  while (rows.length < target) {
5448
- rows.push([
5449
- cell(""),
5450
- cell(""),
5451
- cell(""),
5452
- cell(""),
5453
- cell(""),
5454
- cell(""),
5455
- cell(""),
5456
- cell("")
5457
- ]);
5455
+ rows.push([cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell("")]);
5458
5456
  }
5459
5457
  }
5460
5458
  }
@@ -5462,20 +5460,17 @@ var renderPerFileCompositeTable = async (opts) => {
5462
5460
  console.info(table);
5463
5461
  const sep = ansi.gray(
5464
5462
  "\u2500".repeat(
5465
- Math.max(
5466
- 20,
5467
- typeof process.stdout.columns === "number" ? process.stdout.columns : 100
5468
- )
5463
+ Math.max(20, typeof process.stdout.columns === "number" ? process.stdout.columns : 100)
5469
5464
  )
5470
5465
  );
5471
5466
  console.info(sep);
5472
5467
  };
5473
5468
  var printPerFileCompositeTables = async (opts) => {
5474
5469
  const selectionAbs = (opts.selectionPaths ?? []).map(
5475
- (selPath) => path7.resolve(selPath).replace(/\\/g, "/")
5470
+ (selPath) => path8.resolve(selPath).replace(/\\/g, "/")
5476
5471
  );
5477
5472
  const changedAbs = (opts.changedFiles ?? []).map(
5478
- (chgPath) => path7.resolve(chgPath).replace(/\\/g, "/")
5473
+ (chgPath) => path8.resolve(chgPath).replace(/\\/g, "/")
5479
5474
  );
5480
5475
  const tokenizeForSimilarity = (filePathForTokens) => new Set(
5481
5476
  filePathForTokens.toLowerCase().replace(/[^a-z0-9/_\-.]/g, " ").split(/[/_.-]+/).filter(Boolean)
@@ -5491,15 +5486,15 @@ var printPerFileCompositeTables = async (opts) => {
5491
5486
  return intersectionCount / unionSize;
5492
5487
  };
5493
5488
  const isSameDirOrChild = (firstAbs, secondAbs) => {
5494
- const dirA = path7.dirname(firstAbs).replace(/\\/g, "/");
5495
- const dirB = path7.dirname(secondAbs).replace(/\\/g, "/");
5489
+ const dirA = path8.dirname(firstAbs).replace(/\\/g, "/");
5490
+ const dirB = path8.dirname(secondAbs).replace(/\\/g, "/");
5496
5491
  return dirA === dirB || dirB.startsWith(`${dirA}/`) || dirA.startsWith(`${dirB}/`);
5497
5492
  };
5498
5493
  const selectionTokens = selectionAbs.map(tokenizeForSimilarity);
5499
5494
  const changedTokens = changedAbs.map(tokenizeForSimilarity);
5500
- const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path7.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5495
+ const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path8.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5501
5496
  const testTokens = executedTestsAbs.map(tokenizeForSimilarity);
5502
- const allMapFilesAbs = opts.map.files().map((absPath) => path7.resolve(absPath).replace(/\\/g, "/"));
5497
+ const allMapFilesAbs = opts.map.files().map((absPath) => path8.resolve(absPath).replace(/\\/g, "/"));
5503
5498
  const uncoveredCandidates = allMapFilesAbs.filter((absPath) => {
5504
5499
  const sum = opts.map.fileCoverageFor(absPath).toSummary();
5505
5500
  return !(sum.lines.pct >= 100 && sum.functions.pct >= 100 && sum.branches.pct >= 100);
@@ -5516,33 +5511,24 @@ var printPerFileCompositeTables = async (opts) => {
5516
5511
  const selectionSetAbs = new Set(selectionAbs);
5517
5512
  const scored = await Promise.all(
5518
5513
  candidates.map(async (abs) => {
5519
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
5514
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
5520
5515
  const sum = opts.map.fileCoverageFor(abs).toSummary();
5521
5516
  const pct = Number.isFinite(sum.lines.pct) ? sum.lines.pct : 0;
5522
- const absNorm = path7.resolve(abs).replace(/\\/g, "/");
5517
+ const absNorm = path8.resolve(abs).replace(/\\/g, "/");
5523
5518
  const selfTokens = tokenizeForSimilarity(absNorm);
5524
5519
  const selSim = Math.max(
5525
5520
  0,
5526
- ...selectionTokens.map(
5527
- (selectionTokenSet) => jaccard(selfTokens, selectionTokenSet)
5528
- )
5521
+ ...selectionTokens.map((selectionTokenSet) => jaccard(selfTokens, selectionTokenSet))
5529
5522
  );
5530
5523
  const chgSim = Math.max(
5531
5524
  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))
5525
+ ...changedTokens.map((changedTokenSet) => jaccard(selfTokens, changedTokenSet))
5539
5526
  );
5527
+ const tstSim = Math.max(0, ...testTokens.map((tset) => jaccard(selfTokens, tset)));
5540
5528
  const nearSelection = selectionAbs.some(
5541
5529
  (selectionPath) => isSameDirOrChild(absNorm, selectionPath)
5542
5530
  );
5543
- const nearChanged = changedAbs.some(
5544
- (changedPath) => isSameDirOrChild(absNorm, changedPath)
5545
- );
5531
+ const nearChanged = changedAbs.some((changedPath) => isSameDirOrChild(absNorm, changedPath));
5546
5532
  const related = selSim > 0 || chgSim > 0 || nearSelection || nearChanged;
5547
5533
  const distance = selectionSetAbs.has(absNorm) ? 0 : distFromTests.get(absNorm) ?? INF;
5548
5534
  let group = 6;
@@ -5566,9 +5552,12 @@ var printPerFileCompositeTables = async (opts) => {
5566
5552
  return { abs: absNorm, rel, linesPct: pct, group, score, distance };
5567
5553
  })
5568
5554
  );
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);
5555
+ const prodSeeds = selectionAbs.length > 0 ? selectionAbs : changedAbs;
5556
+ const rank = await computeDirectnessRank({ repoRoot: opts.root, productionSeeds: prodSeeds });
5557
+ let files = sortPathsWithRank(
5558
+ rank,
5559
+ scored.map((s) => s.abs)
5560
+ );
5572
5561
  if (selectionAbs.length > 0) {
5573
5562
  const selectionSet = new Set(selectionAbs);
5574
5563
  const selectedHead = files.filter((filePath) => selectionSet.has(filePath));
@@ -5592,7 +5581,7 @@ var printPerFileCompositeTables = async (opts) => {
5592
5581
 
5593
5582
  // src/lib/jest-bridge.ts
5594
5583
  var import_json5 = __toESM(require_lib(), 1);
5595
- import * as path8 from "node:path";
5584
+ import * as path9 from "node:path";
5596
5585
  import * as fs4 from "node:fs";
5597
5586
  import * as util from "node:util";
5598
5587
  var extractBridgePath = (raw, cwd) => {
@@ -5603,7 +5592,7 @@ var extractBridgePath = (raw, cwd) => {
5603
5592
  return null;
5604
5593
  }
5605
5594
  const jsonPath = matches[matches.length - 1][1].trim().replace(/^["'`]|["'`]$/g, "");
5606
- return path8.isAbsolute(jsonPath) ? jsonPath : path8.resolve(cwd, jsonPath);
5595
+ return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
5607
5596
  };
5608
5597
  var drawRule = (label) => {
5609
5598
  const width = Math.max(
@@ -6251,7 +6240,7 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
6251
6240
  const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
6252
6241
  if (loc) {
6253
6242
  const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
6254
- out.push(` ${ansi.dim("at")} ${osc8(`${path8.basename(loc.file)}:${loc.line}`, href)}`);
6243
+ out.push(` ${ansi.dim("at")} ${osc8(`${path9.basename(loc.file)}:${loc.line}`, href)}`);
6255
6244
  }
6256
6245
  out.push("");
6257
6246
  return out;
@@ -6530,7 +6519,7 @@ function renderVitestFromJestJSON(data, opts) {
6530
6519
  const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
6531
6520
  const locLink = deepestLoc && (() => {
6532
6521
  const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, opts?.editorCmd);
6533
- const base = `${path8.basename(deepestLoc.file)}:${deepestLoc.line}`;
6522
+ const base = `${path9.basename(deepestLoc.file)}:${deepestLoc.line}`;
6534
6523
  return osc8(base, href);
6535
6524
  })();
6536
6525
  const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
@@ -6578,20 +6567,15 @@ ${footer}`;
6578
6567
  init_env_utils();
6579
6568
  init_exec();
6580
6569
  init_args();
6581
- import * as path9 from "node:path";
6570
+ import * as path10 from "node:path";
6582
6571
  import * as os2 from "node:os";
6583
6572
  import * as fsSync3 from "node:fs";
6584
- import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
6585
6573
  import * as fs5 from "node:fs/promises";
6586
6574
  import * as LibReport from "istanbul-lib-report";
6587
- import textReport from "istanbul-reports/lib/text";
6588
- import textSummaryReport from "istanbul-reports/lib/text-summary";
6575
+ import * as Reports from "istanbul-reports";
6576
+ import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
6589
6577
  var jestBin = "./node_modules/.bin/jest";
6590
6578
  var babelNodeBin = "./node_modules/.bin/babel-node";
6591
- var moduleSpecifierForRequire = (
6592
- // @ts-ignore
6593
- typeof __filename !== "undefined" ? __filename : import.meta.url
6594
- );
6595
6579
  var registerSignalHandlersOnce = () => {
6596
6580
  let handled = false;
6597
6581
  const on = (sig) => {
@@ -6609,7 +6593,6 @@ Received ${sig}, exiting...
6609
6593
  };
6610
6594
  var isDebug = () => Boolean(process.env.TEST_CLI_DEBUG);
6611
6595
  var mergeLcov = async () => {
6612
- const jestLcovPath = "coverage/jest/lcov.info";
6613
6596
  const vitestLcovPath = "coverage/vitest/lcov.info";
6614
6597
  const mergedOutPath = "coverage/lcov.info";
6615
6598
  const readOrEmpty = async (filePath) => {
@@ -6620,7 +6603,7 @@ var mergeLcov = async () => {
6620
6603
  }
6621
6604
  };
6622
6605
  let vitestContent = "";
6623
- let jestContent = "";
6606
+ const jestParts = [];
6624
6607
  try {
6625
6608
  vitestContent = await readOrEmpty(vitestLcovPath);
6626
6609
  } catch (readVitestError) {
@@ -6628,20 +6611,46 @@ var mergeLcov = async () => {
6628
6611
  console.info(`read vitest lcov failed: ${String(readVitestError)}`);
6629
6612
  }
6630
6613
  }
6614
+ const collectLcovs = (dir) => {
6615
+ const out = [];
6616
+ try {
6617
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6618
+ for (const entry of entries) {
6619
+ const full = path10.join(dir, entry.name);
6620
+ if (entry.isDirectory()) {
6621
+ out.push(...collectLcovs(full));
6622
+ } else if (entry.isFile() && entry.name === "lcov.info") {
6623
+ out.push(full);
6624
+ }
6625
+ }
6626
+ } catch {
6627
+ }
6628
+ return out;
6629
+ };
6631
6630
  try {
6632
- jestContent = await readOrEmpty(jestLcovPath);
6631
+ const jestRoot = path10.join("coverage", "jest");
6632
+ const candidates = [path10.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
6633
+ for (const filePath of candidates) {
6634
+ try {
6635
+ const content = await readOrEmpty(filePath);
6636
+ if (content.trim()) {
6637
+ jestParts.push(content.trim());
6638
+ }
6639
+ } catch {
6640
+ }
6641
+ }
6633
6642
  } catch (readJestError) {
6634
6643
  if (isDebug()) {
6635
- console.info(`read jest lcov failed: ${String(readJestError)}`);
6644
+ console.info(`scan jest lcov failed: ${String(readJestError)}`);
6636
6645
  }
6637
6646
  }
6638
- if (!vitestContent && !jestContent) {
6647
+ if (!vitestContent && jestParts.length === 0) {
6639
6648
  if (isDebug()) {
6640
6649
  console.info("No coverage outputs found to merge.");
6641
6650
  }
6642
6651
  return;
6643
6652
  }
6644
- const merged = [vitestContent.trim(), jestContent.trim()].filter(Boolean).join("\n");
6653
+ const merged = [vitestContent.trim(), ...jestParts].filter(Boolean).join("\n");
6645
6654
  if (merged.length > 0) {
6646
6655
  await (await import("node:fs/promises")).mkdir("coverage", { recursive: true });
6647
6656
  await (await import("node:fs/promises")).writeFile(mergedOutPath, `${merged}
@@ -6654,23 +6663,40 @@ var mergeLcov = async () => {
6654
6663
  }
6655
6664
  };
6656
6665
  var emitMergedCoverage = async (ui, opts) => {
6657
- const jestJson = path9.join("coverage", "jest", "coverage-final.json");
6658
- const jSize = fsSync3.existsSync(jestJson) ? fsSync3.statSync(jestJson).size : -1;
6659
- const jestSizeLabel = jSize >= 0 ? `${jSize} bytes` : "missing";
6660
- if (isDebug()) {
6661
- console.info(`Coverage JSON probe \u2192 jest: ${jestSizeLabel}`);
6662
- }
6663
- const jestData = await readCoverageJson(jestJson);
6664
- const jestFilesCount = Object.keys(jestData).length;
6665
- if (isDebug()) {
6666
- console.info(`Decoded coverage entries \u2192 jest: ${jestFilesCount}`);
6667
- }
6668
6666
  const map = createCoverageMap2({});
6669
- if (jestFilesCount > 0) {
6667
+ const listJsons = (dir) => {
6668
+ const out = [];
6670
6669
  try {
6671
- map.merge(jestData);
6670
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6671
+ for (const entry of entries) {
6672
+ const full = path10.join(dir, entry.name);
6673
+ if (entry.isDirectory()) {
6674
+ out.push(...listJsons(full));
6675
+ } else if (entry.isFile() && entry.name === "coverage-final.json") {
6676
+ out.push(full);
6677
+ }
6678
+ }
6679
+ } catch {
6680
+ }
6681
+ return out;
6682
+ };
6683
+ const coverageRoot = path10.join("coverage", "jest");
6684
+ const jsonCandidates = [
6685
+ path10.join(coverageRoot, "coverage-final.json"),
6686
+ ...listJsons(coverageRoot)
6687
+ ].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => {
6688
+ const isFirst = arr.indexOf(absolutePath) === index;
6689
+ const exists = fsSync3.existsSync(absolutePath);
6690
+ return isFirst && exists;
6691
+ });
6692
+ for (const jsonPath of jsonCandidates) {
6693
+ try {
6694
+ const data = await readCoverageJson(jsonPath);
6695
+ if (Object.keys(data).length) {
6696
+ map.merge(data);
6697
+ }
6672
6698
  } catch (mergeJestError) {
6673
- console.warn(`Failed merging jest coverage JSON: ${String(mergeJestError)}`);
6699
+ console.warn(`Failed merging jest coverage JSON @ ${jsonPath}: ${String(mergeJestError)}`);
6674
6700
  }
6675
6701
  }
6676
6702
  if (map.files().length === 0) {
@@ -6720,11 +6746,14 @@ var emitMergedCoverage = async (ui, opts) => {
6720
6746
  executedTests: opts.executedTests ?? []
6721
6747
  });
6722
6748
  const context = LibReport.createContext({
6723
- dir: path9.resolve("coverage", "merged"),
6749
+ dir: path10.resolve("coverage", "merged"),
6724
6750
  coverageMap: filteredMap,
6725
6751
  defaultSummarizer: "nested"
6726
6752
  });
6727
- const reporters = ui === "jest" ? [textReport({ file: "coverage.txt" })] : [textReport({ file: "coverage.txt" }), textSummaryReport({ file: "coverage-summary.txt" })];
6753
+ const reporters = ui === "jest" ? [Reports.create("text", { file: "coverage.txt" })] : [
6754
+ Reports.create("text", { file: "coverage.txt" }),
6755
+ Reports.create("text-summary", { file: "coverage-summary.txt" })
6756
+ ];
6728
6757
  const colorizeIstanbulLine = (lineText) => {
6729
6758
  const separator = /^[-=\s]+$/;
6730
6759
  if (separator.test(lineText.trim())) {
@@ -6785,8 +6814,8 @@ var emitMergedCoverage = async (ui, opts) => {
6785
6814
  for (const reporter of reporters) {
6786
6815
  reporter.execute(context);
6787
6816
  }
6788
- const textPath = path9.resolve("coverage", "merged", "coverage.txt");
6789
- const summaryPath = path9.resolve("coverage", "merged", "coverage-summary.txt");
6817
+ const textPath = path10.resolve("coverage", "merged", "coverage.txt");
6818
+ const summaryPath = path10.resolve("coverage", "merged", "coverage-summary.txt");
6790
6819
  const filesToPrint = [];
6791
6820
  if (fsSync3.existsSync(textPath)) {
6792
6821
  filesToPrint.push(textPath);
@@ -6811,7 +6840,7 @@ var emitMergedCoverage = async (ui, opts) => {
6811
6840
  }
6812
6841
  }
6813
6842
  } else {
6814
- const stdoutReporters = ui === "jest" ? [textReport({})] : [textReport({}), textSummaryReport({})];
6843
+ const stdoutReporters = ui === "jest" ? [Reports.create("text", {})] : [Reports.create("text", {}), Reports.create("text-summary", {})];
6815
6844
  for (const reporter of stdoutReporters) {
6816
6845
  reporter.execute(context);
6817
6846
  }
@@ -6868,17 +6897,43 @@ var program = async () => {
6868
6897
  coverageMode,
6869
6898
  coverageMaxFiles: coverageMaxFilesArg,
6870
6899
  coverageMaxHotspots: coverageMaxHotspotsArg,
6871
- coveragePageFit
6900
+ coveragePageFit,
6901
+ changed
6872
6902
  } = deriveArgs(argv);
6873
- console.info(`Selection \u2192 specified=${selectionSpecified} paths=${selectionPaths.length}`);
6903
+ const getChangedFiles = async (mode, cwd) => {
6904
+ const collect = async (cmd, args) => {
6905
+ try {
6906
+ const out = await runText(cmd, args, {
6907
+ cwd,
6908
+ env: safeEnv(process.env, {}),
6909
+ timeoutMs: 4e3
6910
+ });
6911
+ return out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6912
+ } catch {
6913
+ return [];
6914
+ }
6915
+ };
6916
+ const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
6917
+ const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
6918
+ const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
6919
+ const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
6920
+ return rels.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
6921
+ };
6922
+ const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
6923
+ const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
6924
+ const selectionPathsAugmented = changedSelectionAbs.length ? Array.from(/* @__PURE__ */ new Set([...selectionPaths, ...changedSelectionAbs])) : selectionPaths;
6925
+ const selectionSpecifiedAugmented = Boolean(selectionSpecified || changedSelectionAbs.length > 0);
6926
+ console.info(
6927
+ `Selection \u2192 specified=${selectionSpecifiedAugmented} paths=${selectionPathsAugmented.length}`
6928
+ );
6874
6929
  const { jest } = argsForDiscovery(["run"], jestArgs);
6875
- const selectionLooksLikeTest = selectionPaths.some(
6930
+ const selectionLooksLikeTest = selectionPathsAugmented.some(
6876
6931
  (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText)
6877
6932
  );
6878
- const selectionLooksLikePath = selectionPaths.some(
6933
+ const selectionLooksLikePath = selectionPathsAugmented.some(
6879
6934
  (pathText) => /[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)
6880
6935
  );
6881
- const selectionHasPaths = selectionPaths.length > 0;
6936
+ const selectionHasPaths = selectionPathsAugmented.length > 0;
6882
6937
  const repoRootForDiscovery = workspaceRoot ?? await findRepoRoot();
6883
6938
  const expandProductionSelections = async (tokens, repoRoot) => {
6884
6939
  const results = /* @__PURE__ */ new Set();
@@ -6887,18 +6942,18 @@ var program = async () => {
6887
6942
  if (!token) {
6888
6943
  continue;
6889
6944
  }
6890
- const isAbs = path9.isAbsolute(token);
6945
+ const isAbs = path10.isAbsolute(token);
6891
6946
  const looksLikeRelPath = /[\\/]/.test(token);
6892
6947
  let candidateFromRoot;
6893
6948
  if (token.startsWith("/")) {
6894
- candidateFromRoot = path9.join(repoRoot, token.slice(1));
6949
+ candidateFromRoot = path10.join(repoRoot, token.slice(1));
6895
6950
  } else if (looksLikeRelPath) {
6896
- candidateFromRoot = path9.join(repoRoot, token);
6951
+ candidateFromRoot = path10.join(repoRoot, token);
6897
6952
  } else {
6898
6953
  candidateFromRoot = void 0;
6899
6954
  }
6900
6955
  const tryPushIfProd = (absPath) => {
6901
- const norm = path9.resolve(absPath).replace(/\\/g, "/");
6956
+ const norm = path10.resolve(absPath).replace(/\\/g, "/");
6902
6957
  const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
6903
6958
  if (!isTest && fsSync3.existsSync(norm)) {
6904
6959
  results.add(norm);
@@ -6920,7 +6975,7 @@ var program = async () => {
6920
6975
  }),
6921
6976
  timeoutMs: 4e3
6922
6977
  });
6923
- const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path9.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6978
+ const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path10.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6924
6979
  (abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
6925
6980
  );
6926
6981
  matches.forEach((abs) => results.add(abs));
@@ -6929,20 +6984,20 @@ var program = async () => {
6929
6984
  }
6930
6985
  return Array.from(results);
6931
6986
  };
6932
- const initialProdSelections = selectionPaths.filter(
6987
+ const initialProdSelections = selectionPathsAugmented.filter(
6933
6988
  (pathText) => (/[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)) && !/(^|\/)tests?\//i.test(pathText) && !/\.(test|spec)\.[tj]sx?$/i.test(pathText)
6934
6989
  );
6935
- const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPaths, repoRootForDiscovery);
6990
+ const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPathsAugmented, repoRootForDiscovery);
6936
6991
  const selectionIncludesProdPaths = expandedProdSelections.length > 0;
6937
6992
  console.info(
6938
6993
  `Selection classify \u2192 looksLikePath=${selectionLooksLikePath} looksLikeTest=${selectionLooksLikeTest} prodPaths=${selectionIncludesProdPaths}`
6939
6994
  );
6940
- const stripPathTokens = (args) => args.filter((token) => !selectionPaths.includes(token));
6995
+ const stripPathTokens = (args) => args.filter((token) => !selectionPathsAugmented.includes(token));
6941
6996
  const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
6942
6997
  const projectConfigs = [];
6943
6998
  try {
6944
- const baseCfg = path9.resolve("jest.config.js");
6945
- const tsCfg = path9.resolve("jest.ts.config.js");
6999
+ const baseCfg = path10.resolve("jest.config.js");
7000
+ const tsCfg = path10.resolve("jest.ts.config.js");
6946
7001
  if (fsSync3.existsSync(baseCfg)) {
6947
7002
  projectConfigs.push(baseCfg);
6948
7003
  }
@@ -6959,7 +7014,7 @@ var program = async () => {
6959
7014
  );
6960
7015
  const prodSelections2 = expandedProdSelections;
6961
7016
  for (const cfg of projectConfigs) {
6962
- const cfgCwd = path9.dirname(cfg);
7017
+ const cfgCwd = path10.dirname(cfg);
6963
7018
  const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6964
7019
  cwd: cfgCwd
6965
7020
  });
@@ -6972,7 +7027,7 @@ var program = async () => {
6972
7027
  });
6973
7028
  } catch (err) {
6974
7029
  if (isDebug()) {
6975
- console.warn(`direct selection failed for project ${path9.basename(cfg)}: ${String(err)}`);
7030
+ console.warn(`direct selection failed for project ${path10.basename(cfg)}: ${String(err)}`);
6976
7031
  }
6977
7032
  }
6978
7033
  perProjectFiles.set(cfg, directPerProject);
@@ -6984,7 +7039,7 @@ var program = async () => {
6984
7039
  )} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
6985
7040
  );
6986
7041
  for (const cfg of projectConfigs) {
6987
- const cfgCwd = path9.dirname(cfg);
7042
+ const cfgCwd = path10.dirname(cfg);
6988
7043
  const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6989
7044
  cwd: cfgCwd
6990
7045
  });
@@ -6994,18 +7049,18 @@ var program = async () => {
6994
7049
  const perProjectFiltered = /* @__PURE__ */ new Map();
6995
7050
  for (const cfg of projectConfigs) {
6996
7051
  const files = perProjectFiles.get(cfg) ?? [];
6997
- const selectionTestPaths = selectionPaths.filter(
7052
+ const selectionTestPaths = selectionPathsAugmented.filter(
6998
7053
  (pathToken) => /\.(test|spec)\.[tj]sx?$/i.test(pathToken) || /(^|\/)tests?\//i.test(pathToken)
6999
7054
  );
7000
7055
  const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
7001
7056
  const absFiles = candidates.map(
7002
- (candidatePath) => path9.isAbsolute(candidatePath) ? candidatePath : path9.join(repoRootForDiscovery, candidatePath)
7057
+ (candidatePath) => path10.isAbsolute(candidatePath) ? candidatePath : path10.join(repoRootForDiscovery, candidatePath)
7003
7058
  ).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
7004
7059
  const onlyOwned = await filterCandidatesForProject(
7005
7060
  cfg,
7006
7061
  jestDiscoveryArgs,
7007
7062
  absFiles,
7008
- path9.dirname(cfg)
7063
+ path10.dirname(cfg)
7009
7064
  );
7010
7065
  perProjectFiltered.set(cfg, onlyOwned);
7011
7066
  }
@@ -7017,7 +7072,7 @@ var program = async () => {
7017
7072
  if (selectionHasPaths && prodSelections.length > 0) {
7018
7073
  console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
7019
7074
  const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
7020
- const selectionKey = prodSelections.map((absPath) => path9.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((a, b) => a.localeCompare(b)).join("|");
7075
+ const selectionKey = prodSelections.map((absPath) => path10.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
7021
7076
  const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
7022
7077
  const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
7023
7078
  const rgMatches = await cachedRelated2({
@@ -7047,7 +7102,7 @@ var program = async () => {
7047
7102
  cfg,
7048
7103
  jestDiscoveryArgs,
7049
7104
  rgCandidates,
7050
- path9.dirname(cfg)
7105
+ path10.dirname(cfg)
7051
7106
  );
7052
7107
  perProjectFromRg.set(cfg, owned);
7053
7108
  }
@@ -7062,9 +7117,9 @@ var program = async () => {
7062
7117
  } else {
7063
7118
  const repoRootForScan = repoRootForDiscovery;
7064
7119
  const toSeeds = (abs) => {
7065
- const rel = path9.relative(repoRootForScan, abs).replace(/\\/g, "/");
7120
+ const rel = path10.relative(repoRootForScan, abs).replace(/\\/g, "/");
7066
7121
  const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
7067
- const base = path9.basename(withoutExt);
7122
+ const base = path10.basename(withoutExt);
7068
7123
  const segs = withoutExt.split("/");
7069
7124
  const tail2 = segs.slice(-2).join("/");
7070
7125
  return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
@@ -7079,8 +7134,8 @@ var program = async () => {
7079
7134
  }
7080
7135
  };
7081
7136
  const resolveLocalImport = (fromFile, spec) => {
7082
- const baseDir = path9.dirname(fromFile);
7083
- const cand = path9.resolve(baseDir, spec);
7137
+ const baseDir = path10.dirname(fromFile);
7138
+ const cand = path10.resolve(baseDir, spec);
7084
7139
  const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
7085
7140
  for (const ext of exts) {
7086
7141
  const full = ext ? `${cand}${ext}` : cand;
@@ -7089,7 +7144,7 @@ var program = async () => {
7089
7144
  }
7090
7145
  }
7091
7146
  for (const ext of exts) {
7092
- const full = path9.join(cand, `index${ext}`);
7147
+ const full = path10.join(cand, `index${ext}`);
7093
7148
  if (fsSync3.existsSync(full)) {
7094
7149
  return full;
7095
7150
  }
@@ -7143,7 +7198,7 @@ var program = async () => {
7143
7198
  cfg,
7144
7199
  jestDiscoveryArgs,
7145
7200
  keptCandidates,
7146
- path9.dirname(cfg)
7201
+ path10.dirname(cfg)
7147
7202
  );
7148
7203
  perProjectFromScan.set(cfg, owned);
7149
7204
  }
@@ -7170,9 +7225,9 @@ var program = async () => {
7170
7225
  if (effectiveJestFiles.length === 0) {
7171
7226
  const repoRoot = repoRootForRefinement;
7172
7227
  const seeds = prodSelections.map(
7173
- (abs) => path9.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7228
+ (abs) => path10.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7174
7229
  ).flatMap((rel) => {
7175
- const base = path9.basename(rel);
7230
+ const base = path10.basename(rel);
7176
7231
  const segments = rel.split("/");
7177
7232
  return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
7178
7233
  });
@@ -7185,8 +7240,8 @@ var program = async () => {
7185
7240
  }
7186
7241
  };
7187
7242
  const resolveLocalImport = (fromFile, spec) => {
7188
- const baseDir = path9.dirname(fromFile);
7189
- const candidate = path9.resolve(baseDir, spec);
7243
+ const baseDir = path10.dirname(fromFile);
7244
+ const candidate = path10.resolve(baseDir, spec);
7190
7245
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
7191
7246
  for (const ext of extensions) {
7192
7247
  const fullPath = ext ? `${candidate}${ext}` : candidate;
@@ -7195,7 +7250,7 @@ var program = async () => {
7195
7250
  }
7196
7251
  }
7197
7252
  for (const ext of extensions) {
7198
- const fullPath = path9.join(candidate, `index${ext}`);
7253
+ const fullPath = path10.join(candidate, `index${ext}`);
7199
7254
  if (fsSync3.existsSync(fullPath)) {
7200
7255
  return fullPath;
7201
7256
  }
@@ -7314,8 +7369,8 @@ var program = async () => {
7314
7369
  }
7315
7370
  }
7316
7371
  const jestDecision = decideShouldRunJest([], effectiveJestFiles, {
7317
- selectionSpecified,
7318
- selectionPaths
7372
+ selectionSpecified: selectionSpecifiedAugmented,
7373
+ selectionPaths: selectionPathsAugmented
7319
7374
  });
7320
7375
  const { shouldRunJest } = jestDecision;
7321
7376
  const jestCount = effectiveJestFiles.length;
@@ -7333,45 +7388,63 @@ var program = async () => {
7333
7388
  }
7334
7389
  console.info(`Run plan \u2192 Jest maybe=${shouldRunJest} (projects=${projectConfigs.length})`);
7335
7390
  let jestExitCode = 0;
7391
+ const allBridgeJson = [];
7336
7392
  const executedTestFilesSet = /* @__PURE__ */ new Set();
7337
7393
  if (shouldRunJest) {
7338
7394
  console.info("Starting Jest (no Vitest targets)\u2026");
7339
7395
  await runJestBootstrap();
7340
7396
  const jestRunArgs = selectionIncludesProdPaths ? stripPathTokens(jestArgs) : jestArgs;
7397
+ const sanitizedJestRunArgs = jestRunArgs.filter(
7398
+ (arg) => !/^--coverageDirectory(?:=|$)/.test(String(arg))
7399
+ );
7341
7400
  const projectsToRun = projectConfigs.filter(
7342
7401
  (cfg) => (perProjectFiltered.get(cfg) ?? []).length > 0
7343
7402
  );
7344
- const totalProjectsToRun = projectsToRun.length;
7345
7403
  const stripFooter = (text) => {
7346
7404
  const lines = text.split("\n");
7347
7405
  const idx = lines.findIndex((ln) => /^Test Files\s/.test(stripAnsiSimple(ln)));
7348
7406
  return idx >= 0 ? lines.slice(0, idx).join("\n").trimEnd() : text;
7349
7407
  };
7408
+ const prodSeedsForRun = (() => {
7409
+ const changedAbs = (changedSelectionAbs ?? []).map(
7410
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7411
+ );
7412
+ const selAbs = selectionPathsAugmented.map(
7413
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7414
+ );
7415
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7416
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7417
+ );
7418
+ })();
7419
+ const repoRootForRank = repoRootForDiscovery;
7420
+ const fileRank = await computeDirectnessRank({
7421
+ repoRoot: repoRootForRank,
7422
+ productionSeeds: prodSeedsForRun
7423
+ });
7350
7424
  for (let projIndex = 0; projIndex < projectsToRun.length; projIndex += 1) {
7351
7425
  const cfg = projectsToRun[projIndex];
7352
- const isLastProject = projIndex === totalProjectsToRun - 1;
7353
7426
  const files = perProjectFiltered.get(cfg) ?? [];
7354
7427
  if (files.length === 0) {
7355
- console.info(`Project ${path9.basename(cfg)}: 0 matching tests after filter; skipping.`);
7428
+ console.info(`Project ${path10.basename(cfg)}: 0 matching tests after filter; skipping.`);
7356
7429
  continue;
7357
7430
  }
7358
7431
  files.forEach(
7359
- (absTestPath) => executedTestFilesSet.add(path9.resolve(absTestPath).replace(/\\/g, "/"))
7432
+ (absTestPath) => executedTestFilesSet.add(path10.resolve(absTestPath).replace(/\\/g, "/"))
7360
7433
  );
7361
- const outJson = path9.join(
7434
+ const outJson = path10.join(
7362
7435
  os2.tmpdir(),
7363
7436
  `jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
7364
7437
  );
7365
- const reporterPath = path9.resolve("scripts/jest-vitest-bridge.cjs");
7438
+ const reporterPath = path10.resolve("scripts/jest-vitest-bridge.cjs");
7366
7439
  try {
7367
7440
  if (!fsSync3.existsSync(reporterPath)) {
7368
- fsSync3.mkdirSync(path9.dirname(reporterPath), { recursive: true });
7441
+ fsSync3.mkdirSync(path10.dirname(reporterPath), { recursive: true });
7369
7442
  fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
7370
7443
  }
7371
7444
  } catch (ensureReporterError) {
7372
7445
  console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
7373
7446
  }
7374
- 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}`);
7447
+ 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}`);
7375
7448
  const coverageFromArgs = [];
7376
7449
  for (const relPath of selectedFilesForCoverage) {
7377
7450
  coverageFromArgs.push("--collectCoverageFrom", relPath);
@@ -7385,14 +7458,17 @@ var program = async () => {
7385
7458
  "--config",
7386
7459
  cfg,
7387
7460
  "--runTestsByPath",
7388
- "--reporters",
7389
- reporterPath,
7461
+ `--reporters=${reporterPath}`,
7390
7462
  "--silent",
7391
7463
  "--colors",
7392
7464
  "--json",
7393
7465
  "--outputFile",
7394
7466
  outJson,
7395
- ...jestRunArgs,
7467
+ ...sanitizedJestRunArgs,
7468
+ ...collectCoverage ? [
7469
+ "--coverageDirectory",
7470
+ path10.join("coverage", "jest", path10.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
7471
+ ] : [],
7396
7472
  ...coverageFromArgs,
7397
7473
  "--passWithNoTests",
7398
7474
  ...files
@@ -7418,10 +7494,22 @@ var program = async () => {
7418
7494
  const jsonText = fsSync3.readFileSync(outJson, "utf8");
7419
7495
  const parsed = JSON.parse(jsonText);
7420
7496
  const bridge = coerceJestJsonToBridge(parsed);
7421
- pretty = renderVitestFromJestJSON(bridge, {
7422
- cwd: repoRootForDiscovery,
7423
- ...editorCmd !== void 0 ? { editorCmd } : {}
7424
- });
7497
+ allBridgeJson.push(bridge);
7498
+ try {
7499
+ const reordered = {
7500
+ ...bridge,
7501
+ testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
7502
+ };
7503
+ pretty = renderVitestFromJestJSON(reordered, {
7504
+ cwd: repoRootForDiscovery,
7505
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7506
+ });
7507
+ } catch {
7508
+ pretty = renderVitestFromJestJSON(bridge, {
7509
+ cwd: repoRootForDiscovery,
7510
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7511
+ });
7512
+ }
7425
7513
  if (debug) {
7426
7514
  const preview = pretty.split("\n").slice(0, 3).join("\n");
7427
7515
  console.info(`pretty preview (json):
@@ -7445,9 +7533,7 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7445
7533
  ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7446
7534
  }
7447
7535
  }
7448
- if (!isLastProject) {
7449
- pretty = stripFooter(pretty);
7450
- }
7536
+ pretty = stripFooter(pretty);
7451
7537
  if (pretty.trim().length > 0) {
7452
7538
  process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
7453
7539
  `);
@@ -7459,15 +7545,69 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7459
7545
  } else {
7460
7546
  console.info("Jest run skipped based on selection and thresholds.");
7461
7547
  }
7462
- if (collectCoverage && shouldRunJest && coverageAbortOnFailure && jestExitCode !== 0) {
7463
- process.exit(jestExitCode);
7548
+ if (allBridgeJson.length > 0) {
7549
+ const agg = allBridgeJson.map((bridge) => bridge.aggregated);
7550
+ const sum = (select) => agg.reduce((total, item) => total + (select(item) || 0), 0);
7551
+ const startTime = Math.min(
7552
+ ...allBridgeJson.map((bridge) => Number(bridge.startTime || Date.now()))
7553
+ );
7554
+ const unified = {
7555
+ startTime,
7556
+ testResults: allBridgeJson.flatMap((bridge) => bridge.testResults),
7557
+ aggregated: {
7558
+ numTotalTestSuites: sum((item) => item.numTotalTestSuites),
7559
+ numPassedTestSuites: sum((item) => item.numPassedTestSuites),
7560
+ numFailedTestSuites: sum((item) => item.numFailedTestSuites),
7561
+ numTotalTests: sum((item) => item.numTotalTests),
7562
+ numPassedTests: sum((item) => item.numPassedTests),
7563
+ numFailedTests: sum((item) => item.numFailedTests),
7564
+ numPendingTests: sum((item) => item.numPendingTests),
7565
+ numTodoTests: sum((item) => item.numTodoTests),
7566
+ startTime,
7567
+ success: agg.every((item) => Boolean(item.success)),
7568
+ runTimeMs: sum((item) => Number(item.runTimeMs ?? 0))
7569
+ }
7570
+ };
7571
+ try {
7572
+ const prodSeeds = (() => {
7573
+ const changedAbs = (changedSelectionAbs ?? []).map(
7574
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7575
+ );
7576
+ const selAbs = selectionPathsAugmented.map(
7577
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7578
+ );
7579
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7580
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7581
+ );
7582
+ })();
7583
+ const unifiedRank = await computeDirectnessRank({
7584
+ repoRoot: repoRootForDiscovery,
7585
+ productionSeeds: prodSeeds
7586
+ });
7587
+ const ordered = sortTestResultsWithRank(unifiedRank, unified.testResults).reverse();
7588
+ unified.testResults = ordered;
7589
+ } catch {
7590
+ }
7591
+ const text = renderVitestFromJestJSON(unified, {
7592
+ cwd: repoRootForDiscovery,
7593
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7594
+ });
7595
+ if (text.trim().length > 0) {
7596
+ process.stdout.write(text.endsWith("\n") ? text : `${text}
7597
+ `);
7598
+ }
7599
+ }
7600
+ const finalExitCode = jestExitCode;
7601
+ if (collectCoverage && shouldRunJest && coverageAbortOnFailure && finalExitCode !== 0) {
7602
+ process.exit(finalExitCode);
7603
+ return;
7464
7604
  }
7465
7605
  if (collectCoverage && shouldRunJest) {
7466
7606
  await mergeLcov();
7467
7607
  const repoRoot = workspaceRoot ?? await findRepoRoot();
7468
7608
  const mergedOptsBase = {
7469
- selectionSpecified,
7470
- selectionPaths,
7609
+ selectionSpecified: selectionSpecifiedAugmented,
7610
+ selectionPaths: selectionPathsAugmented,
7471
7611
  includeGlobs,
7472
7612
  excludeGlobs,
7473
7613
  workspaceRoot: repoRoot,
@@ -7482,7 +7622,6 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7482
7622
  };
7483
7623
  await emitMergedCoverage(coverageUi, mergedOptsBase);
7484
7624
  }
7485
- const finalExitCode = jestExitCode;
7486
7625
  process.exit(finalExitCode);
7487
7626
  };
7488
7627
  export {