Files
TREK/client/src/components/Map/rtlText.ts
T
Maurice 1696162d22 fix(deps): maplibre-gl 6.15.0 past the attribution sanitizer advisory
GHSA-jrc7-96c5-q579 covers every maplibre-gl up to 6.4.0 and has no 5.x
release, so npm audit kept flagging the patched 5.24.0. Moving to 6.15.0
retires the local backport: patch-maplibre.mjs and its postinstall,
predev and prebuild hooks go, along with the Dockerfile copy and its
format-baseline entry. 6.x sanitizes attributions against an allow list.

What the major changes for TREK:
- ESM only, no default export: the engine module imports the namespace
  and hands that out as its default, so every caller stays as it was.
- The worker is no longer inlined, and inside a bundle MapLibre cannot
  find it next to its own module. The engine sets it from Vite's
  ?worker&url build before any map, the Leaflet bridge's included.
- WebGL2 only: the probe asks for webgl2 alone, so a WebGL1-only browser
  gets the raster basemap instead of a GPUInitializationError.
- 6.9 shapes right-to-left text itself and deprecated setRTLTextPlugin;
  the old plugin would replace that and could only run through eval in
  the module worker, so MapLibre no longer registers it. Mapbox does.

The lockfile moves maplibre-gl and its own dependencies only; the
Leaflet bridge already allows ^6.0.0 as its peer.
2026-10-24 22:41:56 +00:00

51 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Arabic, Hebrew, Persian and Urdu labels need two things a GL engine does not do
* on its own: the bidirectional algorithm, and Arabic contextual shaping (a letter
* is drawn differently depending on its neighbours). Without them the engine lays
* the codepoints out one by one, left to right, in isolated forms — so القاهرة
* comes out as ة ر ه ا ق ل ا, unjoined and reversed. Raster basemaps never showed
* this because their labels are drawn server-side, which is why it only surfaced
* once the default basemap became a vector style.
*
* Mapbox GL exposes the fix as an optional plugin and does not load it by
* default. Registering is global to the engine, so it happens once in the engine
* module rather than at each map's construction site. MapLibre needed the same
* plugin up to version 5; since 6.9 it shapes and reorders the text itself, and
* its engine module registers nothing.
*
* The call itself stays with the caller: Mapbox GL 3 takes
* `(url, callback, deferred)` and returns nothing, while MapLibre took
* `(url, lazy)` and returned a promise, so the guard below accepts either rather
* than guessing an arity.
*
* Deliberately no getRTLTextPluginStatus() pre-check: reading it made MapLibre
* build its worker pool, which is an eager side effect for a module that is
* imported for its default export, and throws outright under jsdom. Registering
* twice is reported — by a throw or a rejected promise depending on the engine —
* and both are handled below, so the probe bought nothing.
*/
/**
* Runs a GL engine's RTL-plugin registration, swallowing the ways it can fail.
*
* Callers register lazily, so the payload is only fetched once a map actually
* paints RTL text and a user who never leaves Latin script downloads nothing.
*
* @returns whether the registration call completed without throwing.
*/
export function registerRtlTextPlugin(register: () => void | Promise<unknown>): boolean {
try {
const pending = register()
// MapLibre reports a failed or duplicate registration by rejecting; Mapbox
// routes the same through its callback. Neither is worth surfacing: a map
// with unshaped labels still renders, and an unhandled rejection would not
// give the user anything to act on.
if (pending instanceof Promise) pending.catch(() => {})
return true
} catch {
// Already registered, or the engine refused outright. Registering the plugin
// must never be able to break map creation.
return false
}
}