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:
2026-10-06 16:25:29 +00:00
parent ce81084cdc
commit 0a2ec261ec
24 changed files with 2682 additions and 2263 deletions
+100
View File
@@ -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)")