Coverage for lintro / parsers / vue_tsc / vue_tsc_parser.py: 93%
57 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 vue-tsc output.
3Vue-tsc outputs in the same format as tsc (TypeScript compiler), so this
4parser uses the same pattern matching logic.
5"""
7from __future__ import annotations
9import re
11from loguru import logger
13from lintro.parsers.base_parser import strip_ansi_codes
14from lintro.parsers.vue_tsc.vue_tsc_issue import VueTscIssue
16# Error codes that indicate missing dependencies rather than actual type errors
17# These are the same as tsc since vue-tsc uses the same error codes
18DEPENDENCY_ERROR_CODES: frozenset[str] = frozenset(
19 {
20 "TS2307", # Cannot find module 'X' or its corresponding type declarations
21 "TS2688", # Cannot find type definition file for 'X'
22 "TS7016", # Could not find a declaration file for module 'X'
23 },
24)
26# Pattern for vue-tsc output (same as tsc with --pretty false):
27# file.vue(line,col): error TS1234: message
28# file.vue(line,col): warning TS1234: message
29VUE_TSC_ISSUE_PATTERN = re.compile(
30 r"^(?P<file>.+?)\((?P<line>\d+),(?P<column>\d+)\):\s*"
31 r"(?P<severity>error|warning)\s+(?P<code>TS\d+):\s*"
32 r"(?P<message>.+)$",
33)
35# Patterns for extracting module names from dependency error messages
36_MODULE_PATTERN = re.compile(r"Cannot find module ['\"]([^'\"]+)['\"]")
37_TYPEDEF_PATTERN = re.compile(r"type definition file for ['\"]([^'\"]+)['\"]")
38_DECL_PATTERN = re.compile(r"declaration file for module ['\"]([^'\"]+)['\"]")
41def _parse_line(line: str) -> VueTscIssue | None:
42 """Parse a single vue-tsc output line into a VueTscIssue.
44 Args:
45 line: A single line of vue-tsc output.
47 Returns:
48 A VueTscIssue instance or None if the line doesn't match.
49 """
50 line = line.strip()
51 if not line:
52 return None
54 match = VUE_TSC_ISSUE_PATTERN.match(line)
55 if not match:
56 return None
58 try:
59 file_path = match.group("file")
60 line_num = int(match.group("line"))
61 column = int(match.group("column"))
62 severity = match.group("severity")
63 code = match.group("code")
64 message = match.group("message").strip()
66 # Normalize Windows paths to forward slashes
67 file_path = file_path.replace("\\", "/")
69 return VueTscIssue(
70 file=file_path,
71 line=line_num,
72 column=column,
73 code=code,
74 message=message,
75 severity=severity,
76 )
77 except (ValueError, AttributeError) as e:
78 logger.debug(f"Failed to parse vue-tsc line: {e}")
79 return None
82def parse_vue_tsc_output(output: str) -> list[VueTscIssue]:
83 """Parse vue-tsc output into VueTscIssue objects.
85 Args:
86 output: Raw stdout emitted by vue-tsc with --pretty false.
88 Returns:
89 A list of VueTscIssue instances parsed from the output.
91 Examples:
92 >>> output = "src/App.vue(10,5): error TS2322: Type error."
93 >>> issues = parse_vue_tsc_output(output)
94 >>> len(issues)
95 1
96 >>> issues[0].code
97 'TS2322'
98 """
99 if not output or not output.strip():
100 return []
102 # Strip ANSI codes for consistent parsing
103 output = strip_ansi_codes(output)
105 issues: list[VueTscIssue] = []
106 for line in output.splitlines():
107 parsed = _parse_line(line)
108 if parsed:
109 issues.append(parsed)
111 return issues
114def categorize_vue_tsc_issues(
115 issues: list[VueTscIssue],
116) -> tuple[list[VueTscIssue], list[VueTscIssue]]:
117 """Categorize vue-tsc issues into type errors and dependency errors.
119 Separates actual type errors from errors caused by missing dependencies
120 (e.g., when node_modules is not installed).
122 Args:
123 issues: List of VueTscIssue objects to categorize.
125 Returns:
126 A tuple of (type_errors, dependency_errors).
127 """
128 type_errors: list[VueTscIssue] = []
129 dependency_errors: list[VueTscIssue] = []
131 for issue in issues:
132 if issue.code and issue.code in DEPENDENCY_ERROR_CODES:
133 dependency_errors.append(issue)
134 else:
135 type_errors.append(issue)
137 return type_errors, dependency_errors
140def extract_missing_modules(dependency_errors: list[VueTscIssue]) -> list[str]:
141 """Extract module names from dependency error messages.
143 Args:
144 dependency_errors: List of VueTscIssue objects with dependency errors.
146 Returns:
147 List of unique module names that are missing.
148 """
149 modules: set[str] = set()
151 for error in dependency_errors:
152 message = error.message or ""
154 for pattern in (_MODULE_PATTERN, _TYPEDEF_PATTERN, _DECL_PATTERN):
155 match = pattern.search(message)
156 if match:
157 modules.add(match.group(1))
158 break
160 return sorted(modules)