Coverage for lintro / parsers / oxfmt / oxfmt_parser.py: 90%

29 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Parser for oxfmt output. 

2 

3Handles oxfmt CLI output in --list-different mode which outputs 

4one file path per line for files that need formatting. 

5""" 

6 

7from loguru import logger 

8 

9from lintro.parsers.base_parser import strip_ansi_codes 

10from lintro.parsers.oxfmt.oxfmt_issue import OxfmtIssue 

11 

12# Known error message patterns from oxfmt that should be ignored 

13_ERROR_PATTERNS: tuple[str, ...] = ( 

14 "Expected at least one target file", 

15 "error:", 

16 "Error:", 

17 "ERROR:", 

18 "warning:", 

19 "Warning:", 

20 "WARNING:", 

21 "Usage:", 

22 "usage:", 

23 "USAGE:", 

24) 

25 

26# Valid file extensions that oxfmt processes 

27_VALID_EXTENSIONS: tuple[str, ...] = ( 

28 ".js", 

29 ".mjs", 

30 ".cjs", 

31 ".jsx", 

32 ".ts", 

33 ".mts", 

34 ".cts", 

35 ".tsx", 

36 ".vue", 

37) 

38 

39 

40def _is_valid_file_path(line: str) -> bool: 

41 """Check if a line looks like a valid file path that oxfmt would process. 

42 

43 Args: 

44 line: The line to check. 

45 

46 Returns: 

47 True if the line appears to be a valid file path, False otherwise. 

48 """ 

49 # Skip known error message patterns 

50 for pattern in _ERROR_PATTERNS: 

51 if pattern in line: 

52 return False 

53 

54 # Check if it has a valid extension 

55 lower_line = line.lower() 

56 return any(lower_line.endswith(ext) for ext in _VALID_EXTENSIONS) 

57 

58 

59def parse_oxfmt_output(output: str | None) -> list[OxfmtIssue]: 

60 """Parse oxfmt output into a list of OxfmtIssue objects. 

61 

62 Args: 

63 output: The raw output from oxfmt --list-different. 

64 

65 Returns: 

66 List of OxfmtIssue objects for each file needing formatting. 

67 """ 

68 issues: list[OxfmtIssue] = [] 

69 

70 if not output: 

71 return issues 

72 

73 # Normalize output by stripping ANSI escape sequences 

74 normalized_output = strip_ansi_codes(output) 

75 

76 for line in normalized_output.splitlines(): 

77 try: 

78 line = line.strip() 

79 if not line: 

80 continue 

81 

82 # Skip lines that don't look like valid file paths 

83 if not _is_valid_file_path(line): 

84 logger.debug(f"Skipping non-file-path line from oxfmt: '{line}'") 

85 continue 

86 

87 # Each valid line is a file path that needs formatting 

88 issues.append( 

89 OxfmtIssue( 

90 file=line, 

91 line=1, 

92 column=1, 

93 message="File is not formatted", 

94 ), 

95 ) 

96 except (AttributeError, TypeError) as e: 

97 logger.debug(f"Failed to parse oxfmt line '{line}': {e}") 

98 continue 

99 

100 return issues