lintro 0.3.2__py3-none-any.whl
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.
Potentially problematic release.
This version of lintro might be problematic. Click here for more details.
- lintro/__init__.py +3 -0
- lintro/__main__.py +6 -0
- lintro/ascii-art/fail.txt +404 -0
- lintro/ascii-art/success.txt +484 -0
- lintro/cli.py +70 -0
- lintro/cli_utils/__init__.py +7 -0
- lintro/cli_utils/commands/__init__.py +7 -0
- lintro/cli_utils/commands/check.py +210 -0
- lintro/cli_utils/commands/format.py +167 -0
- lintro/cli_utils/commands/list_tools.py +114 -0
- lintro/enums/__init__.py +0 -0
- lintro/enums/action.py +29 -0
- lintro/enums/darglint_strictness.py +22 -0
- lintro/enums/group_by.py +31 -0
- lintro/enums/hadolint_enums.py +46 -0
- lintro/enums/output_format.py +40 -0
- lintro/enums/tool_name.py +36 -0
- lintro/enums/tool_type.py +27 -0
- lintro/enums/yamllint_format.py +22 -0
- lintro/exceptions/__init__.py +0 -0
- lintro/exceptions/errors.py +15 -0
- lintro/formatters/__init__.py +0 -0
- lintro/formatters/core/__init__.py +0 -0
- lintro/formatters/core/output_style.py +21 -0
- lintro/formatters/core/table_descriptor.py +24 -0
- lintro/formatters/styles/__init__.py +17 -0
- lintro/formatters/styles/csv.py +41 -0
- lintro/formatters/styles/grid.py +91 -0
- lintro/formatters/styles/html.py +48 -0
- lintro/formatters/styles/json.py +61 -0
- lintro/formatters/styles/markdown.py +41 -0
- lintro/formatters/styles/plain.py +39 -0
- lintro/formatters/tools/__init__.py +35 -0
- lintro/formatters/tools/darglint_formatter.py +72 -0
- lintro/formatters/tools/hadolint_formatter.py +84 -0
- lintro/formatters/tools/prettier_formatter.py +76 -0
- lintro/formatters/tools/ruff_formatter.py +116 -0
- lintro/formatters/tools/yamllint_formatter.py +87 -0
- lintro/models/__init__.py +0 -0
- lintro/models/core/__init__.py +0 -0
- lintro/models/core/tool.py +104 -0
- lintro/models/core/tool_config.py +23 -0
- lintro/models/core/tool_result.py +39 -0
- lintro/parsers/__init__.py +0 -0
- lintro/parsers/darglint/__init__.py +0 -0
- lintro/parsers/darglint/darglint_issue.py +9 -0
- lintro/parsers/darglint/darglint_parser.py +62 -0
- lintro/parsers/hadolint/__init__.py +1 -0
- lintro/parsers/hadolint/hadolint_issue.py +24 -0
- lintro/parsers/hadolint/hadolint_parser.py +65 -0
- lintro/parsers/prettier/__init__.py +0 -0
- lintro/parsers/prettier/prettier_issue.py +10 -0
- lintro/parsers/prettier/prettier_parser.py +60 -0
- lintro/parsers/ruff/__init__.py +1 -0
- lintro/parsers/ruff/ruff_issue.py +43 -0
- lintro/parsers/ruff/ruff_parser.py +89 -0
- lintro/parsers/yamllint/__init__.py +0 -0
- lintro/parsers/yamllint/yamllint_issue.py +24 -0
- lintro/parsers/yamllint/yamllint_parser.py +68 -0
- lintro/tools/__init__.py +40 -0
- lintro/tools/core/__init__.py +0 -0
- lintro/tools/core/tool_base.py +320 -0
- lintro/tools/core/tool_manager.py +167 -0
- lintro/tools/implementations/__init__.py +0 -0
- lintro/tools/implementations/tool_darglint.py +245 -0
- lintro/tools/implementations/tool_hadolint.py +302 -0
- lintro/tools/implementations/tool_prettier.py +270 -0
- lintro/tools/implementations/tool_ruff.py +618 -0
- lintro/tools/implementations/tool_yamllint.py +240 -0
- lintro/tools/tool_enum.py +17 -0
- lintro/utils/__init__.py +0 -0
- lintro/utils/ascii_normalize_cli.py +84 -0
- lintro/utils/config.py +39 -0
- lintro/utils/console_logger.py +783 -0
- lintro/utils/formatting.py +173 -0
- lintro/utils/output_manager.py +301 -0
- lintro/utils/path_utils.py +41 -0
- lintro/utils/tool_executor.py +443 -0
- lintro/utils/tool_utils.py +431 -0
- lintro-0.3.2.dist-info/METADATA +338 -0
- lintro-0.3.2.dist-info/RECORD +85 -0
- lintro-0.3.2.dist-info/WHEEL +5 -0
- lintro-0.3.2.dist-info/entry_points.txt +2 -0
- lintro-0.3.2.dist-info/licenses/LICENSE +21 -0
- lintro-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Prettier code formatter integration."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
from lintro.enums.tool_type import ToolType
|
|
9
|
+
from lintro.models.core.tool import Tool, ToolConfig, ToolResult
|
|
10
|
+
from lintro.parsers.prettier.prettier_parser import parse_prettier_output
|
|
11
|
+
from lintro.tools.core.tool_base import BaseTool
|
|
12
|
+
from lintro.utils.tool_utils import walk_files_with_excludes
|
|
13
|
+
|
|
14
|
+
# Constants for Prettier configuration
|
|
15
|
+
PRETTIER_DEFAULT_TIMEOUT: int = 30
|
|
16
|
+
PRETTIER_DEFAULT_PRIORITY: int = 80
|
|
17
|
+
PRETTIER_FILE_PATTERNS: list[str] = [
|
|
18
|
+
"*.js",
|
|
19
|
+
"*.jsx",
|
|
20
|
+
"*.ts",
|
|
21
|
+
"*.tsx",
|
|
22
|
+
"*.css",
|
|
23
|
+
"*.scss",
|
|
24
|
+
"*.less",
|
|
25
|
+
"*.html",
|
|
26
|
+
"*.json",
|
|
27
|
+
"*.yaml",
|
|
28
|
+
"*.yml",
|
|
29
|
+
"*.md",
|
|
30
|
+
"*.graphql",
|
|
31
|
+
"*.vue",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class PrettierTool(BaseTool):
|
|
37
|
+
"""Prettier code formatter integration.
|
|
38
|
+
|
|
39
|
+
A code formatter that supports multiple languages (JavaScript, TypeScript,
|
|
40
|
+
CSS, HTML, etc.).
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
name: str = "prettier"
|
|
44
|
+
description: str = (
|
|
45
|
+
"Code formatter that supports multiple languages (JavaScript, TypeScript, "
|
|
46
|
+
"CSS, HTML, etc.)"
|
|
47
|
+
)
|
|
48
|
+
can_fix: bool = True
|
|
49
|
+
config: ToolConfig = field(
|
|
50
|
+
default_factory=lambda: ToolConfig(
|
|
51
|
+
priority=PRETTIER_DEFAULT_PRIORITY, # High priority
|
|
52
|
+
conflicts_with=[], # No direct conflicts
|
|
53
|
+
file_patterns=PRETTIER_FILE_PATTERNS, # Applies to many file types
|
|
54
|
+
tool_type=ToolType.FORMATTER,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def set_options(
|
|
59
|
+
self,
|
|
60
|
+
exclude_patterns: list[str] | None = None,
|
|
61
|
+
include_venv: bool = False,
|
|
62
|
+
timeout: int | None = None,
|
|
63
|
+
verbose_fix_output: bool | None = None,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Set options for the core.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
exclude_patterns: List of patterns to exclude
|
|
69
|
+
include_venv: Whether to include virtual environment directories
|
|
70
|
+
timeout: Timeout in seconds per file (default: 30)
|
|
71
|
+
verbose_fix_output: If True, include raw Prettier output in fix()
|
|
72
|
+
"""
|
|
73
|
+
self.exclude_patterns = exclude_patterns or []
|
|
74
|
+
self.include_venv = include_venv
|
|
75
|
+
if timeout is not None:
|
|
76
|
+
self.timeout = timeout
|
|
77
|
+
if verbose_fix_output is not None:
|
|
78
|
+
self.options["verbose_fix_output"] = verbose_fix_output
|
|
79
|
+
|
|
80
|
+
def _find_config(self) -> str | None:
|
|
81
|
+
"""Locate a Prettier config if none is found by native discovery.
|
|
82
|
+
|
|
83
|
+
Wrapper-first default: rely on Prettier's native discovery via cwd. Only
|
|
84
|
+
return a config path if we later decide to ship a default config and the
|
|
85
|
+
user has no config present. For now, return None to avoid forcing config.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
str | None: Path to a discovered configuration file, or None if
|
|
89
|
+
no explicit configuration should be enforced.
|
|
90
|
+
"""
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
def check(
|
|
94
|
+
self,
|
|
95
|
+
paths: list[str],
|
|
96
|
+
) -> ToolResult:
|
|
97
|
+
"""Check files with Prettier without making changes.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
paths: List of file or directory paths to check
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
ToolResult instance
|
|
104
|
+
"""
|
|
105
|
+
self._validate_paths(paths=paths)
|
|
106
|
+
prettier_files: list[str] = walk_files_with_excludes(
|
|
107
|
+
paths=paths,
|
|
108
|
+
file_patterns=self.config.file_patterns,
|
|
109
|
+
exclude_patterns=self.exclude_patterns,
|
|
110
|
+
include_venv=self.include_venv,
|
|
111
|
+
)
|
|
112
|
+
if not prettier_files:
|
|
113
|
+
return Tool.to_result(
|
|
114
|
+
name=self.name,
|
|
115
|
+
success=True,
|
|
116
|
+
output="No files to check.",
|
|
117
|
+
issues_count=0,
|
|
118
|
+
)
|
|
119
|
+
# Use relative paths and set cwd to the common parent
|
|
120
|
+
cwd: str = self.get_cwd(paths=prettier_files)
|
|
121
|
+
rel_files: list[str] = [
|
|
122
|
+
os.path.relpath(f, cwd) if cwd else f for f in prettier_files
|
|
123
|
+
]
|
|
124
|
+
# Resolve executable in a manner consistent with other tools
|
|
125
|
+
cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
126
|
+
"--check",
|
|
127
|
+
]
|
|
128
|
+
# Do not force config; rely on native discovery via cwd
|
|
129
|
+
cmd.extend(rel_files)
|
|
130
|
+
logger.debug(f"[PrettierTool] Running: {' '.join(cmd)} (cwd={cwd})")
|
|
131
|
+
result = self._run_subprocess(
|
|
132
|
+
cmd=cmd,
|
|
133
|
+
timeout=self.options.get("timeout", self._default_timeout),
|
|
134
|
+
cwd=cwd,
|
|
135
|
+
)
|
|
136
|
+
output: str = result[1]
|
|
137
|
+
# Do not filter lines post-hoc; rely on discovery and ignore files
|
|
138
|
+
issues: list = parse_prettier_output(output=output)
|
|
139
|
+
issues_count: int = len(issues)
|
|
140
|
+
success: bool = issues_count == 0
|
|
141
|
+
# Standardize: suppress Prettier's informational output when no issues
|
|
142
|
+
# so the unified logger prints a single, consistent success line.
|
|
143
|
+
if success:
|
|
144
|
+
output = None
|
|
145
|
+
return Tool.to_result(
|
|
146
|
+
name=self.name,
|
|
147
|
+
success=success,
|
|
148
|
+
output=output,
|
|
149
|
+
issues_count=issues_count,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def fix(
|
|
153
|
+
self,
|
|
154
|
+
paths: list[str],
|
|
155
|
+
) -> ToolResult:
|
|
156
|
+
"""Format files with Prettier.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
paths: List of file or directory paths to format
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
ToolResult: Result object with counts and messages.
|
|
163
|
+
"""
|
|
164
|
+
self._validate_paths(paths=paths)
|
|
165
|
+
prettier_files: list[str] = walk_files_with_excludes(
|
|
166
|
+
paths=paths,
|
|
167
|
+
file_patterns=self.config.file_patterns,
|
|
168
|
+
exclude_patterns=self.exclude_patterns,
|
|
169
|
+
include_venv=self.include_venv,
|
|
170
|
+
)
|
|
171
|
+
if not prettier_files:
|
|
172
|
+
return Tool.to_result(
|
|
173
|
+
name=self.name,
|
|
174
|
+
success=True,
|
|
175
|
+
output="No files to format.",
|
|
176
|
+
issues_count=0,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# First, check for issues before fixing
|
|
180
|
+
cwd: str = self.get_cwd(paths=prettier_files)
|
|
181
|
+
rel_files: list[str] = [
|
|
182
|
+
os.path.relpath(f, cwd) if cwd else f for f in prettier_files
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Check for issues first
|
|
186
|
+
check_cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
187
|
+
"--check",
|
|
188
|
+
]
|
|
189
|
+
# Do not force config; rely on native discovery via cwd
|
|
190
|
+
check_cmd.extend(rel_files)
|
|
191
|
+
logger.debug(f"[PrettierTool] Checking: {' '.join(check_cmd)} (cwd={cwd})")
|
|
192
|
+
check_result = self._run_subprocess(
|
|
193
|
+
cmd=check_cmd,
|
|
194
|
+
timeout=self.options.get("timeout", self._default_timeout),
|
|
195
|
+
cwd=cwd,
|
|
196
|
+
)
|
|
197
|
+
check_output: str = check_result[1]
|
|
198
|
+
|
|
199
|
+
# Parse initial issues
|
|
200
|
+
initial_issues: list = parse_prettier_output(output=check_output)
|
|
201
|
+
initial_count: int = len(initial_issues)
|
|
202
|
+
|
|
203
|
+
# Now fix the issues
|
|
204
|
+
fix_cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
205
|
+
"--write",
|
|
206
|
+
]
|
|
207
|
+
fix_cmd.extend(rel_files)
|
|
208
|
+
logger.debug(f"[PrettierTool] Fixing: {' '.join(fix_cmd)} (cwd={cwd})")
|
|
209
|
+
fix_result = self._run_subprocess(
|
|
210
|
+
cmd=fix_cmd,
|
|
211
|
+
timeout=self.options.get("timeout", self._default_timeout),
|
|
212
|
+
cwd=cwd,
|
|
213
|
+
)
|
|
214
|
+
fix_output: str = fix_result[1]
|
|
215
|
+
|
|
216
|
+
# Check for remaining issues after fixing
|
|
217
|
+
final_check_result = self._run_subprocess(
|
|
218
|
+
cmd=check_cmd,
|
|
219
|
+
timeout=self.options.get("timeout", self._default_timeout),
|
|
220
|
+
cwd=cwd,
|
|
221
|
+
)
|
|
222
|
+
final_check_output: str = final_check_result[1]
|
|
223
|
+
remaining_issues: list = parse_prettier_output(output=final_check_output)
|
|
224
|
+
remaining_count: int = len(remaining_issues)
|
|
225
|
+
|
|
226
|
+
# Calculate fixed issues
|
|
227
|
+
fixed_count: int = max(0, initial_count - remaining_count)
|
|
228
|
+
|
|
229
|
+
# Build output message
|
|
230
|
+
output_lines: list[str] = []
|
|
231
|
+
if fixed_count > 0:
|
|
232
|
+
output_lines.append(f"Fixed {fixed_count} formatting issue(s)")
|
|
233
|
+
|
|
234
|
+
if remaining_count > 0:
|
|
235
|
+
output_lines.append(
|
|
236
|
+
f"Found {remaining_count} issue(s) that cannot be auto-fixed"
|
|
237
|
+
)
|
|
238
|
+
for issue in remaining_issues[:5]:
|
|
239
|
+
output_lines.append(f" {issue.file} - {issue.message}")
|
|
240
|
+
if len(remaining_issues) > 5:
|
|
241
|
+
output_lines.append(f" ... and {len(remaining_issues) - 5} more")
|
|
242
|
+
|
|
243
|
+
# If there were no initial issues, rely on the logger's unified
|
|
244
|
+
# success line (avoid duplicate "No issues found" lines here)
|
|
245
|
+
elif remaining_count == 0 and fixed_count > 0:
|
|
246
|
+
output_lines.append("All formatting issues were successfully auto-fixed")
|
|
247
|
+
|
|
248
|
+
# Add verbose raw formatting output only when explicitly requested
|
|
249
|
+
if (
|
|
250
|
+
self.options.get("verbose_fix_output", False)
|
|
251
|
+
and fix_output
|
|
252
|
+
and fix_output.strip()
|
|
253
|
+
):
|
|
254
|
+
output_lines.append(f"Formatting output:\n{fix_output}")
|
|
255
|
+
|
|
256
|
+
final_output: str | None = "\n".join(output_lines) if output_lines else None
|
|
257
|
+
|
|
258
|
+
# Success means no remaining issues
|
|
259
|
+
success: bool = remaining_count == 0
|
|
260
|
+
|
|
261
|
+
return ToolResult(
|
|
262
|
+
name=self.name,
|
|
263
|
+
success=success,
|
|
264
|
+
output=final_output,
|
|
265
|
+
# For fix operations, issues_count represents remaining for summaries
|
|
266
|
+
issues_count=remaining_count,
|
|
267
|
+
initial_issues_count=initial_count,
|
|
268
|
+
fixed_issues_count=fixed_count,
|
|
269
|
+
remaining_issues_count=remaining_count,
|
|
270
|
+
)
|