maplibre-gl v6 cannot land until the plugin ecosystem ships v6-compatible builds. Tracking what has to happen first, so the next Dependabot bump (#1482) is not re-attempted blind.
Why it is blocked
MapLibre v6 is an ESM-only distribution with no default export and no CJS build (the UMD and maplibre-gl-csp bundles are gone). See the v5 → v6 migration guide.
Three separate breakages result, and only the third is ours to fix:
1. Third-party packages ship dist compiled against v5's default export
import maplibregl from "maplibre-gl" is a hard bundling error against v6 ("default" is not exported by maplibre-gl.mjs). These 11 installed packages still do it in their published dist/:
opengeos-owned (9) — need a namespace-import migration + rebuild + release:
maplibre-gl-3d-tiles
maplibre-gl-components
maplibre-gl-duckdb
maplibre-gl-geo-editor
maplibre-gl-geoagent
maplibre-gl-overture-maps
maplibre-gl-raster
maplibre-gl-streetview
maplibre-gl-swipe
external (2) — we can only wait or shim:
@esri/maplibre-arcgis
@geoman-io/maplibre-geoman-free
Note maplibre-gl-raster uses the mixed form import maplibregl, { Popup } from "maplibre-gl", so a scan that only looks for the bare default import will miss it.
Re-check with:
grep -rlE 'import +[A-Za-z_$][A-Za-z0-9_$]* *(,[^;]*)? *from *["'"'"']maplibre-gl["'"'"']|require\( *["'"'"']maplibre-gl["'"'"'] *\)' \
--include='*.js' --include='*.mjs' --include='*.cjs' \
node_modules/maplibre-gl-* node_modules/@esri/maplibre-arcgis node_modules/@geoman-io/*
2. v6 dropped CJS, which breaks the Node test suite
Those packages are dual-published, and their .cjs entries require("maplibre-gl"). v6's exports map has only types and import conditions, so CJS resolution fails outright:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in node_modules/maplibre-gl/package.json
npm run test:frontend fails 29 tests on this. A Vite-level shim cannot help — node --test does not go through Vite.
3. GeoLibre patches the MapLibre namespace (ours to fix)
packages/map/src/globe-popup-occlusion.ts installs globe-occlusion behavior by assigning over the namespace:
api.Popup = GeoLibrePopup; // map-controller.ts calls installGlobePopupOcclusion(maplibregl)
Under v5's mutable CJS/UMD default export this worked. A v6 ESM namespace object is getter-only, so it throws at boot:
TypeError: Cannot set property Popup of #<Object> which has only a getter
The app never mounts the map canvas — 22 of 23 e2e tests fail. This needs a different mechanism (the patch exists so third-party plugins constructing new maplibregl.Popup() also get occlusion, so simply exporting a GeoLibrePopup for our own call sites is a behavior change worth deciding on deliberately).
What is already done
A local wip branch carries the GeoLibre-side migration, verified with tsc -b clean, eslint 0 errors, and a successful vite build:
- Dedupe to a single copy. The Dependabot lockfile resolved both 5.24.0 (hoisted, to satisfy
@maplibre/maplibre-gl-directions' ^5.0.0 peer — there is no v6-compatible release of it) and 6.0.0 (nested in the three workspaces that declare it), so Map was a different nominal type across package boundaries. That is what the original CI errors (Argument of type 'SwipeControl' is not assignable to parameter of type 'IControl') actually were — not an API change. Fixed with a root overrides entry.
- 33 files migrated to
import * as maplibregl from "maplibre-gl".
- v6's tightened typing:
set/getPaintProperty and set/getLayoutProperty are now generic over keyof AllPaintProperties / keyof AllLayoutProperties, and Map#on/off's catch-all overload narrowed from type: string to type: keyof MapEventType (which breaks binding third-party events such as Geoman's gm:*). Added packages/map/src/dynamic-style-property.ts so the unavoidable casts for runtime-computed property names live in one documented place.
Suggested order
- Migrate + release the 9 opengeos packages (namespace imports, rebuild, verify the
.cjs entry no longer requires maplibre-gl).
- Rework
globe-popup-occlusion.ts off namespace patching.
- Wait on / track
@esri/maplibre-arcgis and @geoman-io/maplibre-geoman-free. @maplibre/maplibre-gl-directions also still pins maplibre-gl@^5.0.0 as a peer.
- Re-run the bump and re-check the frontend + e2e suites, not just the build — the build passes with a shim while the app is completely broken at runtime, so a green build alone proves nothing here.
Until then, maplibre-gl should stay on 5.x and Dependabot bumps to v6 should be closed with a pointer to this issue.
maplibre-glv6 cannot land until the plugin ecosystem ships v6-compatible builds. Tracking what has to happen first, so the next Dependabot bump (#1482) is not re-attempted blind.Why it is blocked
MapLibre v6 is an ESM-only distribution with no default export and no CJS build (the UMD and
maplibre-gl-cspbundles are gone). See the v5 → v6 migration guide.Three separate breakages result, and only the third is ours to fix:
1. Third-party packages ship dist compiled against v5's default export
import maplibregl from "maplibre-gl"is a hard bundling error against v6 ("default" is not exported by maplibre-gl.mjs). These 11 installed packages still do it in their publisheddist/:opengeos-owned (9) — need a namespace-import migration + rebuild + release:
maplibre-gl-3d-tilesmaplibre-gl-componentsmaplibre-gl-duckdbmaplibre-gl-geo-editormaplibre-gl-geoagentmaplibre-gl-overture-mapsmaplibre-gl-rastermaplibre-gl-streetviewmaplibre-gl-swipeexternal (2) — we can only wait or shim:
@esri/maplibre-arcgis@geoman-io/maplibre-geoman-freeNote
maplibre-gl-rasteruses the mixed formimport maplibregl, { Popup } from "maplibre-gl", so a scan that only looks for the bare default import will miss it.Re-check with:
2. v6 dropped CJS, which breaks the Node test suite
Those packages are dual-published, and their
.cjsentriesrequire("maplibre-gl"). v6'sexportsmap has onlytypesandimportconditions, so CJS resolution fails outright:npm run test:frontendfails 29 tests on this. A Vite-level shim cannot help —node --testdoes not go through Vite.3. GeoLibre patches the MapLibre namespace (ours to fix)
packages/map/src/globe-popup-occlusion.tsinstalls globe-occlusion behavior by assigning over the namespace:Under v5's mutable CJS/UMD default export this worked. A v6 ESM namespace object is getter-only, so it throws at boot:
The app never mounts the map canvas — 22 of 23 e2e tests fail. This needs a different mechanism (the patch exists so third-party plugins constructing
new maplibregl.Popup()also get occlusion, so simply exporting aGeoLibrePopupfor our own call sites is a behavior change worth deciding on deliberately).What is already done
A local
wipbranch carries the GeoLibre-side migration, verified withtsc -bclean,eslint0 errors, and a successfulvite build:@maplibre/maplibre-gl-directions'^5.0.0peer — there is no v6-compatible release of it) and 6.0.0 (nested in the three workspaces that declare it), soMapwas a different nominal type across package boundaries. That is what the original CI errors (Argument of type 'SwipeControl' is not assignable to parameter of type 'IControl') actually were — not an API change. Fixed with a rootoverridesentry.import * as maplibregl from "maplibre-gl".set/getPaintPropertyandset/getLayoutPropertyare now generic overkeyof AllPaintProperties/keyof AllLayoutProperties, andMap#on/off's catch-all overload narrowed fromtype: stringtotype: keyof MapEventType(which breaks binding third-party events such as Geoman'sgm:*). Addedpackages/map/src/dynamic-style-property.tsso the unavoidable casts for runtime-computed property names live in one documented place.Suggested order
.cjsentry no longerrequires maplibre-gl).globe-popup-occlusion.tsoff namespace patching.@esri/maplibre-arcgisand@geoman-io/maplibre-geoman-free.@maplibre/maplibre-gl-directionsalso still pinsmaplibre-gl@^5.0.0as a peer.Until then,
maplibre-glshould stay on 5.x and Dependabot bumps to v6 should be closed with a pointer to this issue.