Mise à jour des fichiers CSS, ajout des mécanismes de mise à jour de Ship2net avec info sur la page du bateau.
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
# ============================================================================
|
||||
# purger_css.py - Repère (et retire, sur demande) les règles CSS qui ne s'appliquent à rien (synthèse §50).
|
||||
#
|
||||
# Une règle est retirée si CHACUN de ses sélecteurs cite un identifiant (#...) ou une classe (.…) qui n'apparaît nulle
|
||||
# part dans les fichiers PHP / JS / HTML / QML du site (dossiers vendor exclus). Prudence :
|
||||
# - noms construits par le code : un nom est jugé utilisé si lui-même apparaît, OU si l'un de ses débuts terminés par
|
||||
# "_" est suivi d'un guillemet (".statut_actif" gardé grâce à 'statut_'.$classe), OU si l'une de ses fins commençant
|
||||
# par "_" est précédée d'un guillemet (".EN_acc" gardé grâce à $langue.'_acc') ;
|
||||
# - classes d'état posées par des scripts (Bootstrap...) toujours gardées (show, active, collapse...) ;
|
||||
# - commentaires (y compris des règles mises en commentaire) conservés tels quels dans le CSS ; les lignes de
|
||||
# commentaire du code (//, #, /* ... */) ne comptent pas comme un usage ;
|
||||
# - un bloc @media devenu vide est retiré.
|
||||
#
|
||||
# Usage : python3 purger_css.py fichier.css racine_du_site [--appliquer]
|
||||
# sans --appliquer : rapport seulement (à relire !) ; avec : réécrit fichier.css, l'original étant conservé en
|
||||
# fichier.css.avant_purge.
|
||||
# ============================================================================
|
||||
import os, re, sys
|
||||
|
||||
css_chemin, racine = sys.argv[1], sys.argv[2]
|
||||
appliquer = '--appliquer' in sys.argv
|
||||
|
||||
morceaux = []
|
||||
for dossier, sous_dossiers, fichiers in os.walk(racine):
|
||||
sous_dossiers[:] = [d for d in sous_dossiers if d not in ('vendor', 'vendor_OLD', '.git')]
|
||||
for f in fichiers:
|
||||
if f.endswith(('.php', '.js', '.html', '.qml')):
|
||||
with open(os.path.join(dossier, f), encoding='utf-8', errors='replace') as fh:
|
||||
# Lignes de commentaire ignorées (code mis en commentaire, notes) : un nom qui n'y figure plus que là
|
||||
# n'est plus utilisé.
|
||||
morceaux.extend(l for l in fh if not re.match(r'\s*(//|#|\*|/\*)', l))
|
||||
corpus = '\n'.join(morceaux)
|
||||
|
||||
TOUJOURS_GARDEES = {'show', 'hide', 'active', 'actif', 'fade', 'collapse', 'collapsing', 'collapsed', 'open',
|
||||
'disabled', 'selected', 'hidden', 'visible'}
|
||||
|
||||
def nom_utilise(nom):
|
||||
if nom in TOUJOURS_GARDEES: return True
|
||||
if re.search(r'(?<![\w-])' + re.escape(nom) + r'(?![\w-])', corpus): return True
|
||||
for i, c in enumerate(nom):
|
||||
if c == '_' and i > 0:
|
||||
debut, fin = nom[:i + 1], nom[i:]
|
||||
# Uniquement une construction par concaténation : début suivi d'un guillemet ('statut_'.$x),
|
||||
# ou fin précédée d'un guillemet ($x.'_acc') - "pied_de_page_mobile" ne justifie pas "#menu_mobile".
|
||||
if any(q + fin in corpus or debut + q in corpus for q in ("'", '"')): return True
|
||||
return False
|
||||
|
||||
def selecteur_utile(sel):
|
||||
sel = re.sub(r'::?[\w-]+(\([^)]*\))?', '', sel) # pseudo-classes et pseudo-éléments
|
||||
sel = re.sub(r'\[[^\]]*\]', '', sel) # sélecteurs d'attribut
|
||||
noms = re.findall(r'[#.]([A-Za-z_][\w-]*)', sel)
|
||||
return all(nom_utilise(n) for n in noms)
|
||||
|
||||
texte = open(css_chemin, encoding='utf-8', errors='replace').read()
|
||||
|
||||
# Commentaires mis de côté (marqueurs sans accolades), restitués tels quels à la fin :
|
||||
commentaires = []
|
||||
def mettre_de_cote(m):
|
||||
commentaires.append(m.group(0))
|
||||
return '\x00' + str(len(commentaires) - 1) + '\x00'
|
||||
texte = re.sub(r'/\*.*?\*/', mettre_de_cote, texte, flags=re.S)
|
||||
restituer = lambda t: re.sub(r'\x00(\d+)\x00', lambda m: commentaires[int(m.group(1))], t)
|
||||
|
||||
retirees = []
|
||||
def traiter(bloc):
|
||||
res, pos = [], 0
|
||||
for m in re.finditer(r'([^{}]*)\{([^{}]*)\}', bloc):
|
||||
sel_brut = m.group(1)
|
||||
marqueurs = ''.join(re.findall(r'\s*\x00\d+\x00\s*', sel_brut)) # commentaires placés avant la règle
|
||||
sel = re.sub(r'\x00\d+\x00', '', sel_brut).strip()
|
||||
selecteurs = [x.strip() for x in sel.split(',') if x.strip()]
|
||||
res.append(bloc[pos:m.start()])
|
||||
if selecteurs and not any(selecteur_utile(x) for x in selecteurs):
|
||||
retirees.append(' '.join(sel.split()))
|
||||
res.append(marqueurs)
|
||||
else:
|
||||
res.append(m.group(0))
|
||||
pos = m.end()
|
||||
res.append(bloc[pos:])
|
||||
return ''.join(res)
|
||||
|
||||
sortie, pos = [], 0
|
||||
for m in re.finditer(r'@media[^{]*\{((?:[^{}]*\{[^{}]*\})*[^{}]*)\}', texte):
|
||||
sortie.append(traiter(texte[pos:m.start()]))
|
||||
interieur = traiter(m.group(1))
|
||||
if '{' in interieur: sortie.append(texte[m.start():m.start(1)] + interieur + '}')
|
||||
else: retirees.append(' '.join(texte[m.start():m.start(1)].split()) + ' (bloc devenu vide)')
|
||||
pos = m.end()
|
||||
sortie.append(traiter(texte[pos:]))
|
||||
resultat = restituer(''.join(sortie))
|
||||
|
||||
print(f"{len(retirees)} règle(s) sans usage :")
|
||||
try:
|
||||
for r in retirees: print(" -", r[:120])
|
||||
except BrokenPipeError: pass
|
||||
if appliquer:
|
||||
os.replace(css_chemin, css_chemin + '.avant_purge')
|
||||
with open(css_chemin, 'w', encoding='utf-8') as fh: fh.write(resultat)
|
||||
print(f"Écrit : {css_chemin} (original conservé : {css_chemin}.avant_purge)")
|
||||
Reference in New Issue
Block a user