- Beispieldaten werden nicht mehr eingebettet (data/seed.js entfernt), sondern beim ersten Start per fetch(EventMap.config.SEED_DATA_URL) von einem externen, selbst gehosteten Server geladen (siehe ADR 0001, Revision 2026-07-28). - Neuer UI-Sprachumschalter (Deutsch/Englisch) für die Bedienoberfläche: js/i18n/strings.js + js/i18n/i18n.js, Sprachwahl im Menü, Persistenz über settings.language, Retranslation ohne Reload. - Neuer Menüpunkt "Ausflugsziel übersetzen (KI)": erzeugt einen Prompt, der eine externe KI bittet, Name/Beschreibungen eines bestehenden Ortes ins Englische, Spanische oder Französische zu übersetzen (js/ai/translatePromptBuilder.js, js/ui/translatePromptGenerator.js). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6.2 KiB
ADR 0001: Kein Build-Tooling, klassische Scripts mit globalem Namespace, Leaflet lokal vendored
Status
Akzeptiert (überarbeitet – siehe "Revision" unten; ursprüngliche Entscheidung für native ES-Module wurde revidiert)
Kontext
anforderung.md fordert explizit "möglichst einfache und gut verständliche
Technologien" (HTML5, CSS3, JavaScript, eine Open-Source-Kartenbibliothek,
LocalStorage) und stellt keinen Backend-Zwang für die erste Version. Die
Anwendung soll außerdem vollständig responsiv sein und ohne größere
Infrastruktur betrieben und gewartet werden können.
Die tatsächliche Zielumgebung des Nutzers wurde präzisiert: Die App wird
per Doppelklick auf index.html direkt aus einem lokal synchronisierten
Nextcloud-Ordner geöffnet (file://...). Es steht kein lokaler
HTTP-Server zur Verfügung und ein solcher soll vom Endnutzer auch nicht
manuell gestartet werden müssen – Priorität hat maximale Einfachheit für den
Endnutzer, nicht Entwicklerkomfort.
Revision (2026-07-24)
Die ursprüngliche Entscheidung, JavaScript als native ES-Module
(<script type="module"> mit import/export) umzusetzen, war für diesen
Anwendungsfall falsch: Browser blockieren das Nachladen von ES-Modulen
über file:// per CORS-Policy (Access to script ... has been blocked by CORS policy). Die App ließ sich damit nur über einen lokalen HTTP-Server
starten – was der Anforderung "Doppelklick, kein Server" widerspricht.
Neue Entscheidung: Alle JavaScript-Dateien wurden von ES-Modulen auf
klassische, nicht-modulare <script>-Tags umgebaut. Statt
import/export hängt jede Datei ihre öffentliche API an ein einziges
globales Namespace-Objekt window.EventMap (mit Sub-Namespaces utils,
config, models, store, map, ui) an. Jede Datei kapselt ihre
internen Hilfsvariablen weiterhin in einer IIFE
((function () { ... })();), sodass nur das explizit an EventMap.*
angehängte gemeinsam genutzt wird. Die Ladereihenfolge in index.html
(Utils → Config → Models → Store → Map → UI → main.js) stellt sicher,
dass jede Datei beim Ausführen bereits auf die Namespaces ihrer
Abhängigkeiten zugreifen kann.
Entscheidung
- Es wird kein Build-Tooling (kein Bundler wie Webpack/Vite, kein Transpiler, kein Package-Manager-Setup für die App selbst) eingesetzt.
- JavaScript wird als klassische Scripts (
<script src="...">, ohnetype="module") eingebunden, organisiert über einen gemeinsamen globalen Namespacewindow.EventMapstatt überimport/export. Jede Datei ist in eine IIFE gekapselt. - Leaflet und Leaflet.markercluster werden lokal vendored unter
vendor/leaflet/bzw.vendor/leaflet.markercluster/(JS, CSS und Marker-Bildassets), nicht per CDN eingebunden.
Begründung
- Entspricht direkt der Anforderung nach einfachen, gut verständlichen Technologien ohne zusätzliche Lernkurve (kein Framework, kein Build-Schritt).
- Ermöglicht das Öffnen der App per Doppelklick aus dem synchronisierten Ordner heraus, ohne dass der Endnutzer einen Server starten oder irgendetwas installieren muss – das ist für die Zielgruppe wichtiger als natives Modul-Tooling.
- Lokal vendorte Bibliotheken machen die App offline-fähig und unabhängig von der Verfügbarkeit externer CDNs; das passt zum Offline-First-Ansatz der LocalStorage-Datenhaltung.
- Geringere Angriffsfläche/Komplexität: keine Abhängigkeit von Node-Toolchain- Versionen, keine Build-Artefakte, die aktuell gehalten werden müssten.
Konsequenzen
- Trade-off gegenüber ES-Modulen: Es gibt kein natives Modul-Scoping
mehr. Stattdessen dient der globale
window.EventMap-Namespace plus IIFE-Kapselung pro Datei als Ersatz. Das erfordert Disziplin beim Vergeben von Namen unterEventMap.*(keine versehentlichen Überschreibungen) und macht Abhängigkeiten weniger explizit alsimport-Statements – sie ergeben sich aus der Script-Reihenfolge inindex.html, die entsprechend sorgfältig gepflegt werden muss. - Behobenes Restrisiko (2026-07-24): Ursprünglich lud
js/store/store.jsdie Beispieldaten perfetch('data/seed.json'), was unterfile://(insbesondere in Chrome/Chromium) von der Same-Origin-Policy blockiert wird. Die Seed-Daten wurden daher vondata/seed.jsonnachdata/seed.jsverschoben und lagen dort als eingebettetes<script>-Objekt (EventMap.data.SEED_PLACES), das wie jede andere JS-Datei klassisch eingebunden wurde.initStore()war dadurch synchron. Für spätere Phasen (z.B. KI-JSON-Import) gilt weiterhin: Datei-Uploads über<input type="file">+FileReadersind unproblematisch unterfile://, einfetch()auf lokale Dateien dagegen nicht. - Revision (2026-07-28):
data/seed.jswurde aus dem Repo entfernt. Die Beispieldaten werden jetzt beim allerersten Start perfetch(EventMap.config.SEED_DATA_URL)von einem externen, selbst gehosteten Server geladen (siehejs/store/store.js,loadSeedPlaces()/initStore()). Das ist kein Rückfall in das oben beschriebene Problem, da sich die Same-Origin-Blockade unterfile://nur auf lokale Dateien bezieht – einfetch()auf eine entferntehttps://-URL funktioniert auch vonfile://aus, sofern der Server passende CORS-Header (Access-Control-Allow-Origin) sendet.initStore()ist dadurch wieder asynchron (main.jsawaitet es bereits). Schlägt der Download fehl (offline, fehlende CORS-Header, Server nicht erreichbar), startet die App bewusst mit einer leeren Places-Liste und zeigt einen Hinweis im Status-Banner statt die gesamte Initialisierung abzubrechen. - Es gibt keinen automatischen Minify-/Tree-Shaking-Schritt; die Dateigröße im Produktivbetrieb ist etwas größer als mit Bundler, was für den Umfang dieser Anwendung akzeptabel ist.
- Updates der vendorten Bibliotheken müssen manuell durchgeführt werden
(Dateien in
vendor/austauschen), es gibt keinen automatisierten Dependency-Update-Mechanismus (z.B. Dependabot) für diese Assets. - Sollte die Anwendung später deutlich wachsen (z.B. TypeScript, größere Testinfrastruktur, Bereitstellung über einen echten Webserver), kann diese Entscheidung revidiert und zurück auf ES-Module umgestellt werden.