Patchnote GeoLeaf V2.0.0
Version: 2.0.0 Date de release: 2026-03-22 Comparaison: v1.2.0 → v2.0.0Branche: main
⚠️ Breaking Changes
Cette version introduit une migration majeure du moteur de rendu. Consulter MIGRATION_V1_V2 avant toute mise à jour.
Moteur de rendu
- Leaflet 1.9.4 → MapLibre GL JS ^5.0.0 : le moteur de rendu passe du DOM/SVG/Canvas au WebGL GPU. Les performances sont fondamentalement différentes (GPU clustering, vector tiles natif, expressions data-driven).
- Convention de coordonnées inversée :
[lat, lng](Leaflet) →[lng, lat](GeoJSON/MapLibre standard). Toutes les coordonnées passées à l'API doivent être adaptées. - Peer dependency : supprimer
leafletetleaflet.markercluster, ajoutermaplibre-gl@^5.0.0.
Package npm
- Scope renommé :
geoleaf→@geoleaf/core(le nom peut nécessiter une mise à jour danspackage.jsondes projets consommateurs). - UMD supprimé : aucun bundle UMD CJS disponible. Uniquement ESM. Tout chargement via
<script>classique doit migrer vers<script type="module">.
Configuration applicative
container:→mapId:: la clé d'initialisation de l'ID du conteneur carte est renommée.sidepanel→sidepanelConfig: clé canonique pour la config side panel dans les profils JSON. La clésidepanelreste acceptée en lecture (fallback rétrocompat) maissidepanelConfigest désormais la référence.useProfilePoiMapping,useMapping,GeoLeafConfig.geojsonsupprimés des interfaces TypeScript (la logique runtime de fallback est conservée pour les profils existants).
API publique
applyTheme(theme)→applyTheme(layerId, themeId): signature changée, ajout du paramètrelayerIdcible.
CSS
- Les classes
.leaflet-*du moteur sont remplacées par.maplibregl-*(MapLibre). Les classes GeoLeaf internes sont toutes préfixées.gl-*(isolation complète). .geoleaf-ctrl-scale/.geoleaf-ctrl-scale-lineremplacées par.gl-scale-graphic/.gl-scale-graphic-line.
TypeScript
SecureCookieOptions.secure: valeur par défaut changée dans le module CSRF (cf.csrf-token.ts— breaking documented en TSDoc).
Added (Ajouté)
Moteur de rendu — MapLibre GL JS v5 (Sprints 2–9)
MaplibreAdapter: implémentation complète deIMapAdapter(33 méthodes) en 7 fichiers dédiés, remplaçantLeafletAdapterdans tous les contextes runtime :maplibre-adapter.ts— map core, navigation (setView, panTo, flyTo, fitBounds), bridge événements (geoleaf:map:ready/move/zoom), markers, clustering, popups, controls, cleanup destroy()maplibre-helpers.ts—bindGeoJSONClusterEvents(),addSubLayers(),detectGeometryTypes(),safeBeforeId()maplibre-hatch-patterns.ts— génération Canvas de 6 types de hatch (diagonal, dot, cross, x, horizontal, vertical) viaOffscreenCanvas, enregistrementmap.addImage()pourfill-patternmaplibre-poi-icons.ts—registerSpriteIcons()convertit chaque<symbol>SVG sprite enImageData(48×48 px, pixelRatio 2), registered viamap.addImage()maplibre-style-converter.ts—normalizeToFlat(),toFillPaint(),toLinePaint(),toCirclePaint(),toClusterCirclePaint(),toRouteLinePaint(),conditionToExpression()(16 opérateurs), expressions data-driven["case", ...], zoom interpolationmaplibre-layer-registry.ts— registre source/layers GeoLeaf, ordering par z-index, sentinelle POI,toSourceId(),toSubLayerId(),getInsertBeforeId()maplibre-poi-renderer.ts— source GeoJSON clusterisée (cluster: true), 4 layers GPU empilés (clusters circle, cluster-count symbol, unclustered circle, unclustered icons symbol),applyPoiFilter(),poisToFeatureCollection()
- Modèle source/layer GeoJSON : 1 source
gl-src-{id}+ N sub-layers (gl-{id}-fill,gl-{id}-line,gl-{id}-circle) par couche GeoLeaf - MVT/PBF vector tiles natifs :
vector-tiles.tsréécrit —map.addSource({type:'vector'})+map.addLayer()MapLibre natif,fill-patterndata-driven - Symbol layer labels MapLibre :
LabelRenderer.createSymbolLayerForMapLibre()— layer natif sur source GeoJSON existante (field, font, color, buffer/halo), résolution dynamique du font stack depuismap.getStyle() - Gate CI
verify-no-leaflet.cjs: script de scan automatique (6 catégories : runtime, imports, globals, branching, JSDoc, config), résultat final ZERO LEAFLET REFERENCES
Nouveau package — @geoleaf/connector v1.0.0 (MIT, npm public)
- Intercepteur fetch universel pour sources géospatiales authentifiées (GeoJSON, WFS, vector tiles, FlatGeobuf, PMTiles, OGC API Features)
- 6 modules :
config-schema.ts,format-detector.ts,token-store.ts,fetch-interceptor.ts,auth-client.ts,login-ui.ts - Hook
__GEOLEAF_WORKER_HEADERS_HOOK__: couplage optionnel core↔connector sans violerno-premium-in-core - 105 tests, documentation CONNECTOR_GUIDE
Basemaps
- CARTO tiles : positron, dark-matter, voyager ajoutés à
DEFAULT_BASELAYERS - ESRI Street remplace OpenStreetMap standard
normalizeTilesArray(): expansion{s}subdomains (string/array) + supportpmtiles://BasemapConfigétendu : champstiles?: string[]ettileSize?: number
Performance & Monitoring
- Performance marks :
_pm()helper dansboot.tsetinit.ts— 8 paires de marks gateés surwindow.__GEOLEAF_PERF__ = true - Deferred UI init : Legend, LayerManager, Scale, Labels, CoordinatesDisplay déplacés dans un listener
geoleaf:app:ready{ once: true }— TTI amélioré
Architecture TypeScript
lazy-module-loader.ts:LazyModuleName,loadModule(),loadAllSecondaryModules()extraites debundle-esm-entry.ts— zeroany, zero@ts-nocheckloader-types.ts: 13 interfaces service locator,LoaderDependenciesavec 17 getters — remplace 69 occurrences(_g as any).GeoLeaf.*- Contracts enrichis :
content-builder.contract.ts,ui-controls.contract.ts,api.contract.ts,map-adapter.contract.ts
Sécurité
- Module CSRF complet (
csrf-token.ts) :init,getToken,validateToken,addTokenToHeaders,rotateToken,getTokenInfo,setSecureCookie,destroy DOMSecurity.setSafeHTML()systématique — whitelist SVG-onlyCSS.escape()sur POI ID dans le querySelector popup (prévention injection CSS selector)- Login modal (
login-ui.ts) : aucune exposition de données user dansinnerHTML, focus trap, Escape handler
Accessibilité CSS (WCAG 2.1 / RGAA 4.1)
@media (prefers-reduced-motion: reduce)dansgeoleaf-mobile-toolbar.css(WCAG 2.3.3):focus-visibleremplace:focusdans 5 fichiers CSS.gl-search-bar:focus-within: ring de focus visible (WCAG 2.4.7)aria-label+role="img"sur les markers MapLibre (navigation clavier)
Changed (Modifié)
Migration Leaflet → MapLibre GL JS
- Tous les appels
L.*supprimés du code source core — gate CIverify-no-leaflet.cjsbloquant L.Control.extend()→ plain objects avecaddTo(map)/remove()dans tous les contrôlesL.DomEvent.*→ DOM natif (addEventListener,stopPropagation, capture phase)- CSS classes
leaflet-control-*→gl-control-*
Build & Distribution
- UMD supprimé : 3 sorties ESM — bundle full chunked (
dist/esm/), preserveModules, lite (dist/esm-lite/) packages/core/package.jsonv2.0.0 :exportsESM-only,peerDependencies→maplibre-gl: ^5.0.0
Infrastructure de tests — Jest → Vitest 3 (Sprints T1–T5)
- Jest entièrement supprimé : 10 fichiers de configuration + dépendances retirées
- Vitest 3 avec workspace mode, provider
@vitest/coverage-istanbul - Seuils couverture :
branches: 75, functions: 68, lines: 70, statements: 70
TypeScript strict (Sprints R1–R4)
- 30
@ts-nochecksupprimés par contrats de types (api/, ui/controls/, ui/content-builder/, loader-types.ts) tsc --noEmit --strict0 erreur maintenu
Dead code — Knip
- 7 fichiers orphelins supprimés, 172 exports morts supprimés
Fixed (Corrections)
- Basemap z-ordering : raster s'affichait au-dessus des couches data — fix
addLayer(spec, firstLayerId) - Style selector niveau racine :
_applyStyleResult()extrait(styleData).style ?? styleDataavantnormalizeToFlat() - Filtres GeoJSON MapLibre :
_applyFeatureVisibilityForLayer()— nouveau cheminupdateLayerData()ajouté - Clustering natif : options
cluster/clusterRadius/clusterMaxZoomnon propagées à la source GeoJSON - Side panel vide :
normalizeFromGeoJSONparseJSON.parse(props.attributes)quandtypeof props.attributes === "string" - Échelle graphique invisible : classes
.gl-scale-graphicabsentes des règles CSS - Labels glyphs 404 :
_resolveMapFontStack()avec fallback["Noto Sans Regular", "Arial Unicode MS Bold"] - Visibilité zoom :
_applyThemeLayers()appelle_reapplyZoomVisibility()après application du thème - Filtres UI immédiats :
applyFiltersNow()dansattachCategoryTreeListeners()etattachTagsListeners() - Corruption clustering post-filtrage :
POI.setFilteredDisplay()ne crase plusstate.allPois - Tooltips orphelins :
hoverPopup.remove()explicite au début de chaquemouseenter - StyleRules data-driven :
conditionToExpression()supprime le préfixe"properties."— 5 couches corrigées - Hachures dot pattern + styleRules :
collectHatchPatterns()enregistre les patterns avantaddLayer() - Contraste texte cluster sombre :
#e5e7eb→#111827(ratio 1.83:1 → 7.86:1, WCAG AAA)
Tests & Qualité
| Sprint | Périmètre | Résultat |
|---|---|---|
| T1–T5 | Migration Jest → Vitest 3 | 10 configs Jest supprimées |
| T6 | UI + integration/controls/basemap/legend/storage | 0 régression |
| T7 | +223 tests MapLibre (4 nouvelles suites) | 284 fichiers, 6 403 tests |
| T8 | V8 → Istanbul, imports require() → ESM | thresholds réels débloqués |
| T9 | Couverture module par module | 323 fichiers, ~7 800 tests |
| T10 | Cible ≥ 75% branches atteinte | 8 317 tests, 0 échec |
Couverture finale (Istanbul)
| Métrique | v1.2.0 | v2.0.0 |
|---|---|---|
| Suites | 280 | 323 |
| Tests | 6 149 | 8 317 |
| Statements | 89,49 % | 87,82 % |
| Branches | 85,08 % | 77,97 % |
| Functions | — | 84,89 % |
| Lines | — | 87,82 % |
Performance — Baselines
| Métrique | v1.x (Leaflet) | v2.0.0 (MapLibre) |
|---|---|---|
| UMD bundle | 196 KB gzip | ❌ supprimé |
| ESM bundle gzip | 35 KB | ~35 KB |
| ESM lite | — | ~84 KB |
| CSS total | — | 151 KB / 22 KB gzip |
| 10 000 markers | 4 FPS (DOM) | 60 FPS (GPU) |
| 10 000 GeoJSON | 572 ms | < 100 ms WebGL |
| TTI | < 0,5 s | < 0,5 s |
Résumé
- Breaking change moteur : Leaflet 1.9.4 → MapLibre GL JS v5. Rendu WebGL, GPU clustering natif, MVT/PBF, expressions data-driven. Voir MIGRATION_V1_V2.
- Nouveau package MIT :
@geoleaf/connectorv1.0.0 — intercepteur fetch universel, publié sur npmjs.org. - Distribution ESM-only : bundle UMD supprimé définitivement. 3 sorties ESM.
- Tests modernisés : Vitest 3 + Istanbul, 8 317 tests, couverture branches 77,97 %.
- TypeScript 100% strict : 30
@ts-nochecksupprimés, 0 erreurtsc --noEmit --strict. - Documentation complète : site VitePress
geoleaf.dev/docs/, 62+ guides mis à jour.
