fix: 修复前端 assets 清理脚本以支持 Rolldown 产物

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name
2026-06-01 12:31:55 +08:00
parent 2deb89b49c
commit 65c77155d8
2 changed files with 54 additions and 30 deletions
+2 -1
View File
@@ -26,7 +26,8 @@ echo "✓ Upload complete"
# 4. Extract on server + prune orphaned legacy chunks
echo "▶ Deploying on server..."
ssh nexus "cd /www/wwwroot/api.synaglobal.vip/web/app && tar xzf /tmp/nexus-frontend.tar.gz && rm /tmp/nexus-frontend.tar.gz"
echo "▶ Pruning orphaned assets..."
echo "▶ Pruning orphaned assets (dry-run first)..."
ssh nexus "python3 /www/wwwroot/api.synaglobal.vip/deploy/prune_frontend_assets.py --dry-run"
ssh nexus "python3 /www/wwwroot/api.synaglobal.vip/deploy/prune_frontend_assets.py"
echo "✓ Extracted and pruned"
+52 -29
View File
@@ -1,15 +1,32 @@
#!/usr/bin/env python3
"""Remove orphaned files under web/app/assets/ not referenced by current Vite build."""
"""Remove orphaned files under web/app/assets/ not referenced by current Vite build.
Vite/Rolldown minified bundles reference chunks by bare filename (e.g. ServersPage-xxx.js)
without import() paths — scan file contents for known asset basenames.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
IMPORT_RE = re.compile(
r"""(?:from\s*["']|import\s*\(\s*["']|import\s*["'])\./([^"']+\.(?:js|css))"""
ASSET_NAME_RE = re.compile(
r"[A-Za-z0-9][A-Za-z0-9_.-]*\.(?:js|css|woff2?|ttf|eot)"
)
URL_RE = re.compile(r"""url\(["']?([^"')]+\.(?:woff2?|ttf|eot))["']?\)""")
URL_RE = re.compile(r"""url\(["']?([^"')]+)["']?\)""")
def _referenced_basenames(text: str, known: set[str]) -> set[str]:
found: set[str] = set()
for m in ASSET_NAME_RE.finditer(text):
name = m.group(0)
if name in known:
found.add(name)
for raw in URL_RE.findall(text):
name = Path(raw).name
if name in known:
found.add(name)
return found
def collect(app_dir: Path) -> set[Path]:
@@ -17,42 +34,48 @@ def collect(app_dir: Path) -> set[Path]:
index = app_dir / "index.html"
if not index.is_file():
raise SystemExit(f"missing {index}")
keep: set[Path] = set()
queue: list[Path] = []
all_names = {p.name for p in assets.iterdir() if p.is_file()}
keep_names: set[str] = set()
for m in re.finditer(r"/app/assets/([^\s\"']+)", index.read_text(encoding="utf-8")):
p = assets / m.group(1)
if p.is_file():
queue.append(p)
name = m.group(1).split("/")[-1]
if name in all_names:
keep_names.add(name)
while queue:
path = queue.pop()
if path in keep or not path.is_file():
continue
keep.add(path)
text = path.read_text(encoding="utf-8", errors="ignore")
for rel in IMPORT_RE.findall(text):
nxt = assets / rel
if nxt.is_file():
queue.append(nxt)
if path.suffix == ".css":
for rel in URL_RE.findall(text):
rel = rel.lstrip("./")
nxt = assets / rel
if nxt.is_file():
keep.add(nxt)
return keep
if not keep_names:
raise SystemExit("no entry assets found in index.html")
changed = True
while changed:
changed = False
for name in list(keep_names):
path = assets / name
if not path.is_file():
continue
text = path.read_text(encoding="utf-8", errors="ignore")
for ref in _referenced_basenames(text, all_names):
if ref not in keep_names:
keep_names.add(ref)
changed = True
return {assets / n for n in keep_names}
def main() -> int:
app_dir = Path(sys.argv[1] if len(sys.argv) > 1 else "/www/wwwroot/api.synaglobal.vip/web/app")
dry_run = "--dry-run" in sys.argv
args = [a for a in sys.argv[1:] if a != "--dry-run"]
app_dir = Path(args[0] if args else "/www/wwwroot/api.synaglobal.vip/web/app")
assets = app_dir / "assets"
if not assets.is_dir():
raise SystemExit(f"not a directory: {assets}")
keep = collect(app_dir)
all_files = {p for p in assets.rglob("*") if p.is_file()}
orphan = sorted(all_files - keep)
all_files = {p for p in assets.iterdir() if p.is_file()}
orphan = sorted(all_files - keep, key=lambda p: p.name)
if dry_run:
print(f"dry-run kept={len(keep)} would_remove={len(orphan)} total={len(all_files)}")
return 0
for p in orphan:
p.unlink()
print(f"kept={len(keep)} removed={len(orphan)} total_before={len(all_files)}")