Skip to content

chore(deps): bump maplibre-gl to 6.0.0 - #1510

Draft
giswqs wants to merge 9 commits into
mainfrom
chore/maplibre-gl-v6
Draft

chore(deps): bump maplibre-gl to 6.0.0#1510
giswqs wants to merge 9 commits into
mainfrom
chore/maplibre-gl-v6

Conversation

@giswqs

@giswqs giswqs commented Jul 28, 2026

Copy link
Copy Markdown
Member

Draft. Everything in #1489 that is ours is done here and verified; what remains is a policy call on the shim (below), not unfinished work.

Depends on #1509 (blocker 3), already merged.

What was blocking, and where each stands

Blocker 2 — the frontend suite ✅

Not the plugin packages: their exports maps all publish a correct import condition. It 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 the .cjs entries that require("maplibre-gl").

tests/package.json with {"type": "module"} puts them in ESM scope. The 29 ERR_PACKAGE_PATH_NOT_EXPORTED failures go to zero. Measured both ways:

tests CJS tests ESM
5.24.0 4234 pass / 0 fail 31 fail — v5 ships no ESM build at all
6.0.0 29 fail, all ERR_PACKAGE_PATH_NOT_EXPORTED 0 of that class

It is a swap, not an addition, which is why it ships here and could not land ahead of the bump.

Blocker 1 — the two external packages ⚠️ shimmed

@esri/maplibre-arcgis and @geoman-io/maplibre-geoman-free (both at latest) still import maplibregl from "maplibre-gl" in their published ESM. Rather than wait, this rewrites that to a namespace import at load time, in the three places a module can enter the tree:

  1. vite-plugins/maplibre-default-import-shim.ts — the app build.
  2. tests/hooks/maplibre-default-import-shim.mjs — a Node loader hook, because node --test does not go through Vite.
  3. optimizeDeps.exclude — the dev server's dependency optimizer runs outside the plugin pipeline and would fail before the shim is consulted.

Guards so this cannot rot: tests/maplibre-shim-parity.test.ts fails if lists 1 and 2 disagree, and the Vite plugin errors the build if a listed package stops matching — i.e. when one ships a v6-compatible build, CI tells you to delete the entry instead of silently shimming forever.

This is the part worth a second opinion. The alternative is to keep this draft parked until both publish fixes. The shim is ~40 lines and self-deleting by design, but it does mean rewriting third-party code at build time.

The bug none of the gates would have caught

v6 ships its worker as a separate file, located at runtime with new URL("./maplibre-gl-worker.mjs", import.meta.url). That is a computed string, not a static literal, so no bundler can see it: the asset was never emitted, and at runtime the URL resolved 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 it does not even 404 — the worker is handed HTML and the request hangs.

The map still drew, so build, typecheck, and most of e2e were all green. Tile parsing was quietly degraded: in the same 25-second window, 39 tile requests before the fix, 68 after. It surfaced only because pwa.spec.ts waits for networkidle, which that hanging request never let happen.

Fixed by importing the worker through Vite (?worker&url, so its ./maplibre-gl-shared.mjs import is bundled too) and pointing setWorkerUrl at the emitted asset.

Everything else

  • 30 files migrated to import * as maplibregl from "maplibre-gl".
  • Root overrides pin@maplibre/maplibre-gl-directions still peers maplibre-gl@^5.0.0, which otherwise resolves a second hoisted copy and makes Map a different nominal type across package boundaries (the real cause of the original SwipeControl is not assignable to IControl errors). One copy installs now.
  • packages/map/src/dynamic-style-property.ts — v6 made set/getPaintProperty and set/getLayoutProperty generic over keyof AllPaintProperties. Property names we compute at runtime (Object.entries(spec.paint), `${prop}-transition`) need a cast; they live in one documented module instead of scattered call sites.
  • Geoman's gm:* eventsMap#on/off's catch-all overload narrowed from type: string to keyof MapEventType.
  • GeoAgentSyncableTools — its map slice now uses method syntax, so a real Map still satisfies it under strictFunctionTypes while test fakes still do.
  • GeolocateControl factorymap-controller.test.ts swapped maplibregl.GeolocateControl to inject a fake, which the sealed v6 namespace rejects (same root cause as fix(map): patch Popup.prototype instead of the maplibre namespace #1509). Construction now goes through a factory the test can override.
  • jsPDF imported by name — its node export condition resolves a CJS bundle whose default is the module object, not the constructor. Only surfaced once the tests moved to ESM scope; the browser build was always fine.

Verification

  • test:frontend 4236 passed / 0 failed; coverage 86.93 lines / 84.15 branches / 71.13 functions, all above the floors.
  • tsc -b && vite build clean · lint 0 errors · test:worker clean.
  • Browser, dev server and production build: map boots, globe renders, no console errors, no hanging requests. Geoman's drawing toolbar renders through the shim; @esri/maplibre-arcgis instantiates through it with all 8 exports intact.
  • test:e2e 19/23 locally. All four failures (a11y, both export specs, layer-panel) reproduce on this machine independent of this branch and each passes in isolation — they look like local resource contention, and CI is the tiebreaker.

Not run locally: backend/rust suites, which this cannot affect.

Clears blocker 2 of #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.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7ab0f762-a63b-47b7-85aa-40a3304c083d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/maplibre-gl-v6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🔍 Cloudflare PR preview

Item Value
Site https://1b79aab7.geolibre-preview.pages.dev
Demo app https://1b79aab7.geolibre-preview.pages.dev/demo/
Commit a1a2f88

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🔍 GitHub Pages PR preview

Item Value
Site Deploy failed. See the job log.
Demo app Unavailable
Commit a1a2f88

Two import conflicts, both from main editing the same maplibre-gl import
lines this branch is converting for v6 (which has no default export):

- packages/plugins/src/plugins/maplibre-annotations.ts: main promoted the
  type-only default import to a value import because it added runtime use
  of `maplibregl.LngLat`/`maplibregl.Marker`. Resolved to a value namespace
  import (`import * as maplibregl`) so both the runtime use and the v6
  no-default-export rule hold.
- apps/geolibre-desktop/src/components/layout/MapContextMenu.tsx: kept
  main's added lucide-react icons and `useMemo`, with this branch's
  `import type * as maplibregl` (still type-only here).

Verified no default import of maplibre-gl survives anywhere in first-party
code, then ran the gate: build, 4612 frontend tests, worker typecheck.

// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quality/robustness (low-medium confidence): DEFAULT_IMPORT only matches a bare default import (import X from "maplibre-gl"). If either shimmed package's published bundle ever emits a combined default+named import, e.g. import X, { Y } from "maplibre-gl", this regex won't match (no from immediately after the identifier), so the rewrite — and the buildEnd staleness check — would silently miss it, and the build would fail downstream with the raw [MISSING_EXPORT] "default" error instead of the intended clear this.error() message.

tests/maplibre-shim-parity.test.ts only asserts against the two current known bundle shapes, so this would only surface on a future dependency bump that changes the import form. Not a problem today, but worth a short comment noting the regex's coverage is intentionally narrow (or extending it), so it doesn't quietly bit-rot alongside the "list only ever shrinks" invariant this file documents.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

That posted successfully. Now let me finalize with the summary — the null-byte finding couldn't be posted inline (GitHub rejects comments on binary-diffed files), so it needs to go in the summary instead.

Code review

Bugs

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts contains a stray NUL byte (confirmed via byte-level scan, located right after the let prevSelectedLayer = ... line). That's why the diff shows Binary files a/... and b/... differ with 0/0 line counts instead of a normal text diff — the actual content change in this file was never rendered for review, and this looks like accidental corruption from whatever tool produced the edit rather than intentional content. Recommend re-saving the file cleanly so it round-trips as a normal text diff, and spot-checking that no other files picked up the same corruption. High confidence (could not post inline — GitHub rejects review comments on binary-diffed files).

Security

  • Nothing found. The changes are a dependency bump plus mechanical import-shape/type migrations; no new input handling, secrets, or injection surfaces.

Performance

  • Nothing found. The new Vite transform hook (maplibre-default-import-shim.ts) short-circuits on id/code string checks before doing regex work, so it adds negligible overhead outside the two shimmed packages.

Quality

  • apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts:27 — the DEFAULT_IMPORT regex only matches a bare import X from "maplibre-gl"; a future combined import X, { Y } from "maplibre-gl" form would silently miss the rewrite and fail the build with a less-clear message than the plugin's own this.error() path (posted inline). Low-medium confidence — narrow edge case, not an issue with the two packages' current published bundles.
  • The rest of the migration (30 files switching import maplibreglimport * as maplibregl, the dynamic-style-property.ts / geolocateControlFactory / Geoman event-typing escape hatches, the worker-URL fix, and the test-side shim/parity test) is careful, well-documented, and internally consistent — remaining direct setPaintProperty/setLayoutProperty calls all use literal property names, which is correct per the PR's own stated rule.

CLAUDE.md

  • No violations found. This PR doesn't touch any of the drift-prone mirrored constants called out in CLAUDE.md (Whitebox catalog, MAX_VECTOR_PMTILES_ZOOM, MAX_VECTOR_BYTES, MAP_PANEL_SELECTOR, propertySpecFor, DISTANCE_SEGMENTS/NON_DISTANCE_NAMES), and package-lock.json is updated consistently with the version bumps.

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";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor/low-confidence nit: this hardcodes maplibre-gl/dist/maplibre-gl-worker.mjs as the worker entry path. The PR wisely added tests/maplibre-shim-parity.test.ts to guard the other MapLibre-v6 workaround (the import shim) against silently rotting when the dependency updates, but there's no equivalent guard here — if a future maplibre-gl patch renames/moves this dist file, the failure mode is exactly the one this PR just fixed (a silently-hanging worker request), and nothing in CI would catch it. Worth considering a cheap existence check (e.g. an e2e/smoke assertion that the worker actually loads, or at least a comment pointing at what to re-verify on maplibre-gl bumps) so a future Dependabot bump doesn't quietly reintroduce this bug.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Both findings are posted. Now for the final summary comment.

Code review

Bugs

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts contains a stray embedded NUL byte, which is why git shows this file's change as a binary diff (Binary files ... differ) rather than a text diff, and why changed-files.txt reports +0/-0 for it. This hides the actual change from reviewers and ships a corrupted byte in checked-in source. Confidence: high that the byte is present and unintentional (verified via a raw byte scan); medium on the precise line since I lacked shell access to pinpoint it exactly.

Security

  • None found. The changes are a dependency bump (maplibre-gl v5→v6) plus mechanical import-syntax updates, a build-time/load-time compatibility shim for two third-party packages, and type-safety escape hatches — no new input handling, injection surface, or secret handling introduced.

Performance

  • None found. The new worker-loading fix (apps/geolibre-desktop/src/lib/maplibre-worker.ts) is a performance/correctness improvement (previously tile-worker requests silently hung); no regressions spotted.

Quality

  • apps/geolibre-desktop/src/lib/maplibre-worker.ts hardcodes the maplibre-gl/dist/maplibre-gl-worker.mjs path with no regression guard analogous to tests/maplibre-shim-parity.test.ts; a future maplibre-gl bump that moves/renames this file would silently reintroduce the exact hanging-worker bug this PR fixes. Low confidence / nice-to-have — flagged inline.
  • Everything else reviewed (the maplibreDefaultImportShim Vite plugin and its Node-loader twin, dynamic-style-property.ts's typed escape hatches, the geolocateControlFactory indirection for sealed-namespace assignment, the GeoAgentSyncableTools method-syntax fix for strictFunctionTypes, and the ThirdPartyEventTarget casts for Geoman's gm:* events) is well-reasoned, consistently documented, and matches the stated verification (single hoisted maplibre-gl@6.0.0 confirmed in the updated lockfile; no leftover default imports of maplibre-gl anywhere in the tree).

CLAUDE.md

  • No violations found. tests/package.json correctly stays outside the workspaces glob (apps/*, packages/*, workers/*), so it doesn't need a name field or otherwise break npm install. No tile/style host, CSP, i18n, or Whitebox-catalog concerns are implicated by this diff.


// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex only matches a bare default import (import X from "maplibre-gl"). If either shimmed package's bundle ever contains a combined default+named import — import X, { Y } from "maplibre-gl" — this won't rewrite it, and the module still has an unsatisfiable default import going into Rollup.

That in itself would likely fail loudly ([MISSING_EXPORT] "default" is not exported...), so it probably wouldn't ship silently. But the buildEnd staleness guard (lines 56-65) checks only whether the package had at least one rewrite (rewritten.add(pkg)), not whether every matching file in that package was successfully transformed. So if one file in a shimmed package uses the plain form (caught) and another uses the combined form (missed), the guard stays green while the second file still breaks the build — defeating part of the point of the guard, which is to make gaps visible rather than have them surface as an opaque bundler error.

Low confidence this pattern actually appears in either package's current bundle (no way to check node_modules in this environment), but worth a quick check, and maybe worth extending the regex to also match import X, { / import X, * as forms for robustness.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts renders as a binary diff (Binary files a/... and b/... differ) with +0/-0 in changed-files.txt. The working-tree copy contains an embedded NUL byte around offset ~4655, even though the visible content reads as normal, correct TypeScript. This looks like file corruption introduced while producing this commit (bad encoding/save) rather than an intentional change, and should be re-saved as clean UTF-8 before merge — some tooling (diff/blame viewers, linters) may mishandle a NUL in a .ts file even if the current build tolerates it. Confidence: medium-high (posting an inline comment on this file failed — GitHub can't anchor a comment to a line in a binary-rendered diff — so flagging here instead).

Security

  • None found.

Performance

  • None found. The worker-URL fix (apps/geolibre-desktop/src/lib/maplibre-worker.ts) directly addresses a real perf regression (hanging tile-worker requests under v6) and is well-targeted.

Quality

  • apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts:27DEFAULT_IMPORT only matches a bare import X from "maplibre-gl". If either shimmed package ever ships a combined default+named import (import X, { Y } from "maplibre-gl"), the rewrite would miss it and the build would fail with the exact [MISSING_EXPORT] error the shim exists to prevent — and the buildEnd staleness guard wouldn't catch this specific gap, since it only checks that some file in the package was rewritten, not that every matching file was. Confidence: low-medium (couldn't verify against the actual published bundles in this offline environment; posted inline).
  • The rest of the mechanical maplibregl default→namespace import migration (30 files), the dynamic-style-property.ts cast module, the geolocateControlFactory test-injection indirection, and the Geoman gm:* event-binding cast are all well-scoped, consistently applied, and match what the PR description claims.

CLAUDE.md

  • No violations found. The shim is documented as temporary and self-guarding per the stated design goals; overrides pin for @maplibre/maplibre-gl-directions's stale peer dependency is present and matches the PR description.

Overall the migration is careful and the reasoning for each workaround (worker URL, shim, generic paint/layout property types, sealed ESM namespace, strictFunctionTypes) is sound and well-documented in code comments. The main actionable item is the corrupted useCommandBridge.ts file, which should be fixed regardless of whether it currently breaks CI.

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.
assert.deepEqual(shimmedPackagesInVitePlugin(), [...SHIMMED_PACKAGES]);
});

it("rewrites the default import forms both packages actually publish", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quality (medium confidence): the two shims each hard-code their own copy of DEFAULT_IMPORT (apps/geolibre-desktop/vite-plugins/maplibre-default-import-shim.ts and tests/hooks/maplibre-default-import-shim.mjs), and this suite only checks that the two SHIMMED_PACKAGES lists agree — the rewrite regexes' behavior is only exercised against the .mjs copy (via rewriteDefaultImports, this test). If the Vite plugin's regex is ever tweaked (e.g. to handle a new default-import shape one of the shimmed packages starts publishing) and the .mjs copy isn't updated to match, this suite stays green even though the two shims now diverge — the dev-server/test-runner path would silently rewrite differently than the production build path. Consider asserting the two regex sources are identical (or extracting DEFAULT_IMPORT/rewriteDefaultImports into a single module both files import) so a future edit to one is forced to update the other.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs: None found. Traced the core mechanics carefully — setWorkerUrl side-effect import runs before any Map construction (single entry point, ordered imports in main.tsx), the ?worker&url Vite import correctly bundles the ESM worker given build.worker.format: "es" is already set, the dynamic-style-property.ts / GeoAgentSyncableTools / Geoman ThirdPartyEventTarget casts correctly work around the v6 type tightening without leaving any direct setPaintProperty/setLayoutProperty calls with computed property names uncast, and the maplibreDefaultImportShim regex + optimizeDeps.exclude combination is internally consistent with how Vite's dependency pre-bundling bypasses normal transform hooks.

Security: None found. The regex-based source rewriting only touches two explicitly named, trusted node_modules packages, not user input; no new network hosts, injection surface, or credential handling introduced.

Performance: None found. Both the Vite transform hook and the Node load hook bail out cheaply (substring checks) before doing any regex work, and the global-regex lastIndex reset pattern in maplibre-default-import-shim.ts is handled correctly (no accidental skipped matches).

Quality:

  • Medium confidence: tests/maplibre-shim-parity.test.ts only asserts that the Vite plugin's and the Node loader's SHIMMED_PACKAGES lists agree — it doesn't verify the two independently-maintained DEFAULT_IMPORT regexes stay behaviorally identical. A future tweak to one copy (e.g. to handle a new import shape from one of the shimmed packages) could silently diverge from the other while this suite stays green. Posted inline with a suggestion to share the regex/rewrite function or assert source equality.
  • Low confidence: apps/geolibre-desktop/src/hooks/useCommandBridge.ts shows up as a binary diff (0/0 lines changed) in pr-context/diff.patch despite containing only the same plain-text import type maplibreglimport type * as maplibregl edit every other file got. The current file content is clean ASCII with no BOM/NUL/CR, so this most likely reflects something about the pre-image blob on main rather than this PR's own edit, but it's worth a quick sanity check since reviewers currently can't see this file's diff at all (GitHub rejected an inline comment on it for the same reason).

CLAUDE.md: No violations found. The v5→v6 bump correctly threads through the three build surfaces (web, Tauri desktop, embed all share one Vite entry/config), no new external tile/style hosts were added (so no CSP allowlist update needed), and no new user-facing strings were introduced requiring i18n catalog updates.

Conflicts:
- vite.config.ts: keep both new imports (the v6 default-import shim from
  this branch, the guarded proxy helper from main).
- maplibre-geo-editor.ts: keep this branch's ThirdPartyEventTarget slice
  for Geoman's `gm:*` events on top of main's move of
  GEO_EDITOR_PLUGIN_ID into ../plugin-ids.

Also adapts main's new map-comments code to v6, which has no default
export: CommentMapOverlay.tsx and useCommentTool.ts now import the
namespace (`import * as maplibreGl` / `import type * as maplibreGl`).
Comment on lines +19 to +28
/** 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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor typing nit: map is typed as bare object in all four helpers here, which is wider than the stated intent — the docstring says the goal is only to erase the property-name genericity MapLibre v6 introduced, not the map argument itself. Typing map as maplibregl.Map (or a small structural interface) instead of object would keep these from silently accepting an arbitrary non-map object while still erasing the literal-property-name checking. Low severity (internal escape hatch, not public API), but worth tightening since the module's whole point is to keep the erasure as narrow as possible.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts diffs as a binary file (Binary files a/... and b/... differ, and changed-files.txt shows +0/-0) even though the checked-out content is ordinary TypeScript with the same one-line maplibregl import-style change every sibling file got in this migration. .gitattributes (* text=auto) only forces a binary diff when git's own heuristic finds something like a stray NUL byte in one of the two blob versions, so something appears to have corrupted this file's committed bytes despite it rendering fine in a normal viewer. Worth re-saving/re-committing as clean UTF-8 before merge so it diffs, blames, and merges normally. Confidence: medium (couldn't pin the exact byte without shell access in this review environment, but the diff/stat evidence is tool-independent).

Security

  • None found.

Performance

  • None found. The new apps/geolibre-desktop/src/lib/maplibre-worker.ts (routing MapLibre's worker through Vite's ?worker&url) correctly fixes the hanging-worker-request regression described in the PR body, and is wired ahead of the first Map construction in main.tsx.

Quality

  • packages/map/src/dynamic-style-property.ts:19-28 — the four helpers type map as bare object, wider than the stated intent of only erasing the property-name genericity MapLibre v6 introduced. Typing it as maplibregl.Map would keep the erasure scoped more narrowly. Low severity, internal-only. (posted inline)
  • Minor/optional: a few files (packages/map/src/planetary-scale-control.ts, others) switched to a value import * as maplibregl where only types are used, so import type * as maplibregl would do; harmless, not flagged inline given the size of the mechanical migration.

CLAUDE.md

  • No violations found. The relevant mirrored constants (MAX_VECTOR_PMTILES_ZOOM, MAX_VECTOR_BYTES, MAP_PANEL_SELECTOR, propertySpecFor) are gated on bumps to geolibre-wasm, maplibre-gl-vector, maplibre-gl-components, and @maplibre/maplibre-gl-style-spec respectively — none of those are bumped here (the style-spec version bump is a transitive pull-in from maplibre-gl that already matched @geolibre/core's existing pin, per the package-lock diff). No new external tile/map hosts requiring CSP updates, no new MapLibre control CSS needing scoped overrides, no new user-facing strings. Verified against the current file contents, not just the PR description.

The rest of the migration (namespace-import rewrite across ~30 files, the dynamic-style-property.ts/GeoAgentSyncableTools typing fixes, the geolocateControlFactory indirection, the third-party default-import shim with its build-fails-if-stale safety net and Vite/Node parity test) is well-reasoned and consistent with the stated verification.

# Conflicts:
#	package-lock.json
#	packages/map/src/layer-sync.ts
#	packages/map/src/map-controller.ts
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Only the one file. That's the full scope of what I found worth flagging. Here's the final review.

Code review

Bugs

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts now contains an embedded NUL byte, making git treat it as a binary file (the diff shows Binary files a/... and b/... differ, and changed-files.txt reports +0/-0 for it, even though the intended change is just the same one-line maplibregl* as maplibregl import-type swap applied to the other 29 files). Confirmed via ripgrep's binary-file detection (NUL byte at ~offset 4655) and independently via .gitattributes' text=auto rule (git's own NUL-based binary heuristic). This looks like stray corruption from the codemod/tool used to rewrite the import across the repo, and it's isolated to this one file. High confidence this is unintentional and worth fixing before merge — GitHub's API also refused an inline comment on this file since a binary diff has no line mapping, so flagging it here instead. Recommend re-generating this file's diff as plain text (e.g., reapply just the intended import change on a clean checkout).

Security

  • None found.

Performance

  • None found; the worker-loading fix (maplibre-worker.ts + ?worker&url) is itself a real perf/correctness fix (previously-hanging worker request), and is well-documented and verified in the PR description.

Quality

  • Low confidence, minor: the DEFAULT_IMPORT regex in maplibre-default-import-shim.ts/tests/hooks/maplibre-default-import-shim.mjs (/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s*(["'])maplibre-gl\2/g) only matches a bare default import (import X from "maplibre-gl") and wouldn't rewrite a combined default+named import (import X, { Y } from "maplibre-gl"). Not an issue today (both shimmed packages were manually verified to work, and the build/test hooks fail loudly rather than silently if a package's bundle shape changes), but worth keeping in mind if either dependency's publish format changes on a future bump.
  • The rest of the ~30-file maplibregl* as maplibregl migration, the dynamic-style-property.ts / geolocateControlFactory indirections, and the shim-parity test are all consistent, well-documented, and I found no leftover default imports of maplibre-gl or jspdf anywhere in the repo.

CLAUDE.md

  • No violations found. The @maplibre/maplibre-gl-style-spec bump implied by this upgrade (transitively deduplicating to the version packages/core already pins) is covered by the existing propertySpecFor guideline via the full frontend test suite, which the PR description confirms was run and passed in full (4236/4236). No stale maplibre-gl v5 references found in docs/.

giswqs added 2 commits August 4, 2026 00:45
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.
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Binary-diff files can't take inline line comments since there's no diff hunk to anchor to. I'll fold that finding into the summary instead.

Code review

Bugs

  • None found. The v5→v6 migration is unusually thorough: every place that previously called setPaintProperty/setLayoutProperty/getPaintProperty/getLayoutProperty with a computed property name (Object.entries(spec.paint/layout), `${prop}-transition`, generic call sites) has been routed through the new dynamic-style-property.ts wrapper, while call sites using literal property names were correctly left untouched. The GeoAgentSyncableTools structural type sidesteps MapLibre's narrowed generics by design, so it didn't need the wrapper either — checked and consistent. Confidence: high.

Security

  • None found in the changed files.

Performance

  • None found. The Vite shim's transform hook is gated on id.includes(.../node_modules/<pkg>/) and a cheap code.includes("maplibre-gl") check before running the regex, so it doesn't add meaningful overhead to unrelated modules. Confidence: high.

Quality

  • apps/geolibre-desktop/src/hooks/useCommandBridge.ts shows up in the diff as "Binary files a/... and b/... differ" with 0/0 line changes, even though the current file content is normal, readable TypeScript with only the expected maplibregl* as maplibregl change. That strongly suggests a stray non-text byte (BOM, non-breaking space, or a line-ending anomaly) crept into the file. It's low severity, but it means this file's real diff is currently invisible to reviewers (and to future git blame/git log -p), so it's worth confirming nothing beyond the import line actually changed before merging. Confidence: medium.
  • The maplibreDefaultImportShim Vite plugin's buildEnd hook errors the build if either shimmed package (@esri/maplibre-arcgis, @geoman-io/maplibre-geoman-free) wasn't rewritten during that build. I checked usePlugins.ts and both plugins are registered unconditionally (no build-variant/feature-flag gating), so this should hold for all current build targets (web, desktop, embed) — but it's a coupling worth remembering if a future build variant ever excludes one of these plugins to shrink bundle size. Confidence: low (design looks intentional and self-documenting, just flagging the dependency).

CLAUDE.md

  • No violations found. The PR correctly avoids editing node_modules, keeps the new dynamic-style-property escape hatch in one documented module rather than scattering casts, and the third-party shim is self-limiting (fails the build when a shimmed package should be removed) rather than a silent, permanent workaround.

Overall this is a well-scoped, carefully-documented dependency bump with good test coverage for the trickiest parts (the shim parity test, the worker-URL fix, the GeolocateControl factory indirection). The one thing worth double-checking before merge is the binary-diff anomaly in useCommandBridge.ts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant