Beide Sites mit lokalem Font-Hosting, WebP, Build-Pipeline, SEO-Basis, HSTS, Performance-Tuning und aktualisierten Impressum/Datenschutz-Texten. Co-authored-by: Cursor <cursoragent@cursor.com>
135 lines
4.2 KiB
Python
Executable file
135 lines
4.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Synchronisiert head-common, Header und Footer in alle HTML-Seiten."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
VERSION = (ROOT / 'assets' / 'version.txt').read_text().strip()
|
|
HEAD_COMMON = ROOT / 'assets' / 'components' / 'head-common.html'
|
|
HEADER_INDEX = ROOT / 'assets' / 'components' / 'header-index.html'
|
|
HEADER_SUB = ROOT / 'assets' / 'components' / 'header-sub.html'
|
|
FOOTER = ROOT / 'assets' / 'components' / 'footer.html'
|
|
|
|
HEAD_MOUNT = re.compile(r' <!-- data-site-head -->\n')
|
|
HEAD_BLOCK = re.compile(
|
|
r' <meta property="og:locale" content="de_DE" />.*?"name": "ADAMETZ\.MEDIA"\n \}\n \}\n </script>\n',
|
|
re.DOTALL,
|
|
)
|
|
HEADER_MOUNT = re.compile(r' <div data-site-header></div>\n')
|
|
FOOTER_MOUNT = re.compile(r' <div data-site-footer></div>\n')
|
|
|
|
STYLESHEET = re.compile(
|
|
r'<link rel="stylesheet" href="assets/styles\.css(?:\?v=[^"]*)?" />'
|
|
)
|
|
APP_JS = re.compile(
|
|
r'<script src="assets/js/app\.js(?:\?v=[^"]*)?" defer></script>'
|
|
)
|
|
|
|
HERO_IMG = re.compile(
|
|
r'<img\s+src="assets/img/hero-bg2\.webp"[^>]*>',
|
|
re.DOTALL,
|
|
)
|
|
|
|
HERO_IMG_REPLACEMENT = '''<img
|
|
src="assets/img/hero-bg2.webp"
|
|
srcset="assets/img/hero-bg2-640.webp 640w, assets/img/hero-bg2-960.webp 960w, assets/img/hero-bg2.webp 1920w"
|
|
sizes="100vw"
|
|
alt="Abstrakte architektonische Struktur"
|
|
width="1920"
|
|
height="1080"
|
|
loading="eager"
|
|
fetchpriority="high"
|
|
decoding="async"
|
|
/>'''
|
|
|
|
INDEX_PAGES = {'index.html'}
|
|
|
|
|
|
def indent_block(text: str, prefix: str = ' ') -> str:
|
|
lines = text.strip('\n').splitlines()
|
|
return '\n'.join(prefix + line if line.strip() else '' for line in lines) + '\n'
|
|
|
|
|
|
def build_head() -> str:
|
|
content = HEAD_COMMON.read_text()
|
|
content = content.replace('{{VERSION}}', VERSION)
|
|
return indent_block(content)
|
|
|
|
|
|
def build_header(path: Path) -> str:
|
|
if path.name in INDEX_PAGES:
|
|
return indent_block(HEADER_INDEX.read_text())
|
|
return indent_block(HEADER_SUB.read_text())
|
|
|
|
|
|
def process_html(path: Path) -> bool:
|
|
text = path.read_text()
|
|
if '<!-- data-site-head -->' not in text and 'og:locale' not in text:
|
|
return False
|
|
|
|
original = text
|
|
|
|
if HEAD_MOUNT.search(text):
|
|
text = HEAD_MOUNT.sub(build_head(), text, count=1)
|
|
elif 'og:locale' in text:
|
|
text = HEAD_BLOCK.sub(build_head(), text, count=1)
|
|
|
|
if HEADER_MOUNT.search(text):
|
|
text = HEADER_MOUNT.sub(build_header(path), text, count=1)
|
|
|
|
if FOOTER_MOUNT.search(text):
|
|
footer = indent_block(FOOTER.read_text())
|
|
text = FOOTER_MOUNT.sub(footer, text, count=1)
|
|
|
|
text = STYLESHEET.sub(
|
|
f'<link rel="stylesheet" href="assets/styles.css?v={VERSION}" />',
|
|
text,
|
|
)
|
|
text = APP_JS.sub(
|
|
f'<script src="assets/js/app.js?v={VERSION}" defer></script>',
|
|
text,
|
|
)
|
|
|
|
if path.name == 'index.html':
|
|
text = HERO_IMG.sub(HERO_IMG_REPLACEMENT, text, count=1)
|
|
preload = (
|
|
' <link rel="preload" as="image" href="assets/img/hero-bg2-960.webp" '
|
|
'type="image/webp" fetchpriority="high" '
|
|
'imagesrcset="assets/img/hero-bg2-640.webp 640w, '
|
|
'assets/img/hero-bg2-960.webp 960w, assets/img/hero-bg2.webp 1920w" '
|
|
'imagesizes="100vw" />\n'
|
|
)
|
|
for _ in range(3):
|
|
text = re.sub(
|
|
r' <link rel="preload" as="image" href="assets/img/hero-bg2[^"]*"[^/]*/>\n',
|
|
'',
|
|
text,
|
|
)
|
|
manifest_line = ' <link rel="manifest" href="manifest.json" />\n'
|
|
if preload.strip() not in text and manifest_line in text:
|
|
text = text.replace(manifest_line, manifest_line + preload, 1)
|
|
|
|
if text != original:
|
|
path.write_text(text)
|
|
return True
|
|
return False
|
|
|
|
|
|
def main() -> None:
|
|
updated = []
|
|
for path in sorted(ROOT.glob('*.html')):
|
|
if process_html(path):
|
|
updated.append(path.name)
|
|
|
|
print(f'Version: {VERSION}')
|
|
for name in updated:
|
|
print(f' aktualisiert: {name}')
|
|
if not updated:
|
|
print(' keine Änderungen')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|