headlamp 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,6 +69,22 @@ npx headlamp --onlyFailures
69
69
  npx headlamp --changed --onlyFailures
70
70
  ```
71
71
 
72
+ - `--showConsole[=true|false]`:
73
+ - When enabled, Headlamp prints a dedicated "Logs" section under each failing test and failing file with the full console output captured by the runner.
74
+ - By default (without this flag), Headlamp shows a condensed "Console errors" snippet with only the most relevant error messages. `--showConsole` includes all console entries (log/info/warn/error).
75
+ - Supported forms: `--showConsole`, `--showConsole=true`, `--showConsole=false`.
76
+ - Works alongside `--onlyFailures`, coverage flags, and selection flags.
77
+
78
+ Examples:
79
+
80
+ ```bash
81
+ # Always include the full console output for each failure
82
+ npx headlamp --showConsole
83
+
84
+ # Combine with only failures visible during the run
85
+ npx headlamp --onlyFailures --showConsole
86
+ ```
87
+
72
88
  ## Changed-file selection
73
89
 
74
90
  - `--changed[=mode]` selects tests by files changed in your working tree or branch.
@@ -83,6 +99,8 @@ npx headlamp --changed --onlyFailures
83
99
  - Effects:
84
100
  - Uses changed production files as seeds to discover related tests by import-graph.
85
101
  - Coverage tables prioritize and annotate files related to selection/changed files.
102
+ - Additional flags:
103
+ - `--changed.depth=<n>`: cap the transitive import scan depth when refining related tests from changed production files. Default: 5. Increase to include more indirectly-related tests (slower), decrease for speed.
86
104
 
87
105
  Examples:
88
106
 
@@ -100,6 +118,16 @@ npx headlamp --changed=branch
100
118
  npx headlamp --coverage --changed=branch
101
119
  ```
102
120
 
121
+ Depth examples:
122
+
123
+ ```bash
124
+ # Scan imports up to 10 levels deep when resolving related tests for changed files
125
+ npx headlamp --changed=all --changed.depth=10
126
+
127
+ # With branch mode
128
+ npx headlamp --changed=branch --changed.depth=12
129
+ ```
130
+
103
131
  ## Coverage flags
104
132
 
105
133
  - `--coverage`: enables coverage collection and prints merged coverage output after test execution. Uses your project's Jest/Vitest setup and reads coverage JSON from Jest.
package/dist/cli.cjs CHANGED
@@ -249,6 +249,7 @@ var init_args = __esm({
249
249
  coverageUi: (value) => ({ type: "coverageUi", value }),
250
250
  coverageAbortOnFailure: (value) => ({ type: "coverageAbortOnFailure", value }),
251
251
  onlyFailures: (value) => ({ type: "onlyFailures", value }),
252
+ showConsole: (value) => ({ type: "showConsole", value }),
252
253
  jestArg: (value) => ({ type: "jestArg", value }),
253
254
  jestArgs: (values) => ({ type: "jestArgs", values }),
254
255
  vitestArg: (value) => ({ type: "vitestArg", value }),
@@ -265,7 +266,8 @@ var init_args = __esm({
265
266
  coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
266
267
  coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
267
268
  coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
268
- changed: (value) => ({ type: "changed", value })
269
+ changed: (value) => ({ type: "changed", value }),
270
+ changedDepth: (value) => ({ type: "changedDepth", value })
269
271
  };
270
272
  Some = (value) => ({ _tag: "some", value });
271
273
  None = { _tag: "none" };
@@ -414,6 +416,18 @@ var init_args = __esm({
414
416
  "--onlyFailures",
415
417
  (_flag, lookahead) => step([ActionBuilders.onlyFailures(isTruthy(String(lookahead)))], true)
416
418
  ),
419
+ // --showConsole flag (boolean)
420
+ rule.eq("--showConsole", () => step([ActionBuilders.showConsole(true)])),
421
+ rule.startsWith(
422
+ "--showConsole=",
423
+ (value) => step([
424
+ ActionBuilders.showConsole(isTruthy((value.split("=")[1] ?? "").trim().toLowerCase()))
425
+ ])
426
+ ),
427
+ rule.withLookahead(
428
+ "--showConsole",
429
+ (_flag, lookahead) => step([ActionBuilders.showConsole(isTruthy(String(lookahead)))], true)
430
+ ),
417
431
  rule.withLookahead(
418
432
  "--testPathPattern",
419
433
  (flag, lookahead) => step([ActionBuilders.jestArgs([flag, lookahead])], true)
@@ -486,6 +500,16 @@ var init_args = __esm({
486
500
  const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : raw === "branch" ? "branch" : "all";
487
501
  return step([ActionBuilders.changed(mode)], true);
488
502
  }),
503
+ // --changed.depth flag: maximum transitive import depth for changed selection refinement
504
+ rule.startsWith("--changed.depth=", (value) => {
505
+ const raw = (value.split("=")[1] ?? "").trim();
506
+ const num = Number(raw);
507
+ return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : []);
508
+ }),
509
+ rule.withLookahead("--changed.depth", (_flag, lookahead) => {
510
+ const num = Number(String(lookahead).trim());
511
+ return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : [], true);
512
+ }),
489
513
  rule.withLookahead(
490
514
  "-t",
491
515
  (flag, lookahead) => step(
@@ -562,6 +586,8 @@ var init_args = __esm({
562
586
  return { vitest: [], jest: [], coverage: false, coverageAbortOnFailure: action.value };
563
587
  case "onlyFailures":
564
588
  return { vitest: [], jest: [], coverage: false, onlyFailures: action.value };
589
+ case "showConsole":
590
+ return { vitest: [], jest: [], coverage: false, showConsole: action.value };
565
591
  case "jestArgs":
566
592
  return { vitest: [], jest: action.values, coverage: false };
567
593
  case "selectionHint":
@@ -592,6 +618,8 @@ var init_args = __esm({
592
618
  return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
593
619
  case "changed":
594
620
  return { vitest: [], jest: [], coverage: false, changed: action.value };
621
+ case "changedDepth":
622
+ return { vitest: [], jest: [], coverage: false, changedDepth: action.value };
595
623
  case "jestArg":
596
624
  return { vitest: [], jest: [action.value], coverage: false };
597
625
  case "vitestArg":
@@ -632,8 +660,10 @@ var init_args = __esm({
632
660
  return {
633
661
  ...next,
634
662
  ...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
663
+ ...right.changedDepth !== void 0 || left.changedDepth !== void 0 ? { changedDepth: right.changedDepth ?? left.changedDepth } : {},
635
664
  ...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
636
665
  ...right.onlyFailures !== void 0 || left.onlyFailures !== void 0 ? { onlyFailures: right.onlyFailures ?? left.onlyFailures } : {},
666
+ ...right.showConsole !== void 0 || left.showConsole !== void 0 ? { showConsole: right.showConsole ?? left.showConsole } : {},
637
667
  ...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
638
668
  ...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
639
669
  ...right.coverageMode !== void 0 || left.coverageMode !== void 0 ? { coverageMode: right.coverageMode ?? left.coverageMode } : {},
@@ -658,6 +688,7 @@ var init_args = __esm({
658
688
  let coverageUi = "both";
659
689
  let coverageAbortOnFailure = false;
660
690
  let onlyFailures = false;
691
+ let showConsole = false;
661
692
  let coverageShowCode = Boolean(process.stdout.isTTY);
662
693
  let coverageMode = "auto";
663
694
  const coverageMaxFilesLocalInit = void 0;
@@ -676,6 +707,7 @@ var init_args = __esm({
676
707
  coverageUi = contrib.coverageUi ?? coverageUi;
677
708
  coverageAbortOnFailure = contrib.coverageAbortOnFailure ?? coverageAbortOnFailure;
678
709
  onlyFailures = contrib.onlyFailures ?? onlyFailures;
710
+ showConsole = contrib.showConsole ?? showConsole;
679
711
  coverageShowCode = contrib.coverageShowCode ?? coverageShowCode;
680
712
  const coverageDetailComputed = contrib.coverageDetail ?? (contrib.selection ? "auto" : void 0);
681
713
  coverageMode = contrib.coverageMode ?? (contrib.selection ? "compact" : "auto");
@@ -710,6 +742,7 @@ var init_args = __esm({
710
742
  coverageUi,
711
743
  coverageAbortOnFailure,
712
744
  onlyFailures,
745
+ showConsole,
713
746
  selectionSpecified: Boolean(contrib.selection),
714
747
  selectionPaths: [...contrib.selectionPaths ?? []],
715
748
  includeGlobs,
@@ -722,7 +755,8 @@ var init_args = __esm({
722
755
  coveragePageFit,
723
756
  ...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
724
757
  ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
725
- ...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
758
+ ...contrib.changed !== void 0 ? { changed: contrib.changed } : {},
759
+ ...contrib.changedDepth !== void 0 ? { changedDepth: contrib.changedDepth } : {}
726
760
  };
727
761
  return out;
728
762
  };
@@ -5074,24 +5108,43 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
5074
5108
  };
5075
5109
  var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
5076
5110
  var isConsoleEntry = (candidate) => typeof candidate === "object" && candidate !== null;
5077
- var buildConsoleSection = (maybeConsole) => {
5111
+ var buildConsoleSection = (maybeConsole, opts) => {
5078
5112
  const out = [];
5079
5113
  if (!Array.isArray(maybeConsole)) {
5080
5114
  return out;
5081
5115
  }
5082
5116
  const entries = maybeConsole.filter(isConsoleEntry);
5083
- const errorsOnly = entries.filter((entry) => String(entry?.type ?? "").toLowerCase() === "error");
5084
- const scored = errorsOnly.map((entry) => {
5085
- const raw = entry?.message;
5086
- const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
5087
- return { msg, score: msg.length };
5088
- }).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
5089
- if (scored.length) {
5090
- out.push(ansi.dim(" Console errors:"));
5091
- for (const item of scored) {
5092
- out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
5117
+ if (opts?.full) {
5118
+ const toMsg = (entry) => {
5119
+ const type = String(entry?.type ?? "").toLowerCase();
5120
+ const raw = entry?.message;
5121
+ const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
5122
+ const origin = String(entry?.origin ?? "");
5123
+ const typeFmt = type ? `${ansi.white(type)}: ` : "";
5124
+ const originFmt = origin ? ` ${ansi.dim(`(${origin})`)}` : "";
5125
+ return ` ${ansi.dim("\u2022")} ${typeFmt}${msg}${originFmt}`;
5126
+ };
5127
+ const lines = entries.map(toMsg).filter((ln) => stripAnsiSimple(ln).trim().length > 0);
5128
+ if (lines.length) {
5129
+ out.push(ansi.dim(" Logs:"));
5130
+ out.push(...lines, "");
5131
+ }
5132
+ } else {
5133
+ const errorsOnly = entries.filter(
5134
+ (entry) => String(entry?.type ?? "").toLowerCase() === "error"
5135
+ );
5136
+ const scored = errorsOnly.map((entry) => {
5137
+ const raw = entry?.message;
5138
+ const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
5139
+ return { msg, score: msg.length };
5140
+ }).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
5141
+ if (scored.length) {
5142
+ out.push(ansi.dim(" Console errors:"));
5143
+ for (const item of scored) {
5144
+ out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
5145
+ }
5146
+ out.push("");
5093
5147
  }
5094
- out.push("");
5095
5148
  }
5096
5149
  return out;
5097
5150
  };
@@ -5153,8 +5206,8 @@ var buildThrownSection = (details) => {
5153
5206
  }
5154
5207
  };
5155
5208
  const candidates = [];
5156
- for (const d of details) {
5157
- const obj = d && typeof d === "object" ? d : null;
5209
+ for (const detailEntry of details) {
5210
+ const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
5158
5211
  if (obj && obj.error && typeof obj.error === "object") {
5159
5212
  const err = obj.error;
5160
5213
  if (typeof err.name === "string") {
@@ -5351,7 +5404,7 @@ var renderChunks = (chunks, ctx, fns, opts) => {
5351
5404
 
5352
5405
  // src/lib/formatter/context.ts
5353
5406
  var fs5 = __toESM(require("node:fs"), 1);
5354
- var makeCtx = (opts, showStacks = false) => {
5407
+ var makeCtx = (opts, showStacks = false, showConsole = false) => {
5355
5408
  const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
5356
5409
  const width = Math.max(
5357
5410
  40,
@@ -5367,7 +5420,15 @@ var makeCtx = (opts, showStacks = false) => {
5367
5420
  return [];
5368
5421
  }
5369
5422
  };
5370
- return { cwd, width, showStacks, projectHint, editorCmd: opts?.editorCmd, readSource: readSource2 };
5423
+ return {
5424
+ cwd,
5425
+ width,
5426
+ showStacks,
5427
+ showConsole,
5428
+ projectHint,
5429
+ editorCmd: opts?.editorCmd,
5430
+ readSource: readSource2
5431
+ };
5371
5432
  };
5372
5433
 
5373
5434
  // src/lib/formatter/bridge/tryBridgeFallback.ts
@@ -5689,7 +5750,9 @@ var renderFileLevelFailure = (file, ctx) => {
5689
5750
  suppressDiff: pretty.length > 0,
5690
5751
  stackPreview
5691
5752
  });
5692
- const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
5753
+ const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null), {
5754
+ full: Boolean(ctx.showConsole)
5755
+ });
5693
5756
  const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
5694
5757
  const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
5695
5758
  return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
@@ -6004,7 +6067,9 @@ var renderFailedAssertion = (args) => {
6004
6067
  return empty;
6005
6068
  }
6006
6069
  })() : empty;
6007
- const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
6070
+ const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null), {
6071
+ full: Boolean(ctx.showConsole)
6072
+ });
6008
6073
  const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
6009
6074
  const merged = collapseStacks([...msgLines, ...details.stacks]);
6010
6075
  const tail = collapseStacks(merged).filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
@@ -6101,7 +6166,11 @@ var formatJestOutputVitest = (raw, opts) => pipe(
6101
6166
  { raw, opts },
6102
6167
  (state) => ({
6103
6168
  ...state,
6104
- ctx: makeCtx(state.opts, /\bFAIL\b/.test(stripAnsiSimple(state.raw)))
6169
+ ctx: makeCtx(
6170
+ state.opts,
6171
+ /\bFAIL\b/.test(stripAnsiSimple(state.raw)),
6172
+ Boolean(state.opts?.showConsole)
6173
+ )
6105
6174
  }),
6106
6175
  (state) => ({ ...state, chunks: parseChunks(state.raw) }),
6107
6176
  (state) => ({
@@ -6456,6 +6525,7 @@ var program = async () => {
6456
6525
  coverageUi,
6457
6526
  coverageAbortOnFailure,
6458
6527
  onlyFailures,
6528
+ showConsole,
6459
6529
  selectionSpecified,
6460
6530
  selectionPaths,
6461
6531
  includeGlobs,
@@ -6468,7 +6538,8 @@ var program = async () => {
6468
6538
  coverageMaxFiles: coverageMaxFilesArg,
6469
6539
  coverageMaxHotspots: coverageMaxHotspotsArg,
6470
6540
  coveragePageFit,
6471
- changed
6541
+ changed,
6542
+ changedDepth
6472
6543
  } = deriveArgs(argv);
6473
6544
  const getChangedFiles = async (mode, cwd) => {
6474
6545
  const collect = async (cmd, args) => {
@@ -6940,7 +7011,7 @@ var program = async () => {
6940
7011
  resolutionCache.set(key, resolved);
6941
7012
  return resolved;
6942
7013
  };
6943
- const MAX_DEPTH = 5;
7014
+ const MAX_DEPTH = Number.isFinite(Number(changedDepth)) && Number(changedDepth) > 0 ? Number(changedDepth) : 5;
6944
7015
  const seen = /* @__PURE__ */ new Set();
6945
7016
  const matchesTransitively = async (absTestPath, depth) => {
6946
7017
  if (depth > MAX_DEPTH) {
@@ -7157,7 +7228,8 @@ var program = async () => {
7157
7228
  reordered,
7158
7229
  makeCtx(
7159
7230
  { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7160
- /\bFAIL\b/.test(stripAnsiSimple(output))
7231
+ /\bFAIL\b/.test(stripAnsiSimple(output)),
7232
+ Boolean(showConsole)
7161
7233
  ),
7162
7234
  { onlyFailures }
7163
7235
  );
@@ -7166,7 +7238,8 @@ var program = async () => {
7166
7238
  bridge,
7167
7239
  makeCtx(
7168
7240
  { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7169
- /\bFAIL\b/.test(stripAnsiSimple(output))
7241
+ /\bFAIL\b/.test(stripAnsiSimple(output)),
7242
+ Boolean(showConsole)
7170
7243
  ),
7171
7244
  { onlyFailures }
7172
7245
  );
@@ -7268,7 +7341,8 @@ ${stripFooter(rawAlso)}`.trimEnd();
7268
7341
  unified,
7269
7342
  makeCtx(
7270
7343
  { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7271
- showStacks
7344
+ showStacks,
7345
+ Boolean(showConsole)
7272
7346
  ),
7273
7347
  { onlyFailures }
7274
7348
  );