feat(static-assets): cache-bust JS/CSS via ?v=<commit-sha>, immutable in prod
All checks were successful
All checks were successful
Adds a `static_v(request, name, path=...)` Jinja helper that appends
?v=<commit-sha> from app.core.build_info, plus a CachedStaticFiles
subclass that serves Cache-Control: public, max-age=31536000, immutable
in production and no-cache in development. Browsers refetch JS/CSS
automatically on every deploy without the user having to hard-reload.
- New: app/core/static_files.py (CachedStaticFiles)
- Updated: app/templates_config.py (static_v helper)
- Updated: main.py (use CachedStaticFiles for *_static mounts)
- Codemod: 143 url_for('*_static', path='*.js'|'*.css') → static_v(...)
across 123 templates. Images/fonts/JSON locales intentionally
unchanged (out of scope).
- Arch rule: FE-024 (warning) flags raw url_for on JS/CSS to prevent
drift. Note: FE-008 was already taken by the number_stepper rule.
- docs/proposals/static-asset-cache-busting.md marked Done.
Closes plan from docs/proposals/static-asset-cache-busting.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -867,6 +867,10 @@ class ArchitectureValidator:
|
||||
if not is_components_page and not is_macro:
|
||||
self._check_number_stepper_macro_usage(file_path, content, lines)
|
||||
|
||||
# FE-024: Check for raw url_for(...) on .js/.css (should use static_v)
|
||||
if not is_macro:
|
||||
self._check_static_v_usage(file_path, content, lines)
|
||||
|
||||
# TPL-004: Check x-text usage for dynamic content
|
||||
self._check_xtext_usage(file_path, content, lines)
|
||||
|
||||
@@ -1793,6 +1797,40 @@ class ArchitectureValidator:
|
||||
)
|
||||
return # Only report once per file
|
||||
|
||||
_STATIC_V_PATTERN = re.compile(
|
||||
r"""url_for\(\s*['"]\w+_static['"]\s*,\s*path=['"][^'"]+\.(?:js|css)['"]\s*\)""",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
def _check_static_v_usage(
|
||||
self, file_path: Path, content: str, lines: list[str]
|
||||
):
|
||||
"""FE-024: Flag raw url_for() on .js/.css URLs — must use static_v() instead.
|
||||
|
||||
The static_v() Jinja helper appends ?v=<commit-sha> from build_info so
|
||||
browsers refetch JS/CSS automatically after each deploy.
|
||||
|
||||
Skip lines with `noqa: FE-024` or `noqa: FE024`.
|
||||
"""
|
||||
for i, line in enumerate(lines, 1):
|
||||
if not self._STATIC_V_PATTERN.search(line):
|
||||
continue
|
||||
if "noqa" in line.lower() and (
|
||||
"fe-024" in line.lower() or "fe024" in line.lower()
|
||||
):
|
||||
continue
|
||||
stripped = line.strip()
|
||||
self._add_violation(
|
||||
rule_id="FE-024",
|
||||
rule_name="Use static_v() for JS/CSS, not raw url_for()",
|
||||
severity=Severity.WARNING,
|
||||
file_path=file_path,
|
||||
line_number=i,
|
||||
message="Raw url_for() on JS/CSS — browsers won't refetch after deploy",
|
||||
context=stripped[:80],
|
||||
suggestion="Replace url_for(...) with static_v(request, ...) so the URL carries ?v=<commit>",
|
||||
)
|
||||
|
||||
def _validate_api_endpoints(self, target_path: Path):
|
||||
"""Validate API endpoint rules (API-001, API-002, API-003, API-004)"""
|
||||
print("📡 Validating API endpoints...")
|
||||
@@ -3508,6 +3546,10 @@ class ArchitectureValidator:
|
||||
if not is_base_or_partial and not is_macro and not is_components_page:
|
||||
self._check_number_stepper_macro_usage(file_path, content, lines)
|
||||
|
||||
# FE-024: Check for raw url_for(...) on .js/.css (should use static_v)
|
||||
if not is_base_or_partial and not is_macro:
|
||||
self._check_static_v_usage(file_path, content, lines)
|
||||
|
||||
# TPL-008: Check for call table_header() pattern
|
||||
self._check_table_header_call_pattern(file_path, content, lines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user