Repository navigation
Expand file tree
/
Copy pathminify_data.py
More file actions
158 lines (122 loc) · 5.21 KB
/
Copy pathminify_data.py
File metadata and controls
158 lines (122 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import gzip
import os
import re
import shutil
import subprocess
import json
from SCons.Script import Import
Import("env")
# ──────────────────────────────────────────────
# Config
# ──────────────────────────────────────────────
SRC_DIR_NAME = "data-dev"
DST_DIR_NAME = "data"
# Extensions à traiter (Minify + Gzip)
MINIFY_AND_GZIP = {".html", ".htm", ".css", ".js", ".json", ".svg", ".xml"}
WEBP_EXTENSIONS = {".webp"}
def _project_dir():
return env.subst("$PROJECT_DIR")
def _src_dir():
return os.path.join(_project_dir(), SRC_DIR_NAME)
def _dst_dir():
return os.path.join(_project_dir(), DST_DIR_NAME)
# ──────────────────────────────────────────────
# Minificateurs
# ──────────────────────────────────────────────
def minify_html(text: str) -> str:
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
text = re.sub(r">\s+<", "> <", text)
text = re.sub(r"\s{2,}", " ", text)
return text.strip()
def minify_css(text: str) -> str:
# 1. Supprimer les commentaires
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
# 2. Remplacer les retours à la ligne et tabulations par des espaces simples
text = re.sub(r"\s+", " ", text)
# 3. Supprimer les espaces inutiles autour des caractères de structure CSS
text = re.sub(r"\s*([:{};,])\s*", r"\1", text)
# 4. ASTUCE POUR FIREFOX : On isole les règles -webkit- en réinjectant un espace
# après chaque fermeture d'accolade qui les concerne pour casser le bloc unique.
text = re.sub(r"([^{]*?-webkit-[^}]+})", r"\1\n", text)
return text.strip()
def minify_js(text: str) -> str:
return text
def minify_json(text: str) -> str:
try:
data = json.loads(text)
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)
except:
return text
def minify_svg(text: str) -> str:
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
return re.sub(r">\s+<", "><", text).strip()
MINIFIERS = {
".html": minify_html,
".htm": minify_html,
".css": minify_css,
".js": minify_js,
".json": minify_json,
".svg": minify_svg,
".xml": minify_svg,
}
# ──────────────────────────────────────────────
# Logique de traitement
# ──────────────────────────────────────────────
def process_file(src_path: str, dst_path: str):
ext = os.path.splitext(src_path)[1].lower()
original_size = os.path.getsize(src_path)
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
# Traitement des fichiers WebP (Compression via cwebp)
if ext in WEBP_EXTENSIONS:
try:
# -q 75 : Règle la qualité à 75%
subprocess.run(
["cwebp", "-q", "75", src_path, "-o", dst_path],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
final_size = os.path.getsize(dst_path)
return "webp-compress", original_size, final_size
except Exception:
shutil.copy2(src_path, dst_path)
return "copy (webp fail)", original_size, original_size
# Traitement Textes (Minify + Gzip)
if ext in MINIFY_AND_GZIP:
with open(src_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
minifier = MINIFIERS.get(ext)
if minifier:
minified = minifier(content)
action = "minify+gzip" if minified != content else "gzip"
content = minified
else:
action = "gzip"
gz_path = dst_path + ".gz"
with gzip.open(gz_path, "wb", compresslevel=9) as gz:
gz.write(content.encode("utf-8"))
final_size = os.path.getsize(gz_path)
return action, original_size, final_size
shutil.copy2(src_path, dst_path)
return "copy", original_size, original_size
def minify_all():
src_dir = _src_dir()
dst_dir = _dst_dir()
if not os.path.isdir(src_dir):
return
if os.path.exists(dst_dir):
shutil.rmtree(dst_dir)
os.makedirs(dst_dir, exist_ok=True)
print(f"\n[minify] Optimisation des assets : {SRC_DIR_NAME} -> {DST_DIR_NAME}")
for root, dirs, files in os.walk(src_dir):
for fname in sorted(files):
if fname.startswith(".") or fname.endswith("~"): continue
src_path = os.path.join(root, fname)
rel_path = os.path.relpath(src_path, src_dir)
dst_path = os.path.join(dst_dir, rel_path)
action, old_sz, new_sz = process_file(src_path, dst_path)
saved = old_sz - new_sz
pct = (saved / old_sz * 100) if old_sz > 0 else 0
print(f" {rel_path:<30} {old_sz:>7} -> {new_sz:>7} B ({pct:>3.0f}%) [{action}]")
# Lancement
minify_all()