doxa-maps/library/maps/rtl-text.js
doxa-maps-88655c70 c959ed6323
Some checks are pending
build-gate / build (push) Waiting to run
Load the RTL text plugin from one place, and in the composite too
The cap-4 composite bakes research-dashboard's .vue directly, so it never runs
that bundle's entry file and has to repeat everything the entry does. It
repeated Pinia, i18n and the colour strategies. It did not repeat this one.

Measured in the shipped bundles:
  research-dashboard.js ..... 1 occurrence of mapbox-gl-rtl-text
  doxa-research-map.js ...... 1
  dashboard-plus-grid.js .... 0

So Arabic, Hebrew and Persian labels rendered reversed inside the cap-5 shell
and correctly standalone. Not cosmetic here: Arab World has 99 rows and
Persian-Median 49, and a reversed label is a wrong NAME for exactly the groups
this map is about. It failed silently -- no error, no warning, just mirrored
text that reads as a font problem to anyone who does not read the script.

Fixed as a shared library function rather than a fourth hand-copy, which is
what the composite-registration card asks for: one definition, two callers,
nothing to drift. Idempotent by construction, since Mapbox throws on a second
setRTLTextPlugin in a session and that throw is swallowed.

Four of the five entry side effects fail silently; only provide('profileModules')
throws, and that is the one safe to omit because a baked composite does not use
ProfileLoader. So "it renders" is no evidence the entry's work was done.

Found by the map-beauty agent, which I verified independently before acting
rather than taking the bundle counts on report.
2026-09-22 19:55:58 +00:00

50 lines
2.6 KiB
JavaScript

/**
* rtl-text.js — load Mapbox's RTL text plugin ONCE per page.
*
* WHY THIS IS A SHARED FUNCTION AND NOT A COPY-PASTE BLOCK. Every bundle entry that boots a map
* needs this, and the block was duplicated into each `index.js` by hand. A baked cap-4 composite
* imports its child's `.vue` directly and therefore never runs the child's entry, so it has to
* re-declare everything the entry does — and `dashboard-plus-grid` re-declared Pinia, i18n and the
* colour strategies but NOT this one.
*
* MEASURED in the shipped bundles 2026-09-22:
* research-dashboard.js ....... 1 occurrence of mapbox-gl-rtl-text
* doxa-research-map.js ........ 1
* dashboard-plus-grid.js ...... 0 <- the composite
* So Arabic, Hebrew and Persian labels render REVERSED inside the cap-5 shell and correctly
* standalone. That is not cosmetic for this product: Arab World (99 rows) and Persian-Median
* (49 rows) are in scope, and a reversed label is a WRONG NAME for exactly the groups the map is
* about. It also fails silently — no error, no console warning, just mirrored text that looks like
* a font problem to anyone who does not read the script.
*
* ★ THE GENERAL RULE THIS EXISTS TO SERVE: a composite that BAKES a child's `.vue` inherits nothing
* from that child's entry file; a composite that COMPOSES by tag + bundle URL boots the child
* through its own entry and inherits everything. `stack-demo` and `research-map-plus-grid` compose,
* which is why they were never affected. Only `dashboard-plus-grid` bakes.
*
* Four of the five entry side effects fail SILENTLY (Pinia, i18n, colour strategies, this one); only
* `provide('profileModules')` throws, and it is the one that is safe to omit — structurally, because
* a baked composite does not use ProfileLoader.vue. So "it renders" is no evidence that an entry's
* work was done.
*
* Idempotent by construction: Mapbox throws if the plugin is set twice in a session, and that throw
* is swallowed here, so calling this from several entries on one page is safe.
*/
const RTL_PLUGIN_URL =
'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.3.0/mapbox-gl-rtl-text.js'
export function installRtlTextPlugin() {
if (typeof window === 'undefined' || !window.mapboxgl?.setRTLTextPlugin) return false
try {
// `true` = LAZY: fetch the plugin only when RTL text is actually encountered, so a map with no
// RTL labels pays nothing.
window.mapboxgl.setRTLTextPlugin(RTL_PLUGIN_URL, null, true)
return true
} catch (_) {
// Already set in this session. Expected whenever two entries run on one page — not an error.
return false
}
}
export default installRtlTextPlugin