From 7dd2a0aed0ff82c8b9a1144f224fafc7dd63d0a0 Mon Sep 17 00:00:00 2001 From: giswqs Date: Tue, 28 Jul 2026 18:38:08 -0400 Subject: [PATCH 1/3] chore(deps): bump maplibre-gl to 6.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clears blocker 2 of opengeos/GeoLibre#1489 and carries the GeoLibre-side migration. Blocker 1's two external packages are handled by a temporary shim rather than being waited on, so the whole tree builds, tests, and runs on v6 today. v6 is ESM-only with no default export and no CJS build. **Blocker 2 — the frontend suite.** The plugin packages' `exports` maps are correct; the problem was our own module scope. Every workspace package is `"type": "module"`, but the root package.json is not, so tsx compiled all 256 test files to CJS, which selects the `require` condition and loads `.cjs` entries that `require("maplibre-gl")`. `tests/package.json` puts them in ESM scope, and the 29 ERR_PACKAGE_PATH_NOT_EXPORTED failures go to zero. This is a swap, not an addition — v5 ships no ESM build, so it cannot land without the bump. **Blocker 1 — the two external packages.** `@esri/maplibre-arcgis` and `@geoman-io/maplibre-geoman-free` still default-import maplibre-gl in their published ESM. Both are rewritten to namespace imports at load: a Vite plugin for the app build, a Node loader hook for `node --test` (Vite cannot reach the test runner), and an `optimizeDeps.exclude` entry because the dependency optimizer runs outside the plugin pipeline. The two lists are kept in step by `tests/maplibre-shim-parity.test.ts`, and the Vite plugin errors if a package stops matching, so a package that ships a fix cannot be silently shimmed forever. **The worker.** v6 ships its worker as a separate file located at runtime with `new URL("./maplibre-gl-worker.mjs", import.meta.url)` — a computed string no bundler can see. The asset was never emitted, and the URL resolved next to the hashed app chunk, where the SPA fallback answered with index.html, so the request hung instead of 404ing. Tile parsing was silently degraded: the same 25s window pulled 39 tiles before the fix and 68 after. Fixed by emitting the worker through Vite and pointing `setWorkerUrl` at it. Also: - 30 files migrated to `import * as maplibregl from "maplibre-gl"`. - Root `overrides` pin, because `@maplibre/maplibre-gl-directions` still peers `^5.0.0` and would otherwise resolve a second hoisted copy, making `Map` a different nominal type across package boundaries. - `packages/map/src/dynamic-style-property.ts` holds the casts for property names computed at runtime, now that set/getPaint/LayoutProperty are generic over `keyof AllPaintProperties`. - Geoman's `gm:*` events need a cast: `Map#on`/`off`'s catch-all overload narrowed from `type: string` to `keyof MapEventType`. - `GeoAgentSyncableTools`' map slice uses method syntax so a real `Map` still satisfies it under `strictFunctionTypes`. - GeolocateControl is constructed through a factory so tests can substitute a fake — the sealed v6 namespace rejects assignment (same root cause as #1509). - jsPDF is imported by name: its `node` export condition resolves a CJS bundle whose default is the module object, which only surfaced once the tests moved to ESM scope. Verified: test:frontend 4236 passed / 0 failed, coverage 86.93/84.15/71.13, tsc -b + vite build clean, lint 0 errors, test:worker clean. e2e 19/23 locally with all four failures reproducing on this machine independent of the bump (each passes in isolation). Dev server, production build, Geoman's toolbar and the Esri package all verified in a browser. --- apps/geolibre-desktop/package.json | 2 +- .../layout/FieldCollectionDialog.tsx | 2 +- .../components/layout/GeoreferencerDialog.tsx | 2 +- .../components/layout/GpsTrackingDialog.tsx | 2 +- .../src/components/layout/MapContextMenu.tsx | 2 +- .../layout/PixelTimeSeriesControl.tsx | 2 +- .../layout/RemoteCursorsOverlay.tsx | 2 +- .../panels/LayerPanelPlaceSearch.tsx | 2 +- .../storymap/StoryMapHandoutDialog.tsx | 2 +- .../components/storymap/StoryMapPresenter.tsx | 2 +- .../src/hooks/useCommandBridge.ts | Bin 6487 -> 6492 bytes .../src/hooks/useNotebookBridge.ts | 2 +- .../src/lib/assistant/tools.ts | 2 +- .../src/lib/maplibre-worker.ts | 22 ++++ .../src/lib/print-layout-export.ts | 2 +- apps/geolibre-desktop/src/lib/rtl-text.ts | 2 +- apps/geolibre-desktop/src/lib/storymap-pdf.ts | 2 +- apps/geolibre-desktop/src/main.tsx | 2 + .../maplibre-default-import-shim.ts | 67 ++++++++++ apps/geolibre-desktop/vite.config.ts | 9 ++ package-lock.json | 114 ++++++------------ package.json | 5 +- packages/map/package.json | 2 +- packages/map/src/MapCanvas.tsx | 2 +- .../map/src/collapsed-attribution-control.ts | 2 +- packages/map/src/dynamic-style-property.ts | 51 ++++++++ packages/map/src/generated-images.ts | 2 +- packages/map/src/layer-sync.ts | 11 +- packages/map/src/map-controller.ts | 26 ++-- packages/map/src/mapbox-style.ts | 2 +- packages/map/src/maptoolkit-logo-control.ts | 2 +- packages/map/src/planetary-scale-control.ts | 2 +- packages/map/src/protomaps-basemap.ts | 2 +- packages/map/src/reset-bearing-control.ts | 2 +- packages/map/src/terrain-control.ts | 2 +- packages/plugins/package.json | 2 +- packages/plugins/src/arcgis-maplibre.d.ts | 2 +- packages/plugins/src/plugins/arcgis-layer.ts | 2 +- .../src/plugins/geoagent-layer-sync.ts | 13 +- .../src/plugins/maplibre-annotations.ts | 2 +- .../src/plugins/maplibre-components.ts | 2 +- .../src/plugins/maplibre-geo-editor.ts | 27 ++++- tests/hooks/maplibre-default-import-shim.mjs | 42 +++++++ tests/hooks/register-maplibre-shim.mjs | 9 ++ tests/map-controller.test.ts | 15 ++- tests/mapbox-style.test.ts | 2 +- tests/maplibre-shim-parity.test.ts | 58 +++++++++ tests/package.json | 3 + tests/terrain-control.test.ts | 2 +- 49 files changed, 405 insertions(+), 133 deletions(-) create mode 100644 apps/geolibre-desktop/src/lib/maplibre-worker.ts create mode 100644 apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts create mode 100644 packages/map/src/dynamic-style-property.ts create mode 100644 tests/hooks/maplibre-default-import-shim.mjs create mode 100644 tests/hooks/register-maplibre-shim.mjs create mode 100644 tests/maplibre-shim-parity.test.ts create mode 100644 tests/package.json diff --git a/apps/geolibre-desktop/package.json b/apps/geolibre-desktop/package.json index 61bcc5daf..c63f1d0db 100644 --- a/apps/geolibre-desktop/package.json +++ b/apps/geolibre-desktop/package.json @@ -62,7 +62,7 @@ "i18next": "^26.3.6", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", diff --git a/apps/geolibre-desktop/src/components/layout/FieldCollectionDialog.tsx b/apps/geolibre-desktop/src/components/layout/FieldCollectionDialog.tsx index cd23e8016..e4db1a657 100644 --- a/apps/geolibre-desktop/src/components/layout/FieldCollectionDialog.tsx +++ b/apps/geolibre-desktop/src/components/layout/FieldCollectionDialog.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import type { MapController } from "@geolibre/map"; import { getAttributeFormField, diff --git a/apps/geolibre-desktop/src/components/layout/GeoreferencerDialog.tsx b/apps/geolibre-desktop/src/components/layout/GeoreferencerDialog.tsx index 189f587aa..f266cea25 100644 --- a/apps/geolibre-desktop/src/components/layout/GeoreferencerDialog.tsx +++ b/apps/geolibre-desktop/src/components/layout/GeoreferencerDialog.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import type { MapController } from "@geolibre/map"; import { DEFAULT_LAYER_STYLE, type GeoLibreLayer, useAppStore } from "@geolibre/core"; import { diff --git a/apps/geolibre-desktop/src/components/layout/GpsTrackingDialog.tsx b/apps/geolibre-desktop/src/components/layout/GpsTrackingDialog.tsx index c70ecfba6..2c3bb99ff 100644 --- a/apps/geolibre-desktop/src/components/layout/GpsTrackingDialog.tsx +++ b/apps/geolibre-desktop/src/components/layout/GpsTrackingDialog.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import type { Feature, FeatureCollection } from "geojson"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import type { MapController } from "@geolibre/map"; import { useAppStore } from "@geolibre/core"; import { diff --git a/apps/geolibre-desktop/src/components/layout/MapContextMenu.tsx b/apps/geolibre-desktop/src/components/layout/MapContextMenu.tsx index 528d9c10a..303760f3a 100644 --- a/apps/geolibre-desktop/src/components/layout/MapContextMenu.tsx +++ b/apps/geolibre-desktop/src/components/layout/MapContextMenu.tsx @@ -7,7 +7,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@geolibre/ui"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import { BookOpen, Braces, Crosshair, Earth, MapIcon, MapPin, ZoomIn } from "lucide-react"; import { useCallback, useEffect, useRef, useState, type RefObject } from "react"; import { useTranslation } from "react-i18next"; diff --git a/apps/geolibre-desktop/src/components/layout/PixelTimeSeriesControl.tsx b/apps/geolibre-desktop/src/components/layout/PixelTimeSeriesControl.tsx index d552ef855..7642e19ae 100644 --- a/apps/geolibre-desktop/src/components/layout/PixelTimeSeriesControl.tsx +++ b/apps/geolibre-desktop/src/components/layout/PixelTimeSeriesControl.tsx @@ -22,7 +22,7 @@ import { useState, } from "react"; import { useTranslation } from "react-i18next"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import type { MapController } from "@geolibre/map"; import { clamp } from "../../lib/clamp"; import { type ChartDomain, resolveChartDomain } from "../../lib/chart-domain"; diff --git a/apps/geolibre-desktop/src/components/layout/RemoteCursorsOverlay.tsx b/apps/geolibre-desktop/src/components/layout/RemoteCursorsOverlay.tsx index 00129ef03..aa81454e6 100644 --- a/apps/geolibre-desktop/src/components/layout/RemoteCursorsOverlay.tsx +++ b/apps/geolibre-desktop/src/components/layout/RemoteCursorsOverlay.tsx @@ -1,5 +1,5 @@ import { useAppStore, type CollaborationPresence } from "@geolibre/core"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { useEffect, useRef } from "react"; import type { RefObject } from "react"; import type { MapController } from "@geolibre/map"; diff --git a/apps/geolibre-desktop/src/components/panels/LayerPanelPlaceSearch.tsx b/apps/geolibre-desktop/src/components/panels/LayerPanelPlaceSearch.tsx index 02a9dc29e..3ab7fcbc1 100644 --- a/apps/geolibre-desktop/src/components/panels/LayerPanelPlaceSearch.tsx +++ b/apps/geolibre-desktop/src/components/panels/LayerPanelPlaceSearch.tsx @@ -9,7 +9,7 @@ import { useState, } from "react"; import { useTranslation } from "react-i18next"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { type GeocodeMatch, geocodeForward, diff --git a/apps/geolibre-desktop/src/components/storymap/StoryMapHandoutDialog.tsx b/apps/geolibre-desktop/src/components/storymap/StoryMapHandoutDialog.tsx index 869a491e6..2713e8f86 100644 --- a/apps/geolibre-desktop/src/components/storymap/StoryMapHandoutDialog.tsx +++ b/apps/geolibre-desktop/src/components/storymap/StoryMapHandoutDialog.tsx @@ -1,5 +1,5 @@ import { type RefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { useTranslation } from "react-i18next"; import type { StoryActiveSlideMode, StoryChapter, StoryMap } from "@geolibre/core"; import type { MapController } from "@geolibre/map"; diff --git a/apps/geolibre-desktop/src/components/storymap/StoryMapPresenter.tsx b/apps/geolibre-desktop/src/components/storymap/StoryMapPresenter.tsx index 5e9abb80a..4188ccc44 100644 --- a/apps/geolibre-desktop/src/components/storymap/StoryMapPresenter.tsx +++ b/apps/geolibre-desktop/src/components/storymap/StoryMapPresenter.tsx @@ -10,7 +10,7 @@ import { } from "react"; import { createPortal } from "react-dom"; import { useTranslation } from "react-i18next"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { useAppStore, type StoryActiveSlideMode, diff --git a/apps/geolibre-desktop/src/hooks/useCommandBridge.ts b/apps/geolibre-desktop/src/hooks/useCommandBridge.ts index 16265c40232b5008b4a4ec6dd5d1dfa31190d008..96b7cd247708e813ad2fc5633b20b7b71aae960c 100644 GIT binary patch delta 15 Wcmca^bjN6d6^oWaV(~^xIY|I7`2{5a delta 10 Rcmca(blqrz)kb?+NdOwq1PA~C diff --git a/apps/geolibre-desktop/src/hooks/useNotebookBridge.ts b/apps/geolibre-desktop/src/hooks/useNotebookBridge.ts index 6c90fe55b..f573ff2ae 100644 --- a/apps/geolibre-desktop/src/hooks/useNotebookBridge.ts +++ b/apps/geolibre-desktop/src/hooks/useNotebookBridge.ts @@ -1,5 +1,5 @@ import { useAppStore } from "@geolibre/core"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import { type RefObject, useEffect } from "react"; import type { MapController } from "@geolibre/map"; import { createScriptingHandlers } from "../lib/scripting/scriptingApi"; diff --git a/apps/geolibre-desktop/src/lib/assistant/tools.ts b/apps/geolibre-desktop/src/lib/assistant/tools.ts index cd74b7168..ae67af160 100644 --- a/apps/geolibre-desktop/src/lib/assistant/tools.ts +++ b/apps/geolibre-desktop/src/lib/assistant/tools.ts @@ -6,7 +6,7 @@ import { } from "@geolibre/core"; import type { MapController } from "@geolibre/map"; import type { InvokableTool, JSONValue } from "@strands-agents/sdk"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { tool } from "@strands-agents/sdk"; import type { FeatureCollection } from "geojson"; import { z } from "zod"; diff --git a/apps/geolibre-desktop/src/lib/maplibre-worker.ts b/apps/geolibre-desktop/src/lib/maplibre-worker.ts new file mode 100644 index 000000000..a8c47a464 --- /dev/null +++ b/apps/geolibre-desktop/src/lib/maplibre-worker.ts @@ -0,0 +1,22 @@ +import { setWorkerUrl } from "maplibre-gl"; +// Vite bundles the worker (it imports `./maplibre-gl-shared.mjs`) and hands back +// the emitted asset URL. +import maplibreWorkerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url"; + +/** + * Point MapLibre at its bundled worker. + * + * v6 ships the worker as a **separate file** and locates it at runtime with + * `new URL("./maplibre-gl-worker.mjs", import.meta.url)`. That is a computed + * string, not a static `new URL(…, import.meta.url)` literal, so no bundler can + * see it: the file is never emitted, and at runtime the URL resolves next to the + * hashed app chunk (`/assets/maplibre-gl-worker.mjs`), where nothing exists. In + * the web build the SPA fallback answers that request with `index.html`, so the + * failure is not even a 404 — the worker is handed HTML, and the request hangs + * rather than erroring. + * + * `setWorkerUrl` overrides that lookup with an asset the build actually emits. + * Must run before the first `Map` is constructed, so it is imported for effect + * from the app entry. + */ +setWorkerUrl(maplibreWorkerUrl); diff --git a/apps/geolibre-desktop/src/lib/print-layout-export.ts b/apps/geolibre-desktop/src/lib/print-layout-export.ts index d0ff028f2..7edb4fa2e 100644 --- a/apps/geolibre-desktop/src/lib/print-layout-export.ts +++ b/apps/geolibre-desktop/src/lib/print-layout-export.ts @@ -6,7 +6,7 @@ * export helpers rasterize {@link drawLayout} at print resolution. */ import { zipSync } from "fflate"; -import jsPDF from "jspdf"; +import { jsPDF } from "jspdf"; import { isFullViewportMapCanvas } from "./print-capture"; import { drawLayout, pageMm, pagePx, resolvePageSize, type LayoutOptions } from "./print-layout"; import type { PrintExtent } from "./print-extent"; diff --git a/apps/geolibre-desktop/src/lib/rtl-text.ts b/apps/geolibre-desktop/src/lib/rtl-text.ts index 011629a34..d71efdf3e 100644 --- a/apps/geolibre-desktop/src/lib/rtl-text.ts +++ b/apps/geolibre-desktop/src/lib/rtl-text.ts @@ -1,4 +1,4 @@ -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; // Vite resolves this to a hashed, same-origin asset URL at build time, so the // RTL plugin ships with the app (web, desktop, and the Jupyter embed) instead of // being fetched from a CDN — keeps every build's CSP simple and works offline. diff --git a/apps/geolibre-desktop/src/lib/storymap-pdf.ts b/apps/geolibre-desktop/src/lib/storymap-pdf.ts index ff25c7318..36c980662 100644 --- a/apps/geolibre-desktop/src/lib/storymap-pdf.ts +++ b/apps/geolibre-desktop/src/lib/storymap-pdf.ts @@ -10,7 +10,7 @@ * stays free of MapLibre and the DOM and can be unit tested with data-URL * images. */ -import jsPDF from "jspdf"; +import { jsPDF } from "jspdf"; import { pageMm, resolvePageSize, type Orientation, type PaperSizeId } from "./print-layout"; /** An image to embed: a canvas (app) or a PNG/JPEG data URL (tests), plus its diff --git a/apps/geolibre-desktop/src/main.tsx b/apps/geolibre-desktop/src/main.tsx index d0bd042b4..f7a9a7469 100644 --- a/apps/geolibre-desktop/src/main.tsx +++ b/apps/geolibre-desktop/src/main.tsx @@ -1,4 +1,6 @@ import "./lib/symbol-dispose-polyfill"; +// Must precede any Map construction (see the module docs). +import "./lib/maplibre-worker"; import React from "react"; import ReactDOM from "react-dom/client"; import "@geoman-io/maplibre-geoman-free/dist/maplibre-geoman.css"; diff --git a/apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts b/apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts new file mode 100644 index 000000000..7e001f9d3 --- /dev/null +++ b/apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts @@ -0,0 +1,67 @@ +import type { Plugin } from "vite"; + +/** + * Rewrite `import X from "maplibre-gl"` to `import * as X from "maplibre-gl"` + * inside the published bundles of packages that still target MapLibre v5. + * + * MapLibre v6 is ESM-only with **no default export**, so a dist file compiled + * against v5's default export is a hard bundling error: + * + * [MISSING_EXPORT] "default" is not exported by ".../maplibre-gl.mjs" + * + * A namespace object is a drop-in for what these bundles actually do with the + * binding (`X.Map`, `new X.Popup()`, …), so the rewrite is safe. + * + * This is a **temporary** shim for third-party packages we do not control. Every + * opengeos-owned package has already been migrated and released, so the list + * below should only ever shrink. See opengeos/GeoLibre#1489 (blocker 1). + * + * The plugin fails the build if a listed package stops matching — that means it + * either shipped a fix (delete the entry) or changed its bundle shape (revisit), + * and silently shimming nothing would hide both. + */ +const SHIMMED_PACKAGES = ["@esri/maplibre-arcgis", "@geoman-io/maplibre-geoman-free"] as const; + +// Matches a default import of maplibre-gl in minified or unminified ESM: +// import Zt from"maplibre-gl" import e from "maplibre-gl" +const DEFAULT_IMPORT = /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s*(["'])maplibre-gl\2/g; + +export function maplibreDefaultImportShim(): Plugin { + const rewritten = new Set(); + + return { + name: "geolibre:maplibre-default-import-shim", + enforce: "pre", + apply: () => true, + + transform(code, id) { + const pkg = SHIMMED_PACKAGES.find((name) => id.includes(`/node_modules/${name}/`)); + if (!pkg || !code.includes("maplibre-gl")) return null; + + DEFAULT_IMPORT.lastIndex = 0; + if (!DEFAULT_IMPORT.test(code)) return null; + + rewritten.add(pkg); + DEFAULT_IMPORT.lastIndex = 0; + return { + code: code.replace( + DEFAULT_IMPORT, + (_match, binding: string, quote: string) => + `import * as ${binding} from ${quote}maplibre-gl${quote}`, + ), + map: null, + }; + }, + + buildEnd(error) { + if (error) return; + const stale = SHIMMED_PACKAGES.filter((name) => !rewritten.has(name)); + if (stale.length === 0) return; + this.error( + `maplibre default-import shim matched nothing in: ${stale.join(", ")}. ` + + `If the package now ships a v6-compatible build, remove it from ` + + `SHIMMED_PACKAGES in vite-plugins/maplibre-default-import-shim.ts.`, + ); + }, + }; +} diff --git a/apps/geolibre-desktop/vite.config.ts b/apps/geolibre-desktop/vite.config.ts index 16818bb7a..4d24afe5e 100644 --- a/apps/geolibre-desktop/vite.config.ts +++ b/apps/geolibre-desktop/vite.config.ts @@ -11,6 +11,7 @@ import { bundledPlugins } from "./vite-plugins/bundled-plugins"; import { copyCesiumAssets } from "./vite-plugins/copy-cesium-assets"; import { copyRtlText } from "./vite-plugins/copy-rtl-text"; import { copyVectorOps } from "./vite-plugins/copy-vector-ops"; +import { maplibreDefaultImportShim } from "./vite-plugins/maplibre-default-import-shim"; const GEOAGENT_BROWSER_BUNDLE = "maplibre-gl-geoagent/dist/browser-"; const EARTH_ENGINE_CONTROL_BUNDLE = "maplibre-gl-earth-engine/dist/"; @@ -860,6 +861,7 @@ function pwaPlugin(): Plugin[] { export default defineConfig({ base: APP_BASE, plugins: [ + maplibreDefaultImportShim(), ...(PGLITE_CDN ? [pgliteCdnLoaderPlugin()] : []), ...(CEREUS_CDN ? [cereusCdnLoaderPlugin()] : []), duckdbWasmBundlesPlugin(), @@ -958,6 +960,13 @@ export default defineConfig({ // asset reference, so serve it as-is. Only reached through the lazy // dynamic import in local-netcdf.ts when a user opens a local file. "h5wasm", + // These two still default-import maplibre-gl, which v6 does not provide. + // `maplibreDefaultImportShim` rewrites that, but the dependency optimizer + // runs outside the plugin pipeline and would fail before the shim is ever + // consulted, so serve them unbundled and let the shim transform them. + // Remove alongside their entries in the shim. See opengeos/GeoLibre#1489. + "@esri/maplibre-arcgis", + "@geoman-io/maplibre-geoman-free", ], }, build: { diff --git a/package-lock.json b/package-lock.json index 4d5fc107b..dfc07a8f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "i18next": "^26.3.6", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", @@ -5448,9 +5448,9 @@ "license": "BSD-2-Clause" }, "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", - "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", "license": "BSD-2-Clause" }, "node_modules/@mapbox/vector-tile": { @@ -5462,15 +5462,6 @@ "@mapbox/point-geometry": "~0.1.0" } }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@maplibre/geojson-vt": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz", @@ -5494,12 +5485,12 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz", - "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==", + "version": "26.2.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.2.1.tgz", + "integrity": "sha512-QFKCXkOeSzOr8jF75jm6kySOg+dUvOehPhRi68gcOYPHb7U5JloUq0dJW0Y5/fZV8ygfT0Vp2RWodvq+fyxFWA==", "license": "ISC", "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.3", "@mapbox/unitbezier": "^1.0.0", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", @@ -5512,12 +5503,6 @@ "gl-style-validate": "dist/gl-style-validate.mjs" } }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", - "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", - "license": "BSD-2-Clause" - }, "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", @@ -16605,27 +16590,25 @@ "license": "MIT" }, "node_modules/maplibre-gl": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz", - "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.0.0.tgz", + "integrity": "sha512-1wBgEhzTZfA+cDpFdt0fM1mJA5mJ90fifORJ7D8JcKLJCvPT/iTx9bNxkP7DqEYde65DJd7gE7h83khwNRqdyg==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^1.1.0", - "@mapbox/tiny-sdf": "^2.1.0", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^2.0.4", - "@mapbox/whoots-js": "^3.1.0", - "@maplibre/geojson-vt": "^6.1.0", - "@maplibre/maplibre-gl-style-spec": "^24.8.1", - "@maplibre/mlt": "^1.1.8", - "@maplibre/vt-pbf": "^4.3.0", + "@mapbox/tiny-sdf": "^2.2.0", + "@mapbox/unitbezier": "^1.0.0", + "@mapbox/vector-tile": "^3.0.0", + "@maplibre/geojson-vt": "^6.1.1", + "@maplibre/maplibre-gl-style-spec": "^26.1.0", + "@maplibre/mlt": "^1.1.12", + "@maplibre/vt-pbf": "^4.3.2", "@types/geojson": "^7946.0.16", - "earcut": "^3.0.2", + "earcut": "^3.2.3", "gl-matrix": "^3.4.4", - "kdbush": "^4.0.2", + "kdbush": "^4.1.0", "murmurhash-js": "^1.0.0", - "pbf": "^4.0.1", + "pbf": "^5.1.2", "potpack": "^2.1.0", "quickselect": "^3.0.0", "tinyqueue": "^3.0.0" @@ -17311,14 +17294,14 @@ "license": "ISC" }, "node_modules/maplibre-gl/node_modules/@mapbox/vector-tile": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz", - "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-3.0.0.tgz", + "integrity": "sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/point-geometry": "~1.1.0", "@types/geojson": "^7946.0.16", - "pbf": "^4.0.2" + "pbf": "^5.0.0" } }, "node_modules/maplibre-gl/node_modules/earcut": { @@ -17327,6 +17310,18 @@ "integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==", "license": "ISC" }, + "node_modules/maplibre-gl/node_modules/pbf": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/maplibre-gl/node_modules/tinyqueue": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", @@ -21732,37 +21727,6 @@ "typescript": "^7.0.2" } }, - "packages/core/node_modules/@mapbox/unitbezier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", - "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", - "license": "BSD-2-Clause" - }, - "packages/core/node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "26.2.1", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.2.1.tgz", - "integrity": "sha512-QFKCXkOeSzOr8jF75jm6kySOg+dUvOehPhRi68gcOYPHb7U5JloUq0dJW0Y5/fZV8ygfT0Vp2RWodvq+fyxFWA==", - "license": "ISC", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "^2.0.3", - "@mapbox/unitbezier": "^1.0.0", - "json-stringify-pretty-compact": "^4.0.0", - "minimist": "^1.2.8", - "quickselect": "^3.0.0", - "tinyqueue": "^3.0.0" - }, - "bin": { - "gl-style-format": "dist/gl-style-format.mjs", - "gl-style-migrate": "dist/gl-style-migrate.mjs", - "gl-style-validate": "dist/gl-style-validate.mjs" - } - }, - "packages/core/node_modules/tinyqueue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" - }, "packages/core/node_modules/typescript": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", @@ -21815,7 +21779,7 @@ "@turf/mask": "^7.3.5", "cesium": "^1.143.0", "fast-xml-parser": "^5.10.1", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-layer-control": "^0.17.4", "pmtiles": "^4.4.1", "react": "^19.2.8", @@ -21897,7 +21861,7 @@ "h5wasm": "^0.10.3", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", diff --git a/package.json b/package.json index f0b0d7c64..aade2337c 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "typecheck": "npm run build", "lint": "eslint apps packages workers tests", "test": "npm run test:frontend", - "test:frontend": "node --import tsx --test tests/*.test.ts", - "test:frontend:coverage": "node --import tsx --test --experimental-test-coverage --test-coverage-lines=78 --test-coverage-branches=78 --test-coverage-functions=63 --test-coverage-exclude=\"tests/**\" --test-coverage-exclude=\"e2e/**\" --test-coverage-exclude=\"**/*.config.*\" tests/*.test.ts", + "test:frontend": "node --import ./tests/hooks/register-maplibre-shim.mjs --import tsx --test tests/*.test.ts", + "test:frontend:coverage": "node --import ./tests/hooks/register-maplibre-shim.mjs --import tsx --test --experimental-test-coverage --test-coverage-lines=78 --test-coverage-branches=78 --test-coverage-functions=63 --test-coverage-exclude=\"tests/**\" --test-coverage-exclude=\"e2e/**\" --test-coverage-exclude=\"**/*.config.*\" tests/*.test.ts", "test:backend": "python -m pytest backend/geolibre_server/tests", "test:backend:coverage": "python -m pytest backend/geolibre_server/tests --cov=geolibre_server --cov-report=term-missing --cov-fail-under=55", "test:worker": "npm run typecheck -w geolibre-viewer-worker && npm run typecheck -w geolibre-collab-worker && npm run typecheck -w geolibre-tiles-worker && npm run typecheck -w geolibre-ai-proxy-worker", @@ -44,6 +44,7 @@ "brace-expansion": "^5.0.8", "fast-uri": "3.1.4", "filelist": "^2.0.2", + "maplibre-gl": "^6.0.0", "sweepline-intersections": "^1.5.0", "uuid": "^14.0.0" }, diff --git a/packages/map/package.json b/packages/map/package.json index 60f8cc9cf..8b1aa2839 100644 --- a/packages/map/package.json +++ b/packages/map/package.json @@ -22,7 +22,7 @@ "@turf/mask": "^7.3.5", "cesium": "^1.143.0", "fast-xml-parser": "^5.10.1", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-layer-control": "^0.17.4", "pmtiles": "^4.4.1", "react": "^19.2.8", diff --git a/packages/map/src/MapCanvas.tsx b/packages/map/src/MapCanvas.tsx index ec4ace181..22400def3 100644 --- a/packages/map/src/MapCanvas.tsx +++ b/packages/map/src/MapCanvas.tsx @@ -6,7 +6,7 @@ import { useAppStore, type GeoLibreLayer, } from "@geolibre/core"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { memo, useEffect, useMemo, useRef } from "react"; import { circleLayerId, diff --git a/packages/map/src/collapsed-attribution-control.ts b/packages/map/src/collapsed-attribution-control.ts index d8f3675ee..16391e266 100644 --- a/packages/map/src/collapsed-attribution-control.ts +++ b/packages/map/src/collapsed-attribution-control.ts @@ -1,4 +1,4 @@ -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; /** Set by MapLibre once the control is in compact (collapsible) mode. */ const COMPACT_CLASS = "maplibregl-compact"; diff --git a/packages/map/src/dynamic-style-property.ts b/packages/map/src/dynamic-style-property.ts new file mode 100644 index 000000000..b6827cd9a --- /dev/null +++ b/packages/map/src/dynamic-style-property.ts @@ -0,0 +1,51 @@ +/** + * Escape hatches for style properties whose names are only known at runtime. + * + * MapLibre v6 made `set/getPaintProperty` and `set/getLayoutProperty` generic + * over `keyof AllPaintProperties` / `keyof AllLayoutProperties`, so they accept + * only literal property names the compiler can see. GeoLibre computes property + * names in several places that the type system cannot follow: + * + * - iterating `Object.entries(spec.paint)` when applying a whole style block, + * - building transition names as `` `${prop}-transition` ``, + * - taking a `property: string` parameter through a generic helper. + * + * Each needs a cast. They live here — one documented module — rather than being + * sprinkled at every call site, so the blast radius is visible if MapLibre + * tightens or relaxes these signatures again. Prefer the typed MapLibre methods + * whenever the property name *is* a literal; reach for these only when it is not. + */ + +/** The slice of a MapLibre `Map` these helpers reach through. */ +interface DynamicStyleTarget { + setPaintProperty: (layerId: string, property: string, value: unknown) => void; + getPaintProperty: (layerId: string, property: string) => unknown; + setLayoutProperty: (layerId: string, property: string, value: unknown) => void; + getLayoutProperty: (layerId: string, property: string) => unknown; +} + +export function setDynamicPaintProperty( + map: object, + layerId: string, + property: string, + value: unknown, +): void { + (map as DynamicStyleTarget).setPaintProperty(layerId, property, value); +} + +export function getDynamicPaintProperty(map: object, layerId: string, property: string): unknown { + return (map as DynamicStyleTarget).getPaintProperty(layerId, property); +} + +export function setDynamicLayoutProperty( + map: object, + layerId: string, + property: string, + value: unknown, +): void { + (map as DynamicStyleTarget).setLayoutProperty(layerId, property, value); +} + +export function getDynamicLayoutProperty(map: object, layerId: string, property: string): unknown { + return (map as DynamicStyleTarget).getLayoutProperty(layerId, property); +} diff --git a/packages/map/src/generated-images.ts b/packages/map/src/generated-images.ts index 472c8f784..c2e13d247 100644 --- a/packages/map/src/generated-images.ts +++ b/packages/map/src/generated-images.ts @@ -1,4 +1,4 @@ -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** * Lazily-generated MapLibre sprite images (fill-pattern tiles and marker icons). diff --git a/packages/map/src/layer-sync.ts b/packages/map/src/layer-sync.ts index 9918e1492..e233e1012 100644 --- a/packages/map/src/layer-sync.ts +++ b/packages/map/src/layer-sync.ts @@ -13,7 +13,7 @@ import { validateMapExpression, } from "@geolibre/core"; import { addProtocol, config } from "maplibre-gl"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import type { PropertyValueSpecification } from "maplibre-gl"; import { FileSource, PMTiles, Protocol } from "pmtiles"; import { @@ -55,6 +55,7 @@ import { } from "./derived-geometry"; import { ensureGeneratedImageHandler } from "./generated-images"; import { prepareFillPattern } from "./fill-patterns"; +import { setDynamicLayoutProperty, setDynamicPaintProperty } from "./dynamic-style-property"; import { prepareLineDecoration } from "./line-decorations"; import { markerIconSizeValue, prepareMarker } from "./markers"; import { isPlaceholderLayer } from "./placeholders"; @@ -1635,7 +1636,7 @@ function syncVectorControlPointSymbology( // of the other rule-based paint overrides apply to control-owned layers. const radius = proportionalRadiusExpression(layer.style); if (radius) { - map.setPaintProperty(circleNativeId, "circle-radius", radius); + setDynamicPaintProperty(map, circleNativeId, "circle-radius", radius); overriddenRadiusIdsFor(map).add(circleNativeId); } else { restoreOverriddenCircleRadius(map, circleNativeId, layer); @@ -1690,7 +1691,7 @@ function setExternalNativeLayerPaint( for (const [property, value] of Object.entries(paint)) { try { - map.setPaintProperty(nativeLayerId, property, value); + setDynamicPaintProperty(map, nativeLayerId, property, value); } catch { // External controls can create heterogeneous style layers. Ignore paint // properties that do not apply to a specific native layer type. @@ -3347,12 +3348,12 @@ function ensureLayer( if (map.getLayer(id)) { if (spec.paint) { for (const [key, value] of Object.entries(spec.paint)) { - map.setPaintProperty(id, key, value); + setDynamicPaintProperty(map, id, key, value); } } if (spec.layout) { for (const [key, value] of Object.entries(spec.layout)) { - map.setLayoutProperty(id, key, value); + setDynamicLayoutProperty(map, id, key, value); } } if ("filter" in spec) { diff --git a/packages/map/src/map-controller.ts b/packages/map/src/map-controller.ts index c29dc8b68..f243b38cd 100644 --- a/packages/map/src/map-controller.ts +++ b/packages/map/src/map-controller.ts @@ -18,7 +18,7 @@ import type { } from "@geolibre/core"; import bbox from "@turf/bbox"; import type { Feature, FeatureCollection, Geometry } from "geojson"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { LayerControl, type CustomLayerAdapter, type LayerState } from "maplibre-gl-layer-control"; import { CollapsedAttributionControl } from "./collapsed-attribution-control"; import { @@ -46,6 +46,18 @@ import { getOfflineBasemapStyle, isOfflineBasemapSentinel } from "./protomaps-ba import { ResetBearingControl } from "./reset-bearing-control"; import { MaptoolkitLogoControl } from "./maptoolkit-logo-control"; import { TerrainControl, DEFAULT_TERRAIN_EXAGGERATION } from "./terrain-control"; +import { getDynamicPaintProperty, setDynamicPaintProperty } from "./dynamic-style-property"; + +/** + * GeolocateControl is constructed through this indirection so tests can + * substitute a fake. Assigning over `maplibregl.GeolocateControl` is not an + * option: a MapLibre v6 ESM namespace is sealed and rejects assignment + * (`TypeError: Cannot assign to property 'GeolocateControl' of [object Module]`). + */ +export const geolocateControlFactory = { + create: (options: maplibregl.GeolocateControlOptions): maplibregl.GeolocateControl => + new maplibregl.GeolocateControl(options), +}; const DEFAULT_PROJECTION: maplibregl.ProjectionSpecification = { type: "globe", @@ -637,11 +649,11 @@ export class MapController { const props = OPACITY_PAINT_PROPERTIES[styleLayer.type] ?? []; for (const prop of props) { if (typeof durationMs === "number" && durationMs >= 0) { - this.map.setPaintProperty(nativeId, `${prop}-transition`, { + setDynamicPaintProperty(this.map, nativeId, `${prop}-transition`, { duration: durationMs, }); } - this.map.setPaintProperty(nativeId, prop, clamped); + setDynamicPaintProperty(this.map, nativeId, prop, clamped); } } } @@ -667,7 +679,7 @@ export class MapController { const styleLayer = this.map.getLayer(nativeId); if (!styleLayer) continue; for (const prop of OPACITY_PAINT_PROPERTIES[styleLayer.type] ?? []) { - this.map.setPaintProperty(nativeId, `${prop}-transition`, { + setDynamicPaintProperty(this.map, nativeId, `${prop}-transition`, { duration: 0, }); } @@ -1177,7 +1189,7 @@ export class MapController { this.basemapOriginalPaintValues.set(layerId, originalPaintValues); } if (!originalPaintValues.has(property)) { - originalPaintValues.set(property, this.map.getPaintProperty(layerId, property)); + originalPaintValues.set(property, getDynamicPaintProperty(this.map, layerId, property)); } const original = originalPaintValues.get(property); @@ -1188,7 +1200,7 @@ export class MapController { ? original * this.basemapOpacity : this.basemapOpacity; try { - this.map.setPaintProperty(layerId, property, opacity); + setDynamicPaintProperty(this.map, layerId, property, opacity); } catch { // Some third-party custom style layers may not expose paint properties. } @@ -2285,7 +2297,7 @@ export class MapController { if (!this.map || this.geolocateControl || !this.controlVisibility.geolocate) { return false; } - const control = new maplibregl.GeolocateControl({ + const control = geolocateControlFactory.create({ positionOptions: { enableHighAccuracy: true, }, diff --git a/packages/map/src/mapbox-style.ts b/packages/map/src/mapbox-style.ts index c29122f3a..c9b0880dd 100644 --- a/packages/map/src/mapbox-style.ts +++ b/packages/map/src/mapbox-style.ts @@ -29,7 +29,7 @@ * free of credential plumbing and makes a saved project self-contained. */ -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** Host serving both the style descriptors and the `mapbox://` redirect targets. */ const MAPBOX_API_HOST = "api.mapbox.com"; diff --git a/packages/map/src/maptoolkit-logo-control.ts b/packages/map/src/maptoolkit-logo-control.ts index 707218864..f8641a0d2 100644 --- a/packages/map/src/maptoolkit-logo-control.ts +++ b/packages/map/src/maptoolkit-logo-control.ts @@ -1,4 +1,4 @@ -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** Where the logo links, and the brand name used for the tooltip/aria label. */ const MAPTOOLKIT_URL = "https://www.maptoolkit.org/"; diff --git a/packages/map/src/planetary-scale-control.ts b/packages/map/src/planetary-scale-control.ts index 9107ec50b..18954e10e 100644 --- a/packages/map/src/planetary-scale-control.ts +++ b/packages/map/src/planetary-scale-control.ts @@ -5,7 +5,7 @@ import { scaleSpan, type MapScaleUnit, } from "@geolibre/core"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; // Re-exported so the nice-number rounding and unit conversion — now shared with // the Print Layout scale bar in `@geolibre/core` — can still be imported from diff --git a/packages/map/src/protomaps-basemap.ts b/packages/map/src/protomaps-basemap.ts index 983dd70bf..77149a7b5 100644 --- a/packages/map/src/protomaps-basemap.ts +++ b/packages/map/src/protomaps-basemap.ts @@ -8,7 +8,7 @@ // via getOfflineBasemapStyle(). Glyphs and sprites default to app-bundled // assets (see scripts/fetch-basemaps-assets.mjs) so it renders fully offline. import { layers, namedFlavor } from "@protomaps/basemaps"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** The Protomaps basemap flavors we expose. */ export const PROTOMAPS_FLAVORS = ["light", "dark", "white", "grayscale", "black"] as const; diff --git a/packages/map/src/reset-bearing-control.ts b/packages/map/src/reset-bearing-control.ts index cf48fdfb0..7b657a41f 100644 --- a/packages/map/src/reset-bearing-control.ts +++ b/packages/map/src/reset-bearing-control.ts @@ -1,4 +1,4 @@ -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** * Bearing/pitch below this magnitude is treated as "north-up and flat", so a diff --git a/packages/map/src/terrain-control.ts b/packages/map/src/terrain-control.ts index 0a2e26017..24b4fd666 100644 --- a/packages/map/src/terrain-control.ts +++ b/packages/map/src/terrain-control.ts @@ -1,4 +1,4 @@ -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; /** * Clicks landing within this window of the previous one are treated as a diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 4424316c6..b74949e8a 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -36,7 +36,7 @@ "h5wasm": "^0.10.3", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", diff --git a/packages/plugins/src/arcgis-maplibre.d.ts b/packages/plugins/src/arcgis-maplibre.d.ts index a439abd80..9a9f9680f 100644 --- a/packages/plugins/src/arcgis-maplibre.d.ts +++ b/packages/plugins/src/arcgis-maplibre.d.ts @@ -1,5 +1,5 @@ declare module "@esri/maplibre-arcgis" { - import type maplibregl from "maplibre-gl"; + import type * as maplibregl from "maplibre-gl"; export interface IHostedLayerOptions { attribution?: string; diff --git a/packages/plugins/src/plugins/arcgis-layer.ts b/packages/plugins/src/plugins/arcgis-layer.ts index 24a942f66..3a3e675af 100644 --- a/packages/plugins/src/plugins/arcgis-layer.ts +++ b/packages/plugins/src/plugins/arcgis-layer.ts @@ -3,7 +3,7 @@ import { DEFAULT_LAYER_STYLE, type GeoLibreLayer, useAppStore } from "@geolibre/core"; import type { HostedLayer, VectorTileLayer } from "@esri/maplibre-arcgis"; import type { FeatureCollection } from "geojson"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import type { GeoLibreAppAPI } from "../types"; export type ArcGISLayerType = "feature" | "vector-tile"; diff --git a/packages/plugins/src/plugins/geoagent-layer-sync.ts b/packages/plugins/src/plugins/geoagent-layer-sync.ts index 2d93b504e..ee0fcd7f4 100644 --- a/packages/plugins/src/plugins/geoagent-layer-sync.ts +++ b/packages/plugins/src/plugins/geoagent-layer-sync.ts @@ -32,12 +32,19 @@ export type GeoAgentOverlayRecord = { /** * The slice of the GeoAgent tools surface the store -> GeoAgent sync needs. * Structural (rather than maplibre-gl types) so tests can pass fakes. + * + * Declared with method syntax, not function-typed properties: MapLibre v6 made + * `set/getPaintProperty` generic over `keyof AllPaintProperties`, and under + * `strictFunctionTypes` a function-typed property is checked contravariantly, + * so a real `Map` stops satisfying a `(id: string, property: string, ...)` + * slice. Methods are checked bivariantly, which accepts both a real map and a + * test fake. */ export type GeoAgentSyncableTools = { map?: { - getLayer: (id: string) => { type: string } | undefined; - setLayoutProperty: (id: string, property: string, value: unknown) => unknown; - setPaintProperty: (id: string, property: string, value: unknown) => unknown; + getLayer(id: string): { type: string } | undefined; + setLayoutProperty(id: string, property: string, value: unknown): unknown; + setPaintProperty(id: string, property: string, value: unknown): unknown; }; removeOverlay?: (name: string) => boolean; }; diff --git a/packages/plugins/src/plugins/maplibre-annotations.ts b/packages/plugins/src/plugins/maplibre-annotations.ts index 6253f48a8..165348ce7 100644 --- a/packages/plugins/src/plugins/maplibre-annotations.ts +++ b/packages/plugins/src/plugins/maplibre-annotations.ts @@ -1,6 +1,6 @@ import { DEFAULT_LAYER_STYLE, type GeoLibreLayer, useAppStore } from "@geolibre/core"; import type { Feature, FeatureCollection, Position } from "geojson"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import type { GeoLibreAppAPI, GeoLibreMapControlPosition, GeoLibrePlugin } from "../types"; /** diff --git a/packages/plugins/src/plugins/maplibre-components.ts b/packages/plugins/src/plugins/maplibre-components.ts index 038a29d2f..308345b1d 100644 --- a/packages/plugins/src/plugins/maplibre-components.ts +++ b/packages/plugins/src/plugins/maplibre-components.ts @@ -10,7 +10,7 @@ import type { Layer } from "@deck.gl/core"; import type { MapboxOverlay } from "@deck.gl/mapbox"; import { RasterLayer, type RasterLayerProps } from "@developmentseed/deck.gl-raster"; import { fromArrayBuffer } from "geotiff"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import proj4 from "proj4"; import type { AddVectorControl, diff --git a/packages/plugins/src/plugins/maplibre-geo-editor.ts b/packages/plugins/src/plugins/maplibre-geo-editor.ts index aab093aea..89580ff6c 100644 --- a/packages/plugins/src/plugins/maplibre-geo-editor.ts +++ b/packages/plugins/src/plugins/maplibre-geo-editor.ts @@ -1,7 +1,7 @@ import { type GeoLibreLayer, lineWidthValue, styleValue, useAppStore } from "@geolibre/core"; import { Geoman, defaultLayerStyles } from "@geoman-io/maplibre-geoman-free"; import type { Feature, FeatureCollection } from "geojson"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import { GeoEditor, type GeoEditorOptions } from "maplibre-gl-geo-editor"; import { type EditedFeatureProperties, @@ -122,6 +122,16 @@ let viewImportLoadCounter = 0; const GEOMAN_EDIT_SYNC_EVENTS = ["gm:dragend", "gm:editend", "gm:rotateend"] as const; +/** + * MapLibre v6 narrowed the catch-all `Map#on`/`off` overload from `type: string` + * to `type: keyof MapEventType`, so Geoman's `gm:*` events no longer type-check + * even though the map dispatches them. Bind through this slice instead. + */ +interface ThirdPartyEventTarget { + on(type: string, listener: () => void): unknown; + off(type: string, listener: () => void): unknown; +} + export const GEO_EDITOR_PLUGIN_ID = "maplibre-gl-geo-editor"; export const maplibreGeoEditorPlugin: GeoLibrePlugin = { @@ -238,14 +248,14 @@ function bindGeomanEditSync(map: maplibregl.Map): void { unbindGeomanEditSync(); geomanEditSyncMap = map; for (const eventName of GEOMAN_EDIT_SYNC_EVENTS) { - map.on(eventName, handleGeomanEditSync); + (map as unknown as ThirdPartyEventTarget).on(eventName, handleGeomanEditSync); } } function unbindGeomanEditSync(): void { if (!geomanEditSyncMap) return; for (const eventName of GEOMAN_EDIT_SYNC_EVENTS) { - geomanEditSyncMap.off(eventName, handleGeomanEditSync); + (geomanEditSyncMap as unknown as ThirdPartyEventTarget).off(eventName, handleGeomanEditSync); } geomanEditSyncMap = null; } @@ -1260,6 +1270,11 @@ function applyGeomanDisplayLayerOpacity( } } +/** See the cast inside `setGeomanPaintProperty`. */ +interface DynamicPaintTarget { + setPaintProperty(layerId: string, property: string, value: unknown): void; +} + function setGeomanPaintProperty( map: maplibregl.Map, layerId: string, @@ -1267,7 +1282,11 @@ function setGeomanPaintProperty( value: unknown, ): void { try { - map.setPaintProperty(layerId, property, value); + // v6 types setPaintProperty as generic over `keyof AllPaintProperties`, and + // these names are computed per layer type. Same rationale as + // `dynamic-style-property.ts` in @geolibre/map, which this package cannot + // import (plugins depends only on @geolibre/core). + (map as unknown as DynamicPaintTarget).setPaintProperty(layerId, property, value); } catch { // Geoman layers are rebuilt often and may not support every paint property. } diff --git a/tests/hooks/maplibre-default-import-shim.mjs b/tests/hooks/maplibre-default-import-shim.mjs new file mode 100644 index 000000000..13ae1c70f --- /dev/null +++ b/tests/hooks/maplibre-default-import-shim.mjs @@ -0,0 +1,42 @@ +/** + * Node module-loader analogue of `apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts`. + * + * MapLibre v6 is ESM-only with no default export, so a third-party bundle + * compiled against v5's default export fails to instantiate under `node --test`: + * + * SyntaxError: The requested module 'maplibre-gl' does not provide an + * export named 'default' + * + * `tests/geo-editor-plugin.test.ts` hits this because the geo-editor plugin + * statically imports Geoman. The Vite shim cannot help here — `node --test` + * does not go through Vite — so the same rewrite is applied as a load hook. + * + * Temporary, and the list only ever shrinks: see opengeos/GeoLibre#1489 + * (blocker 1). `tests/maplibre-shim-parity.test.ts` fails if this list and the + * Vite plugin's stop agreeing. + */ + +/** Packages whose published ESM still default-imports maplibre-gl. */ +export const SHIMMED_PACKAGES = ["@esri/maplibre-arcgis", "@geoman-io/maplibre-geoman-free"]; + +// Matches a default import of maplibre-gl in minified or unminified ESM: +// import Zt from"maplibre-gl" import e from "maplibre-gl" +const DEFAULT_IMPORT = /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s*(["'])maplibre-gl\2/g; + +/** Rewrite default imports of maplibre-gl to namespace imports. */ +export function rewriteDefaultImports(source) { + return source.replace( + DEFAULT_IMPORT, + (_match, binding, quote) => `import * as ${binding} from ${quote}maplibre-gl${quote}`, + ); +} + +export async function load(url, context, nextLoad) { + const result = await nextLoad(url, context); + const shimmed = SHIMMED_PACKAGES.some((name) => url.includes(`/node_modules/${name}/`)); + if (!shimmed || result.format !== "module" || result.source == null) return result; + + const source = result.source.toString(); + if (!source.includes("maplibre-gl")) return result; + return { ...result, source: rewriteDefaultImports(source) }; +} diff --git a/tests/hooks/register-maplibre-shim.mjs b/tests/hooks/register-maplibre-shim.mjs new file mode 100644 index 000000000..afac2af3a --- /dev/null +++ b/tests/hooks/register-maplibre-shim.mjs @@ -0,0 +1,9 @@ +/** + * Registers the maplibre default-import load hook for `node --test`. + * Used via `node --import ./tests/hooks/register-maplibre-shim.mjs`, alongside + * tsx's own hooks — the two do not overlap (this one only rewrites published + * bundles under node_modules). + */ +import { register } from "node:module"; + +register("./maplibre-default-import-shim.mjs", import.meta.url); diff --git a/tests/map-controller.test.ts b/tests/map-controller.test.ts index 93899f1ce..46ed39443 100644 --- a/tests/map-controller.test.ts +++ b/tests/map-controller.test.ts @@ -1,8 +1,12 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { DEFAULT_LAYER_STYLE, type GeoLibreLayer, type LayerStyle } from "@geolibre/core"; -import { createMapController, MapController } from "../packages/map/src/map-controller"; +import { + createMapController, + geolocateControlFactory, + MapController, +} from "../packages/map/src/map-controller"; // Internal shape of MapController we reach into to inject a fake map. The // controller only ever constructs a real maplibregl.Map through init(), which @@ -889,12 +893,13 @@ function controllerWithGeolocate(): { const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); describe("MapController geolocate permission-denied recovery", () => { - const originalControl = maplibregl.GeolocateControl; + const originalCreate = geolocateControlFactory.create; function withStubbedControl(run: () => Promise): Promise { - (maplibregl as { GeolocateControl: unknown }).GeolocateControl = FakeGeolocateControl; + geolocateControlFactory.create = () => + new FakeGeolocateControl() as unknown as maplibregl.GeolocateControl; return run().finally(() => { - (maplibregl as { GeolocateControl: unknown }).GeolocateControl = originalControl; + geolocateControlFactory.create = originalCreate; }); } diff --git a/tests/mapbox-style.test.ts b/tests/mapbox-style.test.ts index 23fd514d4..11e422180 100644 --- a/tests/mapbox-style.test.ts +++ b/tests/mapbox-style.test.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { validateStyleMin } from "@maplibre/maplibre-gl-style-spec"; import { getMapboxAccessToken } from "@geolibre/core"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import { isMapboxStyleUrl, mapboxAccessTokenFromStyleUrl, diff --git a/tests/maplibre-shim-parity.test.ts b/tests/maplibre-shim-parity.test.ts new file mode 100644 index 000000000..36d5f7775 --- /dev/null +++ b/tests/maplibre-shim-parity.test.ts @@ -0,0 +1,58 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { describe, it } from "node:test"; +import { fileURLToPath } from "node:url"; +import { SHIMMED_PACKAGES } from "./hooks/maplibre-default-import-shim.mjs"; + +/** + * Two shims rewrite `import X from "maplibre-gl"` to a namespace import inside + * third-party bundles that still target v5: a Vite plugin for the app build and + * a Node load hook for `node --test` (Vite cannot reach the test runner). They + * must cover the same packages — a package shimmed in only one place fails in + * whichever environment was missed, and only there. + * + * Both are temporary. When `@esri/maplibre-arcgis` or + * `@geoman-io/maplibre-geoman-free` ship v6-compatible builds, drop the package + * from both lists together. See opengeos/GeoLibre#1489 (blocker 1). + */ +const VITE_PLUGIN = fileURLToPath( + new URL("../apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts", import.meta.url), +); + +function shimmedPackagesInVitePlugin(): string[] { + const source = readFileSync(VITE_PLUGIN, "utf8"); + const list = /const SHIMMED_PACKAGES = \[([^\]]*)\]/.exec(source); + assert.ok(list, "SHIMMED_PACKAGES array not found in the Vite plugin"); + return [...list[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]); +} + +describe("maplibre default-import shims", () => { + it("cover the same packages in the Vite build and the Node test runner", () => { + assert.deepEqual(shimmedPackagesInVitePlugin(), [...SHIMMED_PACKAGES]); + }); + + it("rewrites the default import forms both packages actually publish", async () => { + const { rewriteDefaultImports } = await import("./hooks/maplibre-default-import-shim.mjs"); + + // @esri/maplibre-arcgis (minified, no space before the specifier — the + // rewrite normalizes that to one space, which is still valid ESM). + assert.equal( + rewriteDefaultImports('import Zt from"maplibre-gl"'), + 'import * as Zt from "maplibre-gl"', + ); + // @geoman-io/maplibre-geoman-free (spaced). + assert.equal( + rewriteDefaultImports('import e from "maplibre-gl"'), + 'import * as e from "maplibre-gl"', + ); + // Named and namespace imports are already v6-safe and must be left alone. + assert.equal( + rewriteDefaultImports('import { Popup } from "maplibre-gl"'), + 'import { Popup } from "maplibre-gl"', + ); + assert.equal( + rewriteDefaultImports('import * as ml from "maplibre-gl"'), + 'import * as ml from "maplibre-gl"', + ); + }); +}); diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 000000000..3dbc1ca59 --- /dev/null +++ b/tests/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/tests/terrain-control.test.ts b/tests/terrain-control.test.ts index 377e755f0..fe3db57a9 100644 --- a/tests/terrain-control.test.ts +++ b/tests/terrain-control.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { afterEach, describe, it, mock } from "node:test"; -import type maplibregl from "maplibre-gl"; +import type * as maplibregl from "maplibre-gl"; import { TerrainControl } from "../packages/map/src/terrain-control"; // TerrainControl.onAdd builds DOM nodes; node:test has no DOM, so stand up a From bb811c4446644be21c337f459a7bd3bbf5bcbdd7 Mon Sep 17 00:00:00 2001 From: giswqs Date: Sat, 1 Aug 2026 00:36:32 -0400 Subject: [PATCH 2/3] fix: read addProtocol off the maplibre-gl namespace The KML super-overlay protocol registration (merged from main) reached for `maplibre.default.addProtocol` as a fallback for maplibre-gl's CJS build. v6 is ESM-only with no default export and no CJS build, so that property no longer exists and `tsc -b` failed the build in every job that compiles the app (CI build, E2E, website, PR preview, container image). Destructure the named export directly, matching the shape already used in maplibre-reverse-geocode.ts. --- apps/geolibre-desktop/src/lib/kml-super-overlay.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/geolibre-desktop/src/lib/kml-super-overlay.ts b/apps/geolibre-desktop/src/lib/kml-super-overlay.ts index 30e77fa17..473b9c6ec 100644 --- a/apps/geolibre-desktop/src/lib/kml-super-overlay.ts +++ b/apps/geolibre-desktop/src/lib/kml-super-overlay.ts @@ -78,10 +78,10 @@ export function setKmlSuperOverlayResolver(resolver: KmlSuperOverlayResolver | n async function ensureProtocol(): Promise { if (protocolRegistered) return; // Keep MapLibre out of tauri-io's static module graph. This also lets the - // DOM-only file-loader tests import tauri-io in Node — where maplibre-gl - // resolves to its CJS build, so the named exports sit under `default`. - const maplibre = await import("maplibre-gl"); - const addProtocol = maplibre.addProtocol ?? maplibre.default.addProtocol; + // DOM-only file-loader tests import tauri-io in Node without pulling the map + // in. MapLibre v6 is ESM-only with named exports and no default export, so + // this reads `addProtocol` straight off the namespace. + const { addProtocol } = await import("maplibre-gl"); addProtocol(PROTOCOL, handleTileRequest); protocolRegistered = true; } From a1a2f88c4e8cdd837193c5aaf6073d06d17e5e4d Mon Sep 17 00:00:00 2001 From: giswqs Date: Tue, 4 Aug 2026 00:49:06 -0400 Subject: [PATCH 3/3] chore(deps): bump maplibre-gl to 6.1.0 Updates the three workspace dependencies and the root override that forces transitive consumers onto v6. 6.1.0 is still ESM-only with no default export, so the default-import shim (Vite plugin + node --test load hook) is still required and its shimmed-package list is unchanged. --- apps/geolibre-desktop/package.json | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- packages/map/package.json | 2 +- packages/plugins/package.json | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/geolibre-desktop/package.json b/apps/geolibre-desktop/package.json index 90f090126..e60945699 100644 --- a/apps/geolibre-desktop/package.json +++ b/apps/geolibre-desktop/package.json @@ -65,7 +65,7 @@ "i18next": "^26.3.6", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", diff --git a/package-lock.json b/package-lock.json index 0f9fb942d..633030b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,7 +78,7 @@ "i18next": "^26.3.6", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", @@ -16761,9 +16761,9 @@ "license": "MIT" }, "node_modules/maplibre-gl": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.0.0.tgz", - "integrity": "sha512-1wBgEhzTZfA+cDpFdt0fM1mJA5mJ90fifORJ7D8JcKLJCvPT/iTx9bNxkP7DqEYde65DJd7gE7h83khwNRqdyg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.1.0.tgz", + "integrity": "sha512-vLRukjvbUai4SXW2/jKo8rPsw6YMXuA/b4fKFVSulZKFVRHkOvk4ZmNlDSVZpTYPV0/7/iTqq8ltYdyr764b7w==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/point-geometry": "^1.1.0", @@ -16771,7 +16771,7 @@ "@mapbox/unitbezier": "^1.0.0", "@mapbox/vector-tile": "^3.0.0", "@maplibre/geojson-vt": "^6.1.1", - "@maplibre/maplibre-gl-style-spec": "^26.1.0", + "@maplibre/maplibre-gl-style-spec": "^26.2.1", "@maplibre/mlt": "^1.1.12", "@maplibre/vt-pbf": "^4.3.2", "@types/geojson": "^7946.0.16", @@ -22323,7 +22323,7 @@ "@turf/mask": "^7.3.5", "cesium": "^1.143.0", "fast-xml-parser": "^5.10.1", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-layer-control": "^0.17.4", "pmtiles": "^4.4.1", "react": "^19.2.8", @@ -22409,7 +22409,7 @@ "h5wasm": "^0.10.3", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0", diff --git a/package.json b/package.json index ca7126759..079034fb9 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "brace-expansion": "^5.0.8", "fast-uri": "^3.1.5", "filelist": "^2.0.2", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "sweepline-intersections": "^1.5.0", "uuid": "^14.0.0" }, diff --git a/packages/map/package.json b/packages/map/package.json index 7eb946214..e7f686abb 100644 --- a/packages/map/package.json +++ b/packages/map/package.json @@ -22,7 +22,7 @@ "@turf/mask": "^7.3.5", "cesium": "^1.143.0", "fast-xml-parser": "^5.10.1", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-layer-control": "^0.17.4", "pmtiles": "^4.4.1", "react": "^19.2.8", diff --git a/packages/plugins/package.json b/packages/plugins/package.json index c11486e46..357c2ea3f 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -40,7 +40,7 @@ "h5wasm": "^0.10.3", "jspdf": "^4.2.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^6.0.0", + "maplibre-gl": "^6.1.0", "maplibre-gl-3d-tiles": "^0.5.6", "maplibre-gl-basemap-control": "^0.13.0", "maplibre-gl-components": "^0.30.0",