Skip to content

Commit d50258d

Browse files
style: apply ruff format to src/deadcode/cli.py
1 parent 155b123 commit d50258d

1 file changed

Lines changed: 103 additions & 29 deletions

File tree

src/deadcode/cli.py

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,30 @@
1818
err_console = Console(stderr=True)
1919

2020
FORMAT_HELP = "Output format: pretty (default), compact, github, or json"
21-
ALL_CATEGORIES = ["unused_export", "dead_route", "orphaned_css", "unreferenced_component"]
21+
ALL_CATEGORIES = [
22+
"unused_export",
23+
"dead_route",
24+
"orphaned_css",
25+
"unreferenced_component",
26+
]
2227
FORMAT_CHOICES = click.Choice(["pretty", "compact", "github", "json"])
2328

2429

2530
@click.group()
2631
@click.option("--project", "-p", default=".", help="Project directory to scan")
27-
@click.option("--ignore", "-i", multiple=True, help="Additional ignore patterns (gitignore-style)")
28-
@click.option("--include", multiple=True, help="Include only matching files (gitignore-style whitelist)")
32+
@click.option(
33+
"--ignore", "-i", multiple=True, help="Additional ignore patterns (gitignore-style)"
34+
)
35+
@click.option(
36+
"--include",
37+
multiple=True,
38+
help="Include only matching files (gitignore-style whitelist)",
39+
)
2940
@click.version_option(__version__, prog_name="deadcode")
3041
@click.pass_context
31-
def cli(ctx: click.Context, project: str, ignore: tuple[str, ...], include: tuple[str, ...]) -> None:
42+
def cli(
43+
ctx: click.Context, project: str, ignore: tuple[str, ...], include: tuple[str, ...]
44+
) -> None:
3245
"""DeadCode — Find and remove dead code in TS/React/Next.js projects.
3346
3447
Scans for unused exports, dead routes, orphaned CSS classes,
@@ -67,11 +80,24 @@ def _get_fail_threshold(ctx: click.Context) -> int:
6780

6881

6982
@cli.command()
70-
@click.option("--json-output", "-j", is_flag=True, help="Alias for --format=json (deprecated)")
83+
@click.option(
84+
"--json-output", "-j", is_flag=True, help="Alias for --format=json (deprecated)"
85+
)
7186
@click.option("--format", type=FORMAT_CHOICES, default="pretty", help=FORMAT_HELP)
72-
@click.option("--category", "-c", type=click.Choice(ALL_CATEGORIES), default=None, help="Filter by category")
73-
@click.option("--fail", "fail_threshold", type=int, default=None,
74-
help="Exit code 1 if findings >= threshold (overrides .deadcode.yml)")
87+
@click.option(
88+
"--category",
89+
"-c",
90+
type=click.Choice(ALL_CATEGORIES),
91+
default=None,
92+
help="Filter by category",
93+
)
94+
@click.option(
95+
"--fail",
96+
"fail_threshold",
97+
type=int,
98+
default=None,
99+
help="Exit code 1 if findings >= threshold (overrides .deadcode.yml)",
100+
)
75101
@click.pass_context
76102
def scan(
77103
ctx: click.Context,
@@ -89,7 +115,9 @@ def scan(
89115
sys.exit(1)
90116

91117
include_patterns = ctx.obj.get("include")
92-
scanner = DeadCodeScanner(project, ignore_patterns=ignore, include_patterns=include_patterns)
118+
scanner = DeadCodeScanner(
119+
project, ignore_patterns=ignore, include_patterns=include_patterns
120+
)
93121
result = scanner.scan()
94122

95123
# Filter by category
@@ -109,8 +137,14 @@ def scan(
109137
output = {
110138
"files_scanned": result.files_scanned,
111139
"findings": [
112-
{"file": f.file, "line": f.line, "name": f.name,
113-
"category": f.category, "detail": f.detail, "removable": f.removable}
140+
{
141+
"file": f.file,
142+
"line": f.line,
143+
"name": f.name,
144+
"category": f.category,
145+
"detail": f.detail,
146+
"removable": f.removable,
147+
}
114148
for f in findings
115149
],
116150
"errors": result.errors,
@@ -138,7 +172,9 @@ def scan(
138172
console.print(f"\n::notice::deadcode: {len(findings)} findings")
139173
else:
140174
# Summary
141-
console.print(f"\n[bold]DeadCode Scan[/bold] — {result.files_scanned} files scanned\n")
175+
console.print(
176+
f"\n[bold]DeadCode Scan[/bold] — {result.files_scanned} files scanned\n"
177+
)
142178

143179
if not findings:
144180
console.print("[green]✓ No dead code found![/green]")
@@ -157,7 +193,9 @@ def scan(
157193

158194
for cat, cat_findings in by_category.items():
159195
label = category_labels.get(cat, cat)
160-
console.print(f"\n[bold yellow]{label}[/bold yellow] ({len(cat_findings)})")
196+
console.print(
197+
f"\n[bold yellow]{label}[/bold yellow] ({len(cat_findings)})"
198+
)
161199

162200
table = Table(show_header=True)
163201
table.add_column("File", style="cyan")
@@ -174,26 +212,43 @@ def scan(
174212

175213
# Total
176214
removable = sum(1 for f in findings if f.removable)
177-
console.print(f"\n[bold]Total:[/bold] {len(findings)} findings ({removable} removable)")
215+
console.print(
216+
f"\n[bold]Total:[/bold] {len(findings)} findings ({removable} removable)"
217+
)
178218

179219
if result.errors:
180-
console.print(f"\n[yellow]{len(result.errors)} scan errors (use --json-output to see)[/yellow]")
220+
console.print(
221+
f"\n[yellow]{len(result.errors)} scan errors (use --json-output to see)[/yellow]"
222+
)
181223

182224
# CI fail threshold
183-
effective_threshold = fail_threshold if fail_threshold is not None else _get_fail_threshold(ctx)
225+
effective_threshold = (
226+
fail_threshold if fail_threshold is not None else _get_fail_threshold(ctx)
227+
)
184228
if effective_threshold >= 0 and len(findings) >= effective_threshold:
185229
if effective_format not in ("json", "github"):
186-
console.print(f"\n[red]FAIL: {len(findings)} findings >= threshold {effective_threshold}[/red]")
230+
console.print(
231+
f"\n[red]FAIL: {len(findings)} findings >= threshold {effective_threshold}[/red]"
232+
)
187233
sys.exit(1)
188234

189235

190236
# ── remove ────────────────────────────────────────────────────────────
191237

192238

193239
@cli.command()
194-
@click.option("--dry-run", is_flag=True, help="Preview what would be removed without making changes")
195-
@click.option("--category", "-c", type=click.Choice(ALL_CATEGORIES),
196-
default=None, help="Only remove findings in this category")
240+
@click.option(
241+
"--dry-run",
242+
is_flag=True,
243+
help="Preview what would be removed without making changes",
244+
)
245+
@click.option(
246+
"--category",
247+
"-c",
248+
type=click.Choice(ALL_CATEGORIES),
249+
default=None,
250+
help="Only remove findings in this category",
251+
)
197252
@click.pass_context
198253
def remove(ctx: click.Context, dry_run: bool, category: str | None) -> None:
199254
"""Remove dead code (with --dry-run for preview).
@@ -209,13 +264,18 @@ def remove(ctx: click.Context, dry_run: bool, category: str | None) -> None:
209264
sys.exit(1)
210265

211266
if not dry_run:
212-
console.print("[red]WARNING: This will modify files. Use --dry-run first![/red]")
267+
console.print(
268+
"[red]WARNING: This will modify files. Use --dry-run first![/red]"
269+
)
213270
console.print("[dim]Press Ctrl+C to abort. Running in 3 seconds...[/dim]")
214271
import time
272+
215273
time.sleep(3)
216274

217275
include_patterns = ctx.obj.get("include")
218-
scanner = DeadCodeScanner(project, ignore_patterns=ignore, include_patterns=include_patterns)
276+
scanner = DeadCodeScanner(
277+
project, ignore_patterns=ignore, include_patterns=include_patterns
278+
)
219279
result = scanner.scan()
220280

221281
findings = result.findings
@@ -248,7 +308,9 @@ def remove(ctx: click.Context, dry_run: bool, category: str | None) -> None:
248308
continue
249309

250310
try:
251-
lines = filepath.read_text(encoding="utf-8", errors="replace").splitlines(keepends=True)
311+
lines = filepath.read_text(encoding="utf-8", errors="replace").splitlines(
312+
keepends=True
313+
)
252314
except Exception as e:
253315
console.print(f"[red]Error reading {rel_file}: {e}[/red]")
254316
continue
@@ -259,15 +321,19 @@ def remove(ctx: click.Context, dry_run: bool, category: str | None) -> None:
259321
if dry_run:
260322
for line_num in sorted(lines_to_remove):
261323
content = lines[line_num - 1].rstrip() if line_num <= len(lines) else ""
262-
console.print(f"[yellow]WOULD REMOVE[/yellow] {rel_file}:{line_num}{content.strip()[:80]}")
324+
console.print(
325+
f"[yellow]WOULD REMOVE[/yellow] {rel_file}:{line_num}{content.strip()[:80]}"
326+
)
263327
removed_count += len(lines_to_remove)
264328
else:
265329
for line_num in lines_to_remove:
266330
if 0 < line_num <= len(lines):
267331
lines[line_num - 1] = "" # Blank the line (safer than deleting)
268332
filepath.write_text("".join(lines), encoding="utf-8")
269333
removed_count += len(lines_to_remove)
270-
console.print(f"[green]✓[/green] Cleaned {rel_file} ({len(lines_to_remove)} lines)")
334+
console.print(
335+
f"[green]✓[/green] Cleaned {rel_file} ({len(lines_to_remove)} lines)"
336+
)
271337

272338
action = "Would remove" if dry_run else "Removed"
273339
console.print(f"\n[bold]{action}: {removed_count} dead code entries[/bold]")
@@ -283,14 +349,22 @@ def stats(ctx: click.Context) -> None:
283349
project = ctx.obj["project"]
284350
ignore = _merge_config_ignore(ctx)
285351
include_patterns = ctx.obj.get("include")
286-
scanner = DeadCodeScanner(project, ignore_patterns=ignore, include_patterns=include_patterns)
352+
scanner = DeadCodeScanner(
353+
project, ignore_patterns=ignore, include_patterns=include_patterns
354+
)
287355
result = scanner.scan()
288356

289357
console.print(f"Files scanned: [bold]{result.files_scanned}[/bold]")
290-
console.print(f"Unused exports: [bold yellow]{len(result.unused_exports)}[/bold yellow]")
358+
console.print(
359+
f"Unused exports: [bold yellow]{len(result.unused_exports)}[/bold yellow]"
360+
)
291361
console.print(f"Dead routes: [bold red]{len(result.dead_routes)}[/bold red]")
292-
console.print(f"Orphaned CSS: [bold magenta]{len(result.orphaned_css)}[/bold magenta]")
293-
console.print(f"Unreferenced components: [bold cyan]{len(result.unreferenced_components)}[/bold cyan]")
362+
console.print(
363+
f"Orphaned CSS: [bold magenta]{len(result.orphaned_css)}[/bold magenta]"
364+
)
365+
console.print(
366+
f"Unreferenced components: [bold cyan]{len(result.unreferenced_components)}[/bold cyan]"
367+
)
294368
console.print(f"Total findings: [bold]{len(result.findings)}[/bold]")
295369

296370
if result.errors:

0 commit comments

Comments
 (0)