story-research-zapwall/scripts/lintReportSummary.py
2026-01-08 23:04:56 +01:00

93 lines
2.6 KiB
Python

from __future__ import annotations
from collections import Counter
from dataclasses import dataclass
from pathlib import Path
import re
import sys
@dataclass(frozen=True)
class RuleCounts:
errors: Counter[str]
warnings: Counter[str]
def _extract_rule_from_line(line: str) -> str | None:
# ESLint flat output lines look like:
# "<file>\n 12:34 error Message... rule-name"
# Only parse "location lines" to avoid counting explanatory multiline blocks.
# Require at least two spaces before the rule token; otherwise we might capture the last
# word of the message (e.g. "renders") for multiline explanatory errors that omit rule ids.
m = re.match(r"^\s*\d+:\d+\s+(error|warning)\s+.+\s{2,}([@\w\-/]+)\s*$", line)
if not m:
return None
return m.group(2)
def parse_eslint_output(path: Path) -> RuleCounts:
errors: Counter[str] = Counter()
warnings: Counter[str] = Counter()
with path.open("r", encoding="utf-8", errors="replace") as f:
for line in f:
rule = _extract_rule_from_line(line)
if not rule:
continue
if " error " in line:
errors[rule] += 1
continue
if " warning " in line:
warnings[rule] += 1
return RuleCounts(errors=errors, warnings=warnings)
def _order_bucket(rule: str) -> int:
# User requested ordering:
# - file size last: max-lines
# - function size before last: max-lines-per-function
if rule == "max-lines":
return 2
if rule == "max-lines-per-function":
return 1
return 0
def print_summary(counts: RuleCounts) -> None:
print("ERRORS by rule:")
for rule, cnt in sorted(
counts.errors.items(),
key=lambda kv: (_order_bucket(kv[0]), -kv[1], kv[0]),
):
print(f"{cnt:4d} {rule}")
print("\nWARNINGS by rule:")
for rule, cnt in sorted(counts.warnings.items(), key=lambda kv: (-kv[1], kv[0])):
print(f"{cnt:4d} {rule}")
total_errors = sum(counts.errors.values())
total_warnings = sum(counts.warnings.values())
print(f"\nTotal errors: {total_errors} | Total warnings: {total_warnings}")
def main(argv: list[str]) -> int:
if len(argv) != 2:
print("Usage: python scripts/lintReportSummary.py <path-to-eslint-output.txt>", file=sys.stderr)
return 2
path = Path(argv[1])
if not path.exists():
print(f"File not found: {path}", file=sys.stderr)
return 2
counts = parse_eslint_output(path)
print_summary(counts)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))