branch-never 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,10 @@ Options:
31
31
  --no-fail Don't exit 1 when uncovered branches found
32
32
  --pattern <type> Only check: env|retry|error|feature|all (default: all)
33
33
  --threshold <pct> Fail if coverage below N% (default: 0, fail on any)
34
+ --report <file> Write an HTML report
35
+ --baseline <ref> Compare current uncovered branches against a git ref
36
+ --blame Show git blame metadata for each unreachable branch
37
+ --pr-check Compare current unreachable branches against a baseline JSON file
34
38
  ```
35
39
 
36
40
  Example output:
@@ -47,6 +51,39 @@ Covered branches:
47
51
  Summary: 1 uncovered branches, 1 covered. Coverage: 50%
48
52
  ```
49
53
 
54
+ HTML report:
55
+
56
+ ```bash
57
+ branch-never src --tests test --report report.html
58
+ ```
59
+
60
+ Baseline comparison:
61
+
62
+ ```bash
63
+ branch-never src --tests test --baseline main
64
+ ```
65
+
66
+ Blame mode:
67
+
68
+ ```bash
69
+ branch-never src --tests test --blame
70
+ ```
71
+
72
+ PR check mode:
73
+
74
+ ```bash
75
+ branch-never src --tests test --json --no-fail > .branch-never-baseline.json
76
+ branch-never src --tests test --pr-check --baseline .branch-never-baseline.json
77
+ ```
78
+
79
+ GitHub Actions:
80
+
81
+ ```yaml
82
+ - run: pnpm build
83
+ - run: pnpm test
84
+ - run: node dist/index.js src --tests test --pr-check --baseline .branch-never-baseline.json
85
+ ```
86
+
50
87
  ## What It Detects
51
88
 
52
89
  The extractor looks for these branch shapes using regex:
@@ -0,0 +1,36 @@
1
+ import { type PatternType } from "./extractor.js";
2
+ import type { FormatPayload } from "./formatter.js";
3
+ import { type BranchCoverage } from "./matcher.js";
4
+ export interface AnalysisOptions {
5
+ srcDir: string;
6
+ testsDir: string;
7
+ pattern: PatternType | "all";
8
+ cwd?: string;
9
+ }
10
+ export interface BaselineComparison {
11
+ baselineRef: string;
12
+ baseline: FormatPayload;
13
+ current: FormatPayload;
14
+ newlyUncovered: BranchCoverage[];
15
+ newlyCovered: BranchCoverage[];
16
+ netChange: number;
17
+ }
18
+ export interface PrCheckComparison {
19
+ baselinePath: string;
20
+ baseline: FormatPayload;
21
+ current: FormatPayload;
22
+ newUnreachable: BranchCoverage[];
23
+ resolvedUnreachable: BranchCoverage[];
24
+ }
25
+ export declare function analyzeBranchCoverage(options: AnalysisOptions): Promise<FormatPayload>;
26
+ export declare function compareUncoveredBranches(baselineRef: string, baseline: FormatPayload, current: FormatPayload): BaselineComparison;
27
+ export declare function analyzeAgainstBaseline(options: AnalysisOptions & {
28
+ baselineRef: string;
29
+ }): Promise<BaselineComparison>;
30
+ export declare function analyzePrCheck(options: AnalysisOptions & {
31
+ baselinePath: string;
32
+ }): Promise<PrCheckComparison>;
33
+ export declare function formatBaselineComparison(comparison: BaselineComparison): string;
34
+ export declare function comparePrBaseline(baselinePath: string, baseline: FormatPayload, current: FormatPayload): PrCheckComparison;
35
+ export declare function loadBaselinePayload(baselinePath: string): Promise<FormatPayload>;
36
+ export declare function formatPrCheckComparison(comparison: PrCheckComparison): string;
@@ -0,0 +1,208 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { promisify } from "node:util";
3
+ import { execFile as execFileCallback } from "node:child_process";
4
+ import { extractBranchesFromDirectory } from "./extractor.js";
5
+ import { loadTestFiles, matchBranchesToTests } from "./matcher.js";
6
+ import { calculateCoverage } from "./scorer.js";
7
+ const execFile = promisify(execFileCallback);
8
+ export async function analyzeBranchCoverage(options) {
9
+ const cwd = options.cwd ?? process.cwd();
10
+ const branches = await extractBranchesFromDirectory(options.srcDir, options.pattern, cwd);
11
+ const testFiles = await loadTestFiles(options.testsDir, cwd);
12
+ const results = matchBranchesToTests(branches, testFiles);
13
+ const covered = results.filter((result) => result.covered);
14
+ const uncovered = results.filter((result) => !result.covered);
15
+ return {
16
+ covered,
17
+ uncovered,
18
+ summary: calculateCoverage(results)
19
+ };
20
+ }
21
+ export function compareUncoveredBranches(baselineRef, baseline, current) {
22
+ const baselineKeys = new Map(baseline.uncovered.map((branch) => [branchIdentity(branch), branch]));
23
+ const currentKeys = new Map(current.uncovered.map((branch) => [branchIdentity(branch), branch]));
24
+ const newlyUncovered = [...currentKeys.entries()]
25
+ .filter(([key]) => !baselineKeys.has(key))
26
+ .map(([, branch]) => branch)
27
+ .sort(compareBranches);
28
+ const newlyCovered = [...baselineKeys.entries()]
29
+ .filter(([key]) => !currentKeys.has(key))
30
+ .map(([, branch]) => branch)
31
+ .sort(compareBranches);
32
+ return {
33
+ baselineRef,
34
+ baseline,
35
+ current,
36
+ newlyUncovered,
37
+ newlyCovered,
38
+ netChange: newlyCovered.length - newlyUncovered.length
39
+ };
40
+ }
41
+ export async function analyzeAgainstBaseline(options) {
42
+ const cwd = options.cwd ?? process.cwd();
43
+ const current = await analyzeBranchCoverage(options);
44
+ const restoreRef = await resolveRestoreRef(cwd);
45
+ const stashRef = await stashWorkspaceIfNeeded(cwd);
46
+ let baseline = null;
47
+ try {
48
+ await git(["checkout", options.baselineRef], cwd);
49
+ baseline = await analyzeBranchCoverage({ ...options, cwd });
50
+ }
51
+ finally {
52
+ await git(["checkout", restoreRef], cwd);
53
+ if (stashRef) {
54
+ await git(["stash", "pop", "--index", stashRef], cwd);
55
+ }
56
+ }
57
+ return compareUncoveredBranches(options.baselineRef, baseline, current);
58
+ }
59
+ export async function analyzePrCheck(options) {
60
+ const current = await analyzeBranchCoverage(options);
61
+ const baseline = await loadBaselinePayload(options.baselinePath);
62
+ return comparePrBaseline(options.baselinePath, baseline, current);
63
+ }
64
+ export function formatBaselineComparison(comparison) {
65
+ const lines = [];
66
+ lines.push(`Comparing branch coverage: ${comparison.baselineRef} \u2192 HEAD`);
67
+ lines.push("");
68
+ lines.push(`Newly uncovered branches (${comparison.newlyUncovered.length}):`);
69
+ if (comparison.newlyUncovered.length === 0) {
70
+ lines.push(" None");
71
+ }
72
+ else {
73
+ for (const branch of comparison.newlyUncovered) {
74
+ lines.push(` ${branch.file}:${branch.line} ${branch.conditionText} \u2190 added in this PR, not tested`);
75
+ }
76
+ }
77
+ lines.push("");
78
+ lines.push(`Newly covered branches (${comparison.newlyCovered.length}):`);
79
+ if (comparison.newlyCovered.length === 0) {
80
+ lines.push(" None");
81
+ }
82
+ else {
83
+ for (const branch of comparison.newlyCovered) {
84
+ lines.push(` ${branch.file}:${branch.line} ${branch.conditionText} \u2190 you added a test \u2713`);
85
+ }
86
+ }
87
+ lines.push("");
88
+ const direction = comparison.netChange > 0 ? "improvement" : comparison.netChange < 0 ? "regression" : "no change";
89
+ const signed = comparison.netChange > 0 ? `+${comparison.netChange}` : `${comparison.netChange}`;
90
+ lines.push(`Net: ${signed} branches (${direction})`);
91
+ return lines.join("\n");
92
+ }
93
+ export function comparePrBaseline(baselinePath, baseline, current) {
94
+ const baselineKeys = new Map(baseline.uncovered.map((branch) => [branchIdentity(branch), branch]));
95
+ const currentKeys = new Map(current.uncovered.map((branch) => [branchIdentity(branch), branch]));
96
+ const newUnreachable = [...currentKeys.entries()]
97
+ .filter(([key]) => !baselineKeys.has(key))
98
+ .map(([, branch]) => branch)
99
+ .sort(compareBranches);
100
+ const resolvedUnreachable = [...baselineKeys.entries()]
101
+ .filter(([key]) => !currentKeys.has(key))
102
+ .map(([, branch]) => branch)
103
+ .sort(compareBranches);
104
+ return {
105
+ baselinePath,
106
+ baseline,
107
+ current,
108
+ newUnreachable,
109
+ resolvedUnreachable
110
+ };
111
+ }
112
+ export async function loadBaselinePayload(baselinePath) {
113
+ const raw = await readFile(baselinePath, "utf8");
114
+ const parsed = JSON.parse(raw);
115
+ if (isFormatPayload(parsed)) {
116
+ return parsed;
117
+ }
118
+ if (Array.isArray(parsed) && parsed.every(isBranchCoverage)) {
119
+ return {
120
+ covered: [],
121
+ uncovered: parsed,
122
+ summary: calculateCoverage(parsed)
123
+ };
124
+ }
125
+ throw new Error(`Invalid baseline file: ${baselinePath}`);
126
+ }
127
+ export function formatPrCheckComparison(comparison) {
128
+ const lines = [];
129
+ lines.push("Comparing against baseline...");
130
+ lines.push(` Baseline: ${comparison.baseline.uncovered.length} unreachable branches`);
131
+ lines.push(` Current: ${comparison.current.uncovered.length} unreachable branches`);
132
+ lines.push("");
133
+ if (comparison.newUnreachable.length === 0) {
134
+ lines.push("No new unreachable branches introduced in this PR.");
135
+ lines.push("");
136
+ lines.push("\u2705 PR check passed");
137
+ return lines.join("\n");
138
+ }
139
+ lines.push("New unreachable branches introduced in this PR:");
140
+ for (const branch of comparison.newUnreachable) {
141
+ lines.push(` ${branch.file}:${branch.line} ${branch.conditionText} \u2190 NEW`);
142
+ }
143
+ lines.push("");
144
+ lines.push(`\u274c PR check failed: ${comparison.newUnreachable.length} new unreachable branches`);
145
+ return lines.join("\n");
146
+ }
147
+ async function resolveRestoreRef(cwd) {
148
+ const branch = (await git(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd, true)).stdout.trim();
149
+ if (branch) {
150
+ return branch;
151
+ }
152
+ return (await git(["rev-parse", "HEAD"], cwd)).stdout.trim();
153
+ }
154
+ async function stashWorkspaceIfNeeded(cwd) {
155
+ const status = (await git(["status", "--porcelain"], cwd)).stdout.trim();
156
+ if (!status) {
157
+ return null;
158
+ }
159
+ const marker = `branch-never-baseline-${Date.now()}`;
160
+ await git(["stash", "push", "--include-untracked", "--message", marker], cwd);
161
+ const list = (await git(["stash", "list", "--format=%gd %gs"], cwd)).stdout
162
+ .split("\n")
163
+ .map((line) => line.trim())
164
+ .find((line) => line.endsWith(marker));
165
+ return list?.split(" ")[0] ?? null;
166
+ }
167
+ async function git(args, cwd, allowFailure = false) {
168
+ try {
169
+ return await execFile("git", args, { cwd });
170
+ }
171
+ catch (error) {
172
+ if (allowFailure && error && typeof error === "object" && "stdout" in error && "stderr" in error) {
173
+ return {
174
+ stdout: String(error.stdout ?? ""),
175
+ stderr: String(error.stderr ?? "")
176
+ };
177
+ }
178
+ throw error;
179
+ }
180
+ }
181
+ function branchIdentity(branch) {
182
+ return [branch.file, branch.line, branch.pattern, branch.kind, branch.conditionText].join(":");
183
+ }
184
+ function compareBranches(left, right) {
185
+ if (left.file === right.file) {
186
+ return left.line - right.line;
187
+ }
188
+ return left.file.localeCompare(right.file);
189
+ }
190
+ function isFormatPayload(value) {
191
+ if (!value || typeof value !== "object") {
192
+ return false;
193
+ }
194
+ const candidate = value;
195
+ return Array.isArray(candidate.covered) && Array.isArray(candidate.uncovered) && candidate.summary !== undefined;
196
+ }
197
+ function isBranchCoverage(value) {
198
+ if (!value || typeof value !== "object") {
199
+ return false;
200
+ }
201
+ const candidate = value;
202
+ return (typeof candidate.file === "string" &&
203
+ typeof candidate.line === "number" &&
204
+ typeof candidate.conditionText === "string" &&
205
+ typeof candidate.pattern === "string" &&
206
+ typeof candidate.kind === "string");
207
+ }
208
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { BranchCoverage } from "./matcher.js";
2
+ export interface BlameInfo {
3
+ authorName: string;
4
+ authorEmail: string;
5
+ authorTime: number;
6
+ authorDate: string;
7
+ relativeDays: number;
8
+ commitSummary: string;
9
+ commitHash: string;
10
+ }
11
+ export interface BlamedBranch extends BranchCoverage {
12
+ blame: BlameInfo | null;
13
+ }
14
+ export declare function attachBlameToBranches(branches: BranchCoverage[], cwd?: string, now?: Date): Promise<BlamedBranch[]>;
15
+ export declare function loadBlameForBranch(branch: Pick<BranchCoverage, "file" | "line">, cwd?: string, now?: Date): Promise<BlameInfo | null>;
16
+ export declare function parseBlamePorcelain(output: string, now?: Date): BlameInfo | null;
package/dist/blame.js ADDED
@@ -0,0 +1,56 @@
1
+ import { promisify } from "node:util";
2
+ import { execFile as execFileCallback } from "node:child_process";
3
+ const execFile = promisify(execFileCallback);
4
+ export async function attachBlameToBranches(branches, cwd = process.cwd(), now = new Date()) {
5
+ return Promise.all(branches.map(async (branch) => ({
6
+ ...branch,
7
+ blame: await loadBlameForBranch(branch, cwd, now)
8
+ })));
9
+ }
10
+ export async function loadBlameForBranch(branch, cwd = process.cwd(), now = new Date()) {
11
+ try {
12
+ const { stdout } = await execFile("git", ["blame", "--line-porcelain", "-L", `${branch.line},${branch.line}`, "--", branch.file], { cwd });
13
+ return parseBlamePorcelain(stdout, now);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export function parseBlamePorcelain(output, now = new Date()) {
20
+ const lines = output.split("\n");
21
+ const header = lines[0]?.trim() ?? "";
22
+ const authorName = readBlameField(lines, "author");
23
+ const authorEmail = stripAngleBrackets(readBlameField(lines, "author-mail"));
24
+ const authorTimeText = readBlameField(lines, "author-time");
25
+ const commitSummary = readBlameField(lines, "summary");
26
+ const commitHash = header.split(" ")[0] ?? "";
27
+ const authorTime = Number(authorTimeText);
28
+ if (!commitHash || !authorName || !authorEmail || !Number.isFinite(authorTime) || !commitSummary) {
29
+ return null;
30
+ }
31
+ return {
32
+ authorName,
33
+ authorEmail,
34
+ authorTime,
35
+ authorDate: formatDate(authorTime),
36
+ relativeDays: calculateRelativeDays(authorTime, now),
37
+ commitSummary,
38
+ commitHash
39
+ };
40
+ }
41
+ function readBlameField(lines, field) {
42
+ const prefix = `${field} `;
43
+ const match = lines.find((line) => line.startsWith(prefix));
44
+ return match?.slice(prefix.length).trim() ?? "";
45
+ }
46
+ function stripAngleBrackets(value) {
47
+ return value.replace(/^<|>$/g, "");
48
+ }
49
+ function formatDate(unixSeconds) {
50
+ return new Date(unixSeconds * 1000).toISOString().slice(0, 10);
51
+ }
52
+ function calculateRelativeDays(unixSeconds, now) {
53
+ const diffMs = now.getTime() - unixSeconds * 1000;
54
+ return Math.max(0, Math.floor(diffMs / 86_400_000));
55
+ }
56
+ //# sourceMappingURL=blame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blame.js","sourceRoot":"","sources":["../src/blame.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,6 @@ export interface BranchMatch {
7
7
  kind: "if" | "catch" | "ternary" | "short-circuit";
8
8
  }
9
9
  export declare function extractInterestingBranches(code: string, file?: string): BranchMatch[];
10
- export declare function extractBranchesFromDirectory(srcDir: string, patternType?: PatternType | "all"): Promise<BranchMatch[]>;
10
+ export declare function extractBranchesFromDirectory(srcDir: string, patternType?: PatternType | "all", cwd?: string): Promise<BranchMatch[]>;
11
11
  export declare function detectPattern(conditionText: string, kind?: BranchMatch["kind"]): PatternType | null;
12
12
  export declare function normalizeCondition(conditionText: string): string;
package/dist/extractor.js CHANGED
@@ -79,8 +79,8 @@ export function extractInterestingBranches(code, file = "<memory>") {
79
79
  }
80
80
  return branches.sort((left, right) => left.line - right.line);
81
81
  }
82
- export async function extractBranchesFromDirectory(srcDir, patternType = "all") {
83
- const absoluteDir = path.resolve(srcDir);
82
+ export async function extractBranchesFromDirectory(srcDir, patternType = "all", cwd = process.cwd()) {
83
+ const absoluteDir = path.resolve(cwd, srcDir);
84
84
  const files = await glob(FILE_GLOB, {
85
85
  absolute: true,
86
86
  cwd: absoluteDir,
@@ -89,7 +89,7 @@ export async function extractBranchesFromDirectory(srcDir, patternType = "all")
89
89
  });
90
90
  const branches = await Promise.all(files.map(async (file) => {
91
91
  const code = await readFile(file, "utf8");
92
- return extractInterestingBranches(code, path.relative(process.cwd(), file));
92
+ return extractInterestingBranches(code, path.relative(cwd, file));
93
93
  }));
94
94
  return branches
95
95
  .flat()
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiB5B,MAAM,mBAAmB,GAAwB;IAC/C;QACE,IAAI,EAAE,KAAK;QACX,OAAO,EAAE;YACP,mBAAmB;YACnB,mCAAmC;YACnC,wBAAwB;SACzB;KACF;IACD;QACE,IAAI,EAAE,OAAO;QACb,OAAO,EAAE;YACP,sBAAsB;YACtB,uBAAuB;YACvB,cAAc;SACf;KACF;IACD;QACE,IAAI,EAAE,OAAO;QACb,OAAO,EAAE;YACP,4CAA4C;YAC5C,mCAAmC;SACpC;KACF;IACD;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE;YACP,uBAAuB;SACxB;KACF;CACF,CAAC;AAEF,MAAM,SAAS,GAAG,8BAA8B,CAAC;AAEjD,MAAM,UAAU,0BAA0B,CAAC,IAAY,EAAE,IAAI,GAAG,UAAU;IACxE,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,SAAS,GAAG,CAAC,aAAqB,EAAE,KAAa,EAAE,IAAyB,EAAE,EAAE;QACpF,MAAM,UAAU,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,UAAU,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI;YACJ,IAAI;YACJ,aAAa,EAAE,UAAU;YACzB,OAAO;YACP,IAAI;SACL,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAC5D,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,CAAC;QAChF,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAChC,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,SAAS,CAAC,uDAAuD,EAAE,GAAG,CAAC,CACtG,EAAE,CAAC;gBACF,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,6CAA6C,CAAC,EAAE,CAAC;QACjF,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,wCAAwC,CAAC,EAAE,CAAC;QAC5E,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACtD,IAAI,2CAA2C,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjE,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,eAAe,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAAc,EACd,cAAmC,KAAK;IAExC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE;QAClC,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,WAAW;QAChB,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,CAAC,WAAW,CAAC;KACtB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,OAAO,0BAA0B,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,QAAQ;SACZ,IAAI,EAAE;SACN,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,KAAK,KAAK,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,CAAC;SAC3E,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QAChC,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,aAAqB,EACrB,IAA0B;IAE1B,KAAK,MAAM,UAAU,IAAI,mBAAmB,EAAE,CAAC;QAC7C,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;YAClE,OAAO,UAAU,CAAC,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,eAAe,IAAI,2CAA2C,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAChG,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,aAAqB;IACtD,OAAO,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { BlamedBranch } from "./blame.js";
1
2
  import type { BranchCoverage } from "./matcher.js";
2
3
  import type { CoverageSummary } from "./scorer.js";
3
4
  export interface FormatPayload {
@@ -6,4 +7,5 @@ export interface FormatPayload {
6
7
  summary: CoverageSummary;
7
8
  }
8
9
  export declare function formatText(payload: FormatPayload): string;
9
- export declare function formatJson(payload: FormatPayload): string;
10
+ export declare function formatJson(payload: unknown): string;
11
+ export declare function formatBlameText(branches: BlamedBranch[]): string;
package/dist/formatter.js CHANGED
@@ -30,6 +30,30 @@ export function formatText(payload) {
30
30
  export function formatJson(payload) {
31
31
  return JSON.stringify(payload, null, 2);
32
32
  }
33
+ export function formatBlameText(branches) {
34
+ const lines = [];
35
+ lines.push(chalk.bold("Unreachable branches with blame:"));
36
+ lines.push("");
37
+ if (branches.length === 0) {
38
+ lines.push(` ${chalk.green("None")}`);
39
+ return lines.join("\n");
40
+ }
41
+ for (const branch of branches) {
42
+ lines.push(` ${chalk.red(`${branch.file}:${branch.line}`)} ${branch.conditionText}`);
43
+ if (branch.blame) {
44
+ lines.push(` \u2192 Author: ${branch.blame.authorName} <${branch.blame.authorEmail}>`);
45
+ lines.push(` \u2192 Last modified: ${branch.blame.authorDate} (${branch.blame.relativeDays} days ago)`);
46
+ lines.push(` \u2192 Commit: "${branch.blame.commitSummary}"`);
47
+ }
48
+ else {
49
+ lines.push(" \u2192 Author: unavailable");
50
+ lines.push(" \u2192 Last modified: unavailable");
51
+ lines.push(" \u2192 Commit: unavailable");
52
+ }
53
+ lines.push("");
54
+ }
55
+ return lines.join("\n").trimEnd();
56
+ }
33
57
  function describeUncovered(branch) {
34
58
  switch (branch.pattern) {
35
59
  case "env":
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,MAAM,UAAU,UAAU,CAAC,OAAsB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC,CAAC;IACjF,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,GAAG,CACtF,UAAU,iBAAiB,CAAC,MAAM,CAAC,EAAE,CACtC,EAAE,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YAC1D,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,GAAG,CACxF,wBAAwB,YAAY,EAAE,CACvC,EAAE,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,YAAY,OAAO,CAAC,OAAO,CAAC,cAAc,wBAAwB,OAAO,CAAC,OAAO,CAAC,YAAY,uBAAuB,OAAO,CAAC,OAAO,CAAC,eAAe,GAAG,CACxJ,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAsB;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAsB;IAC/C,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,KAAK,KAAK;YACR,OAAO,qBAAqB,CAAC;QAC/B,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC,KAAK,OAAO;YACV,OAAO,uBAAuB,CAAC;QACjC,KAAK,SAAS;YACZ,OAAO,uBAAuB,CAAC;QACjC;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,MAAM,UAAU,UAAU,CAAC,OAAsB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC,CAAC;IACjF,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,GAAG,CACtF,UAAU,iBAAiB,CAAC,MAAM,CAAC,EAAE,CACtC,EAAE,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YAC1D,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,GAAG,CACxF,wBAAwB,YAAY,EAAE,CACvC,EAAE,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,YAAY,OAAO,CAAC,OAAO,CAAC,cAAc,wBAAwB,OAAO,CAAC,OAAO,CAAC,YAAY,uBAAuB,OAAO,CAAC,OAAO,CAAC,eAAe,GAAG,CACxJ,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAwB;IACtD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;QACvF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;YAC1F,KAAK,CAAC,IAAI,CAAC,6BAA6B,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,YAAY,YAAY,CAAC,CAAC;YAC3G,KAAK,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YACpD,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAsB;IAC/C,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,KAAK,KAAK;YACR,OAAO,qBAAqB,CAAC;QAC/B,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC,KAAK,OAAO;YACV,OAAO,uBAAuB,CAAC;QACjC,KAAK,SAAS;YACZ,OAAO,uBAAuB,CAAC;QACjC;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { BaselineComparison } from "./baseline.js";
2
+ import type { FormatPayload } from "./formatter.js";
3
+ interface HtmlReportOptions {
4
+ baselineComparison?: BaselineComparison;
5
+ cwd?: string;
6
+ }
7
+ export declare function generateHtmlReport(payload: FormatPayload, options?: HtmlReportOptions): Promise<string>;
8
+ export {};
@@ -0,0 +1,280 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function generateHtmlReport(payload, options = {}) {
4
+ const cwd = options.cwd ?? process.cwd();
5
+ const allBranches = [...payload.covered, ...payload.uncovered];
6
+ const files = summarizeFiles(allBranches);
7
+ const sourceSections = await Promise.all(files.map(async (fileSummary) => renderSourceSection(fileSummary.file, payload, cwd)));
8
+ return `<!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="utf-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1">
13
+ <title>branch-never report</title>
14
+ <style>
15
+ :root {
16
+ --bg: #f6f1e8;
17
+ --panel: #fffdf8;
18
+ --ink: #1f2933;
19
+ --muted: #5b6773;
20
+ --border: #d7cbb8;
21
+ --covered: #1f7a4d;
22
+ --covered-bg: #e5f6ec;
23
+ --uncovered: #b42318;
24
+ --uncovered-bg: #ffe5e0;
25
+ --accent: #0f4c81;
26
+ --code-bg: #f3ede2;
27
+ }
28
+ * { box-sizing: border-box; }
29
+ body {
30
+ margin: 0;
31
+ font-family: ui-sans-serif, system-ui, sans-serif;
32
+ background: linear-gradient(180deg, #f1e7d9 0%, var(--bg) 100%);
33
+ color: var(--ink);
34
+ line-height: 1.5;
35
+ }
36
+ main {
37
+ max-width: 1200px;
38
+ margin: 0 auto;
39
+ padding: 32px 20px 64px;
40
+ }
41
+ h1, h2, h3 { margin: 0 0 12px; }
42
+ section {
43
+ background: var(--panel);
44
+ border: 1px solid var(--border);
45
+ border-radius: 16px;
46
+ padding: 20px;
47
+ margin-bottom: 20px;
48
+ box-shadow: 0 8px 30px rgba(31, 41, 51, 0.06);
49
+ }
50
+ table {
51
+ width: 100%;
52
+ border-collapse: collapse;
53
+ font-size: 14px;
54
+ }
55
+ th, td {
56
+ text-align: left;
57
+ padding: 10px 12px;
58
+ border-bottom: 1px solid var(--border);
59
+ vertical-align: top;
60
+ }
61
+ th { color: var(--muted); }
62
+ a { color: var(--accent); text-decoration: none; }
63
+ a:hover { text-decoration: underline; }
64
+ .metric-grid {
65
+ display: grid;
66
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
67
+ gap: 12px;
68
+ margin-top: 16px;
69
+ }
70
+ .metric {
71
+ border: 1px solid var(--border);
72
+ border-radius: 12px;
73
+ padding: 14px;
74
+ background: #fff9f0;
75
+ }
76
+ .metric strong {
77
+ display: block;
78
+ font-size: 28px;
79
+ margin-top: 4px;
80
+ }
81
+ .pill {
82
+ display: inline-block;
83
+ border-radius: 999px;
84
+ padding: 2px 10px;
85
+ font-size: 12px;
86
+ font-weight: 700;
87
+ }
88
+ .pill-covered { color: var(--covered); background: var(--covered-bg); }
89
+ .pill-uncovered { color: var(--uncovered); background: var(--uncovered-bg); }
90
+ .branches td:last-child { white-space: nowrap; }
91
+ .code {
92
+ background: var(--code-bg);
93
+ border-radius: 12px;
94
+ overflow: auto;
95
+ border: 1px solid var(--border);
96
+ }
97
+ .code table { border-collapse: separate; border-spacing: 0; }
98
+ .code td {
99
+ border: 0;
100
+ padding: 0;
101
+ font-family: ui-monospace, SFMono-Regular, monospace;
102
+ font-size: 13px;
103
+ }
104
+ .line-no {
105
+ width: 64px;
106
+ min-width: 64px;
107
+ text-align: right;
108
+ color: var(--muted);
109
+ padding: 0 12px;
110
+ user-select: none;
111
+ border-right: 1px solid var(--border);
112
+ }
113
+ .line-code {
114
+ white-space: pre;
115
+ padding: 0 12px;
116
+ }
117
+ tr.covered { background: var(--covered-bg); }
118
+ tr.uncovered { background: var(--uncovered-bg); }
119
+ .empty { color: var(--muted); }
120
+ </style>
121
+ </head>
122
+ <body>
123
+ <main>
124
+ <section>
125
+ <h1>branch-never report</h1>
126
+ <p>Detailed branch coverage report for uncovered and covered heuristic branches.</p>
127
+ <div class="metric-grid">
128
+ <div class="metric"><span>Total branches</span><strong>${payload.summary.total}</strong></div>
129
+ <div class="metric"><span>Covered</span><strong>${payload.summary.coveredCount}</strong></div>
130
+ <div class="metric"><span>Uncovered</span><strong>${payload.summary.uncoveredCount}</strong></div>
131
+ <div class="metric"><span>Coverage</span><strong>${payload.summary.coveragePercent}%</strong></div>
132
+ </div>
133
+ </section>
134
+ ${options.baselineComparison ? renderTrendSection(options.baselineComparison) : ""}
135
+ <section>
136
+ <h2>Files</h2>
137
+ <table>
138
+ <thead>
139
+ <tr><th>File</th><th>Coverage</th><th>Covered</th><th>Uncovered</th></tr>
140
+ </thead>
141
+ <tbody>
142
+ ${files
143
+ .map((file) => `<tr>
144
+ <td><a href="#file-${slug(file.file)}">${escapeHtml(file.file)}</a></td>
145
+ <td>${file.coveragePercent}%</td>
146
+ <td>${file.coveredCount}</td>
147
+ <td>${file.uncoveredCount}</td>
148
+ </tr>`)
149
+ .join("")}
150
+ </tbody>
151
+ </table>
152
+ </section>
153
+ <section>
154
+ <h2>Uncovered branches</h2>
155
+ ${payload.uncovered.length === 0
156
+ ? `<p class="empty">No uncovered branches found.</p>`
157
+ : `<table class="branches">
158
+ <thead>
159
+ <tr><th>Location</th><th>Condition</th><th>Pattern</th><th>Status</th></tr>
160
+ </thead>
161
+ <tbody>
162
+ ${payload.uncovered
163
+ .map((branch) => `<tr>
164
+ <td><a href="#branch-${branchAnchor(branch)}">${escapeHtml(branch.file)}:${branch.line}</a></td>
165
+ <td>${escapeHtml(branch.conditionText)}</td>
166
+ <td>${escapeHtml(branch.pattern)}</td>
167
+ <td><span class="pill pill-uncovered">uncovered</span></td>
168
+ </tr>`)
169
+ .join("")}
170
+ </tbody>
171
+ </table>`}
172
+ </section>
173
+ ${sourceSections.join("\n")}
174
+ </main>
175
+ </body>
176
+ </html>`;
177
+ }
178
+ function summarizeFiles(branches) {
179
+ const grouped = new Map();
180
+ for (const branch of branches) {
181
+ const current = grouped.get(branch.file) ?? { coveredCount: 0, uncoveredCount: 0 };
182
+ if (branch.covered) {
183
+ current.coveredCount += 1;
184
+ }
185
+ else {
186
+ current.uncoveredCount += 1;
187
+ }
188
+ grouped.set(branch.file, current);
189
+ }
190
+ return [...grouped.entries()]
191
+ .map(([file, counts]) => {
192
+ const total = counts.coveredCount + counts.uncoveredCount;
193
+ return {
194
+ file,
195
+ coveredCount: counts.coveredCount,
196
+ uncoveredCount: counts.uncoveredCount,
197
+ total,
198
+ coveragePercent: total === 0 ? 100 : Math.round((counts.coveredCount / total) * 100)
199
+ };
200
+ })
201
+ .sort((left, right) => left.file.localeCompare(right.file));
202
+ }
203
+ async function renderSourceSection(file, payload, cwd) {
204
+ const branches = [...payload.covered, ...payload.uncovered]
205
+ .filter((branch) => branch.file === file)
206
+ .sort((left, right) => left.line - right.line);
207
+ const branchMap = new Map();
208
+ for (const branch of branches) {
209
+ const atLine = branchMap.get(branch.line) ?? [];
210
+ atLine.push(branch);
211
+ branchMap.set(branch.line, atLine);
212
+ }
213
+ const source = await readFile(path.resolve(cwd, file), "utf8");
214
+ const lines = source.split("\n");
215
+ const rows = lines
216
+ .map((line, index) => {
217
+ const lineNumber = index + 1;
218
+ const lineBranches = branchMap.get(lineNumber) ?? [];
219
+ const status = lineBranches.some((branch) => !branch.covered)
220
+ ? "uncovered"
221
+ : lineBranches.some((branch) => branch.covered)
222
+ ? "covered"
223
+ : "";
224
+ const anchors = lineBranches
225
+ .map((branch) => `<a id="branch-${branchAnchor(branch)}"></a>`)
226
+ .join("");
227
+ return `<tr class="${status}">
228
+ <td class="line-no" id="file-${slug(file)}-line-${lineNumber}">${anchors}${lineNumber}</td>
229
+ <td class="line-code">${escapeHtml(line)}</td>
230
+ </tr>`;
231
+ })
232
+ .join("");
233
+ const branchDetails = branches
234
+ .map((branch) => `<tr>
235
+ <td><a href="#branch-${branchAnchor(branch)}">${branch.line}</a></td>
236
+ <td>${escapeHtml(branch.conditionText)}</td>
237
+ <td>${escapeHtml(branch.pattern)}</td>
238
+ <td><span class="pill ${branch.covered ? "pill-covered" : "pill-uncovered"}">${branch.covered ? "covered" : "uncovered"}</span></td>
239
+ </tr>`)
240
+ .join("");
241
+ return `<section id="file-${slug(file)}">
242
+ <h2>${escapeHtml(file)}</h2>
243
+ <table class="branches">
244
+ <thead><tr><th>Line</th><th>Condition</th><th>Pattern</th><th>Status</th></tr></thead>
245
+ <tbody>${branchDetails}</tbody>
246
+ </table>
247
+ <div class="code">
248
+ <table><tbody>${rows}</tbody></table>
249
+ </div>
250
+ </section>`;
251
+ }
252
+ function renderTrendSection(comparison) {
253
+ const tone = comparison.netChange > 0 ? "pill-covered" : comparison.netChange < 0 ? "pill-uncovered" : "";
254
+ const label = comparison.netChange > 0 ? "improvement" : comparison.netChange < 0 ? "regression" : "no change";
255
+ return `<section>
256
+ <h2>Trend</h2>
257
+ <p>Comparing branch coverage: ${escapeHtml(comparison.baselineRef)} &rarr; HEAD</p>
258
+ <div class="metric-grid">
259
+ <div class="metric"><span>Newly uncovered</span><strong>${comparison.newlyUncovered.length}</strong></div>
260
+ <div class="metric"><span>Newly covered</span><strong>${comparison.newlyCovered.length}</strong></div>
261
+ <div class="metric"><span>Net</span><strong><span class="pill ${tone}">${comparison.netChange > 0 ? "+" : ""}${comparison.netChange}</span></strong></div>
262
+ </div>
263
+ <p>${escapeHtml(label)}</p>
264
+ </section>`;
265
+ }
266
+ function branchAnchor(branch) {
267
+ return slug(`${branch.file}-${branch.line}-${branch.pattern}-${branch.kind}-${branch.conditionText}`);
268
+ }
269
+ function slug(value) {
270
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
271
+ }
272
+ function escapeHtml(value) {
273
+ return value
274
+ .replace(/&/g, "&amp;")
275
+ .replace(/</g, "&lt;")
276
+ .replace(/>/g, "&gt;")
277
+ .replace(/"/g, "&quot;")
278
+ .replace(/'/g, "&#39;");
279
+ }
280
+ //# sourceMappingURL=html-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-report.js","sourceRoot":"","sources":["../src/html-report.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import { writeFile } from "node:fs/promises";
3
+ import { attachBlameToBranches } from "./blame.js";
2
4
  import { Command } from "commander";
3
- import { extractBranchesFromDirectory } from "./extractor.js";
4
- import { formatJson, formatText } from "./formatter.js";
5
- import { loadTestFiles, matchBranchesToTests } from "./matcher.js";
6
- import { calculateCoverage } from "./scorer.js";
5
+ import { analyzeAgainstBaseline, analyzeBranchCoverage, analyzePrCheck, formatBaselineComparison, formatPrCheckComparison } from "./baseline.js";
6
+ import { formatBlameText, formatJson, formatText } from "./formatter.js";
7
+ import { generateHtmlReport } from "./html-report.js";
7
8
  const program = new Command();
8
9
  program
9
10
  .name("branch-never")
@@ -13,6 +14,10 @@ program
13
14
  .option("--no-fail", "Don't exit 1 when uncovered branches found")
14
15
  .option("--pattern <type>", "Only check: env|retry|error|feature|all", "all")
15
16
  .option("--threshold <pct>", "Fail if coverage below N% (default: 0, fail on any)", "0")
17
+ .option("--report <output-file>", "Write an HTML report to a file")
18
+ .option("--baseline <branch>", "Compare current uncovered branches against a git ref")
19
+ .option("--blame", "Show git blame metadata for each unreachable branch", false)
20
+ .option("--pr-check", "Compare current unreachable branches against a baseline JSON file", false)
16
21
  .action(async (srcDir, options) => {
17
22
  const threshold = Number(options.threshold);
18
23
  if (!Number.isFinite(threshold) || threshold < 0 || threshold > 100) {
@@ -25,21 +30,83 @@ program
25
30
  process.exitCode = 2;
26
31
  return;
27
32
  }
28
- const branches = await extractBranchesFromDirectory(srcDir, options.pattern);
29
- const testFiles = await loadTestFiles(options.tests);
30
- const results = matchBranchesToTests(branches, testFiles);
31
- const covered = results.filter((result) => result.covered);
32
- const uncovered = results.filter((result) => !result.covered);
33
- const summary = calculateCoverage(results);
34
- const payload = { covered, uncovered, summary };
35
- if (options.json) {
36
- console.log(formatJson(payload));
33
+ let payload;
34
+ let comparison;
35
+ let prCheck;
36
+ let blamedUncovered = null;
37
+ if (options.prCheck && !options.baseline) {
38
+ console.error("Missing --baseline value. --pr-check expects a baseline JSON file path.");
39
+ process.exitCode = 2;
40
+ return;
41
+ }
42
+ if (options.prCheck && options.baseline) {
43
+ prCheck = await analyzePrCheck({
44
+ srcDir,
45
+ testsDir: options.tests,
46
+ pattern: options.pattern,
47
+ baselinePath: options.baseline
48
+ });
49
+ payload = prCheck.current;
50
+ }
51
+ else if (options.baseline) {
52
+ comparison = await analyzeAgainstBaseline({
53
+ srcDir,
54
+ testsDir: options.tests,
55
+ pattern: options.pattern,
56
+ baselineRef: options.baseline
57
+ });
58
+ payload = comparison.current;
37
59
  }
38
60
  else {
61
+ payload = await analyzeBranchCoverage({
62
+ srcDir,
63
+ testsDir: options.tests,
64
+ pattern: options.pattern
65
+ });
66
+ }
67
+ if (options.blame) {
68
+ blamedUncovered = await attachBlameToBranches(payload.uncovered);
69
+ }
70
+ const shouldPrintText = !options.report || Boolean(options.baseline);
71
+ if (options.json) {
72
+ const basePayload = blamedUncovered ? { ...payload, uncovered: blamedUncovered } : payload;
73
+ if (prCheck) {
74
+ console.log(formatJson({ ...basePayload, prCheck }));
75
+ }
76
+ else {
77
+ console.log(formatJson(comparison ? { ...basePayload, comparison } : basePayload));
78
+ }
79
+ }
80
+ else if (prCheck && shouldPrintText) {
81
+ console.log(formatPrCheckComparison(prCheck));
82
+ }
83
+ else if (comparison && shouldPrintText) {
84
+ console.log(formatBaselineComparison(comparison));
85
+ }
86
+ else if (blamedUncovered && shouldPrintText) {
87
+ console.log(formatBlameText(blamedUncovered));
88
+ }
89
+ else if (shouldPrintText) {
39
90
  console.log(formatText(payload));
40
91
  }
41
- const thresholdTriggered = threshold > 0 ? summary.coveragePercent < threshold : uncovered.length > 0;
42
- if (options.fail && thresholdTriggered) {
92
+ if (options.report) {
93
+ const html = await generateHtmlReport(payload, { baselineComparison: comparison });
94
+ await writeFile(options.report, html, "utf8");
95
+ const target = `Generated: ${options.report}`;
96
+ if (options.json) {
97
+ console.error(target);
98
+ }
99
+ else {
100
+ console.log(target);
101
+ }
102
+ }
103
+ const thresholdTriggered = threshold > 0 ? payload.summary.coveragePercent < threshold : payload.uncovered.length > 0;
104
+ if (prCheck) {
105
+ if (prCheck.newUnreachable.length > 0) {
106
+ process.exitCode = 1;
107
+ }
108
+ }
109
+ else if (options.fail && thresholdTriggered) {
43
110
  process.exitCode = 1;
44
111
  }
45
112
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/matcher.d.ts CHANGED
@@ -11,5 +11,5 @@ export interface TestFileRecord {
11
11
  }
12
12
  export declare function extractConditionTokens(conditionText: string): string[];
13
13
  export declare function collectTestTokens(contents: string): Set<string>;
14
- export declare function loadTestFiles(testDir: string): Promise<Map<string, TestFileRecord>>;
14
+ export declare function loadTestFiles(testDir: string, cwd?: string): Promise<Map<string, TestFileRecord>>;
15
15
  export declare function matchBranchesToTests(branches: BranchMatch[], testFiles: Map<string, string | TestFileRecord>): BranchCoverage[];
package/dist/matcher.js CHANGED
@@ -55,8 +55,8 @@ export function collectTestTokens(contents) {
55
55
  }
56
56
  return tokens;
57
57
  }
58
- export async function loadTestFiles(testDir) {
59
- const absoluteDir = path.resolve(testDir);
58
+ export async function loadTestFiles(testDir, cwd = process.cwd()) {
59
+ const absoluteDir = path.resolve(cwd, testDir);
60
60
  const files = await glob(FILE_GLOB, {
61
61
  absolute: true,
62
62
  cwd: absoluteDir,
@@ -66,7 +66,7 @@ export async function loadTestFiles(testDir) {
66
66
  const entries = await Promise.all(files.map(async (file) => {
67
67
  const contents = await readFile(file, "utf8");
68
68
  return [
69
- path.relative(process.cwd(), file),
69
+ path.relative(cwd, file),
70
70
  {
71
71
  contents,
72
72
  tokens: collectTestTokens(contents)
@@ -1 +1 @@
1
- {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "branch-never",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "branch-never": "dist/index.js"