Coverage for lintro / parsers / oxlint / oxlint_parser.py: 81%
59 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Parser for Oxlint JSON output.
3Handles Oxlint JSON format output from --format json flag.
4"""
6import json
7from typing import Any
9from loguru import logger
11from lintro.parsers.oxlint.oxlint_issue import OxlintIssue
14def parse_oxlint_output(output: str) -> list[OxlintIssue]:
15 """Parse Oxlint JSON output into a list of OxlintIssue objects.
17 Args:
18 output: The raw JSON output from Oxlint.
20 Returns:
21 List of OxlintIssue objects.
22 """
23 issues: list[OxlintIssue] = []
25 if not output:
26 return issues
28 try:
29 # Oxlint JSON format is a single object with diagnostics array
30 # Extract JSON from output (Oxlint may add extra text after JSON)
31 json_start = output.find("{")
32 json_end = output.rfind("}") + 1
33 if json_start == -1 or json_end == 0:
34 return issues
35 json_content = output[json_start:json_end]
36 oxlint_data: dict[str, Any] = json.loads(json_content)
37 except json.JSONDecodeError as e:
38 logger.debug(f"Failed to parse Oxlint JSON output: {e}")
39 return issues
40 except (ValueError, TypeError) as e:
41 logger.debug(f"Error processing Oxlint output: {e}")
42 return issues
44 if not isinstance(oxlint_data, dict):
45 logger.debug("Oxlint output is not a dictionary")
46 return issues
48 diagnostics = oxlint_data.get("diagnostics", [])
49 if not isinstance(diagnostics, list):
50 logger.debug("Oxlint diagnostics is not a list")
51 return issues
53 for diagnostic in diagnostics:
54 if not isinstance(diagnostic, dict):
55 continue
56 try:
57 issue = _parse_diagnostic(diagnostic)
58 if issue is not None:
59 issues.append(issue)
60 except (KeyError, TypeError, ValueError) as e:
61 logger.debug(f"Failed to parse Oxlint diagnostic: {e}")
62 continue
64 return issues
67def _parse_diagnostic(diagnostic: dict[str, Any]) -> OxlintIssue | None:
68 """Parse a single Oxlint diagnostic into an OxlintIssue.
70 Args:
71 diagnostic: A single diagnostic dictionary from Oxlint output.
73 Returns:
74 OxlintIssue if parsing succeeds, None otherwise.
75 """
76 # Extract filename
77 file_path = diagnostic.get("filename", "")
78 if not file_path:
79 return None
81 # Extract message and code
82 message = diagnostic.get("message", "")
83 code = diagnostic.get("code", "")
84 severity = diagnostic.get("severity", "warning")
85 help_text = diagnostic.get("help")
87 # Extract line and column from labels array
88 # labels[0].span contains {offset, length, line, column}
89 # Default to 1 for 1-based line/column numbering
90 line = 1
91 column = 1
92 labels = diagnostic.get("labels", [])
93 if isinstance(labels, list) and len(labels) > 0:
94 first_label = labels[0]
95 if isinstance(first_label, dict):
96 span = first_label.get("span", {})
97 if isinstance(span, dict):
98 line = span.get("line", 1)
99 column = span.get("column", 1)
101 # Oxlint does not currently indicate fixable issues in JSON output
102 # Default to False
103 fixable = False
105 return OxlintIssue(
106 file=file_path,
107 line=line,
108 column=column,
109 message=message,
110 code=code,
111 severity=severity,
112 fixable=fixable,
113 help=help_text,
114 )