Css - biały
This commit is contained in:
120
find_unused_css_classes.py
Normal file
120
find_unused_css_classes.py
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
# --- Regexy do zbierania klas z CSS ---
|
||||
# Łapie ".class-name" w selektorach (pomija np. ".5" i rzeczy z escapami – prosto, ale działa w praktyce)
|
||||
CSS_CLASS_RE = re.compile(r'(?<![\\\w-])\.([a-zA-Z_-][\w-]*)')
|
||||
|
||||
# --- Regexy do zbierania stringów klas z Astro/JSX/TSX ---
|
||||
# 1) class="..."
|
||||
CLASS_ATTR_RE = re.compile(r'\bclass\s*=\s*("([^"]*)"|\'([^\']*)\')', re.IGNORECASE)
|
||||
# 2) className="..."
|
||||
CLASSNAME_ATTR_RE = re.compile(r'\bclassName\s*=\s*("([^"]*)"|\'([^\']*)\')', re.IGNORECASE)
|
||||
|
||||
# Astro: class:list={{ a: cond, "b c": cond2 }} / class:list={[...]} – tu łapiemy stringi w środku
|
||||
STRING_LIT_RE = re.compile(r'("([^"]+)"|\'([^\']+)\')')
|
||||
|
||||
# Dla wyszukiwania tokenów klas (żeby "f-card" nie matchowało jako fragment "f-card-x")
|
||||
def token_pattern(cls: str) -> re.Pattern:
|
||||
return re.compile(r'(?<![\w-])' + re.escape(cls) + r'(?![\w-])')
|
||||
|
||||
def read_text(path: Path) -> str:
|
||||
try:
|
||||
return path.read_text(encoding="utf-8", errors="ignore")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def collect_css_classes(css_dir: Path) -> Tuple[Set[str], Dict[str, Set[Path]]]:
|
||||
classes: Set[str] = set()
|
||||
where: Dict[str, Set[Path]] = {}
|
||||
|
||||
for p in css_dir.rglob("*.css"):
|
||||
txt = read_text(p)
|
||||
for m in CSS_CLASS_RE.finditer(txt):
|
||||
c = m.group(1)
|
||||
classes.add(c)
|
||||
where.setdefault(c, set()).add(p)
|
||||
|
||||
return classes, where
|
||||
|
||||
def collect_used_classes(code_dir: Path, candidates: Set[str]) -> Tuple[Set[str], Dict[str, Set[Path]]]:
|
||||
used: Set[str] = set()
|
||||
used_where: Dict[str, Set[Path]] = {c: set() for c in candidates}
|
||||
|
||||
exts = {".astro", ".jsx", ".tsx", ".js", ".ts"}
|
||||
# prekompilacja patternów dla szybkości
|
||||
patterns = {c: token_pattern(c) for c in candidates}
|
||||
|
||||
for p in code_dir.rglob("*"):
|
||||
if not p.is_file():
|
||||
continue
|
||||
if p.suffix.lower() not in exts:
|
||||
continue
|
||||
|
||||
txt = read_text(p)
|
||||
if not txt:
|
||||
continue
|
||||
|
||||
# szybki filtr: jeśli żaden kandydat nie ma nawet prefiksu "f-" / "jmb-" itd,
|
||||
# to i tak skan tokenowy jest ok, ale tu robimy prosty scan wszystkich.
|
||||
for c, pat in patterns.items():
|
||||
if pat.search(txt):
|
||||
used.add(c)
|
||||
used_where[c].add(p)
|
||||
|
||||
# usuń puste wpisy
|
||||
used_where = {k: v for k, v in used_where.items() if v}
|
||||
return used, used_where
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Znajdź potencjalnie nieużywane klasy CSS w projekcie Astro/JSX.")
|
||||
ap.add_argument("--css", required=True, help="Katalog z plikami CSS (np. src/styles)")
|
||||
ap.add_argument("--code", required=True, help="Katalog z kodem (np. src)")
|
||||
ap.add_argument("--min-len", type=int, default=3, help="Minimalna długość nazwy klasy (domyślnie 3)")
|
||||
ap.add_argument("--prefix", action="append", default=[], help="Filtruj klasy po prefiksie (np. --prefix f- --prefix jmb-)")
|
||||
ap.add_argument("--show-where", action="store_true", help="Pokaż gdzie zdefiniowano klasę w CSS")
|
||||
args = ap.parse_args()
|
||||
|
||||
css_dir = Path(args.css).resolve()
|
||||
code_dir = Path(args.code).resolve()
|
||||
|
||||
if not css_dir.exists():
|
||||
raise SystemExit(f"Brak katalogu CSS: {css_dir}")
|
||||
if not code_dir.exists():
|
||||
raise SystemExit(f"Brak katalogu code: {code_dir}")
|
||||
|
||||
all_classes, defined_where = collect_css_classes(css_dir)
|
||||
|
||||
# filtr długości + prefiksów
|
||||
classes = {c for c in all_classes if len(c) >= args.min_len}
|
||||
if args.prefix:
|
||||
classes = {c for c in classes if any(c.startswith(px) for px in args.prefix)}
|
||||
|
||||
used, used_where = collect_used_classes(code_dir, classes)
|
||||
unused = sorted(classes - used)
|
||||
|
||||
print(f"CSS katalog: {css_dir}")
|
||||
print(f"CODE katalog: {code_dir}")
|
||||
print(f"Klasy w CSS: {len(classes)} (po filtrach)")
|
||||
print(f"Użyte w kodzie:{len(used)}")
|
||||
print(f"NIEUŻYTE: {len(unused)}")
|
||||
print("-" * 60)
|
||||
|
||||
for c in unused:
|
||||
print(c)
|
||||
if args.show_where:
|
||||
files = sorted(defined_where.get(c, []))
|
||||
for f in files:
|
||||
rel = f.relative_to(css_dir.parent) if css_dir.parent in f.parents else f
|
||||
print(f" defined in: {rel}")
|
||||
print("-" * 60)
|
||||
|
||||
# opcjonalnie: pokaż top kilka użyć
|
||||
# (jak chcesz, dopiszę flagę na raport "gdzie użyte")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -53,7 +53,7 @@
|
||||
}
|
||||
|
||||
.f-card-label {
|
||||
@apply text-base font-medium opacity-80;
|
||||
@apply text-base font-medium opacity-95;
|
||||
}
|
||||
|
||||
.f-card-value {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
p:first-of-type {
|
||||
@apply py-3 text-3xl text-gray-400;
|
||||
@apply py-3 text-3xl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -110,7 +110,8 @@ margin: 1.75rem auto;
|
||||
|
||||
border-radius: 2rem;
|
||||
border: 1px solid color-mix(in oklab, var(--f-border-color) 82%, transparent 18%);
|
||||
background: color-mix(in oklab, var(--f-background) 96%, black 10%);
|
||||
background-color: var(--f-premium-bg);
|
||||
/* background: color-mix(in oklab, var(--f-background) 96%, black 10%); */
|
||||
|
||||
box-shadow:
|
||||
0 18px 40px -28px rgba(0,0,0,.35),
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
.fuz-markdown p a:hover{
|
||||
@apply text-[--f-link-text-hover];
|
||||
}
|
||||
|
||||
.fuz-markdown h1 {
|
||||
@apply text-3xl font-bold mt-8 mb-4;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
/* @layer components { */
|
||||
.f-section-header {
|
||||
@apply text-4xl md:text-5xl font-bold mb-3 text-[--f-header];
|
||||
|
||||
.f-section-header {
|
||||
@apply text-4xl md:text-5xl font-bold mb-3 text-[--f-header];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.f-section {
|
||||
@apply pt-1 pb-1 mx-2 my-6;;
|
||||
}
|
||||
.f-section {
|
||||
@apply pt-1 pb-1 mx-2 my-6;
|
||||
;
|
||||
}
|
||||
|
||||
.f-section-center {
|
||||
@apply f-section text-center;
|
||||
}
|
||||
.f-section-center {
|
||||
@apply f-section text-center;
|
||||
}
|
||||
|
||||
.f-section-grid {
|
||||
@apply grid items-center gap-5 max-w-7xl mx-auto mt-8;
|
||||
}
|
||||
.f-section-grid {
|
||||
@apply grid items-center gap-5 max-w-7xl mx-auto mt-8;
|
||||
}
|
||||
|
||||
.f-section-grid-single {
|
||||
@apply grid items-center gap-5 max-w-7xl mx-auto;
|
||||
}
|
||||
.f-section-grid-single {
|
||||
@apply grid items-center gap-5 max-w-7xl mx-auto;
|
||||
}
|
||||
|
||||
.f-section-grid-single-center {
|
||||
@apply f-section-grid-single text-center;
|
||||
}
|
||||
.f-section-grid-single-center {
|
||||
@apply f-section-grid-single text-center;
|
||||
}
|
||||
|
||||
.f-section-image {
|
||||
@apply w-full object-contain;
|
||||
}
|
||||
.f-section-image {
|
||||
@apply w-full object-contain;
|
||||
}
|
||||
|
||||
.f-image-dimmed {
|
||||
@apply opacity-[1];
|
||||
}
|
||||
.f-image-dimmed {
|
||||
@apply opacity-[1];
|
||||
}
|
||||
|
||||
.f-section-title {
|
||||
@apply text-4xl md:text-5xl font-bold mb-2 text-[--f-header];
|
||||
}
|
||||
.f-section-title {
|
||||
@apply text-4xl md:text-5xl font-bold mb-2 text-[--f-header];
|
||||
}
|
||||
|
||||
.f-section-nav {
|
||||
@apply mt-0 flex justify-center;
|
||||
}
|
||||
.f-section-nav {
|
||||
@apply mt-0 flex justify-center;
|
||||
}
|
||||
|
||||
/* } */
|
||||
|
||||
|
||||
@@ -7,27 +7,30 @@
|
||||
--brand-dark: hsl(200 calc(100% / 0.2) calc(50% / 1.5));
|
||||
|
||||
--f-navbar-height: 84px;
|
||||
|
||||
|
||||
/* -------------------------- */
|
||||
--f-hero-text: #d1d6d9;
|
||||
--f-hero-header: #d1d6d9;
|
||||
|
||||
/* --- Background and Text --- */
|
||||
--f-background: #e0e6eb;
|
||||
--f-background: #ffffff;
|
||||
/* #e0e6eb; */
|
||||
--f-text: #36525e;
|
||||
--f-header: #001a33;
|
||||
--f-header-items: (#001a33);
|
||||
/*--- Navbar --- */
|
||||
--f-navbar-background: #b3b8ba;
|
||||
--f-navbar-background: #e0e6eb;
|
||||
--f-navbar-link: #0066cc;
|
||||
--f-navbar-link-hover: #1a6655;
|
||||
--f-navbar-link-hover: #ef233c;
|
||||
/*--- Footer --- */
|
||||
--f-footer-background: #ccd6db;
|
||||
--f-footer-background: #e0e6eb;
|
||||
--f-footer-link-text: #0066cc;
|
||||
/* --- Linki --- */
|
||||
--f-link-text: #0066cc;
|
||||
--f-link-text-hover: #1a6655;
|
||||
--f-link-text-hover: #ef233c;
|
||||
--btn-background: #0066cc;
|
||||
--btn-text: #ccd6db;
|
||||
--btn-text: #e4ebee;
|
||||
--btn-background-hover: #ccd6db;
|
||||
--btn-text-hover: #0066cc;
|
||||
|
||||
@@ -60,6 +63,7 @@
|
||||
|
||||
/* Obramowanie footer w kanałów premium */
|
||||
--f-premium-border: #adb5bd;
|
||||
--f-premium-bg: #f7f5f5;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,5 +124,6 @@ html.dark {
|
||||
--f-addons-text: #fffae6;
|
||||
--f-addons-background: #1a6655;
|
||||
|
||||
--f-premium-border: #3d3f40;
|
||||
--f-premium-border: #6c757d;
|
||||
--f-premium-bg: #343a40;
|
||||
}
|
||||
Reference in New Issue
Block a user