React SDK 0.2.1 ── MapLibre の worker を同梱し、黙って出ない地図を消す

MapLibre GL JS v6 の worker を同梱して自動登録するようにし、一度も発火していなかった onMapLoaded を直しました。TomTom は MapLibre 6 に揃い、そのぶん Node の ESM でも読めるようになっています。21 パッケージ中 19 が動きました。

MapConductor React SDK の各モジュールを更新しました。npm 上の 21 パッケージのうち 19 が動いています。多くは依存の更新ですが、MapLibre まわりに 2 つ、踏むと原因が分かりにくい不具合の修正が入っています。

版は揃えていません。react-for-maplibrereact-for-openlayers0.2.2react-iconsreact-kml は据え置き、残りが 0.2.1 です。0.2.0 のときのように全部を同じ数字にするのはやめました。中身の変わっていないパッケージの版だけを上げても、利用側には「何かが変わった」としか伝わらないためです。

地図が背景色だけになり、しかもエラーが出ない

react-for-maplibre 0.2.2 の本題です。

MapLibre GL JS v6 は Web Worker を別ファイルで配布し、その URL を自身の import.meta.url から組み立てます。バンドラを通すとこの値はビルド時に畳み込まれ(Rspack なら file:// パス、Vite なら事前バンドル先)、worker の実ファイルはどこにも出力されないので、解決に失敗します。

厄介なのは 失敗しても例外が飛ばない ことです。new Worker("") のようなものが黙って作られ、タイル要求が 1 件も出ないまま止まります。画面に出るのは背景色だけの地図です。ネットワークパネルで .pbf の要求が 0 件なら、この症状だと思ってください。

0.2.2 は、束ねた worker をパッケージに同梱し、最初の地図を生成する時点で自動的に登録します。設定は要りません。 Vite でも webpack / Rspack でも、dev・本番ビルドのいずれでも同じです。

例外は 1 つ、worker を自前の URL から配信する場合(共有 CDN や、すでに出力しているビルド)です。最初の地図を生成する前に setMapLibreWorkerUrl(url) を呼べば、同梱 worker はスキップされ、ダウンロードもされません。

ts
import { setMapLibreWorkerUrl } from '@mapconductor/react-for-maplibre';

setMapLibreWorkerUrl('https://cdn.example.com/maplibre-worker.mjs');

なお Vite の dev サーバーは同梱 worker を変換パイプラインに通すため、/@vite/client の import が注入されファイルサイズも膨らみます(約 478KB → 約 2.8MB)。見た目は不穏ですが動作に影響はなく、本番ビルドでは素のアセットとして出力されます。

worker のコードを Blob URL にして渡す方法は v6 では動きません。worker 自体はエラーなく起動しますが、やはりタイル要求が 1 件も出ません。実ファイルとして配信する必要があります。

onMapLoaded が一度も発火しない

同じく react-for-maplibre 0.2.2 です。

MapLibreProvider.initialize()map.once('load') を await してからコントローラを生成します。つまりコントローラができた時点で load は済んでおり、setupEventListeners が張る on('load') は二度と発火しません。

コンストラクタでの「初期化済みか」の判定がその代わりのはずでしたが、機能していませんでした。loaded()isStyleLoaded() も load 直後は false を返すためです。MapLibre v6 の isStyleLoaded()Style.loaded() を呼び、すべての tileManager の完了まで見にいきます。

0.2.2 で直しました。onMapLoaded を使っていた場合、それまでは一度も呼ばれていません。

js-sdk-core 0.2.1 ── アイコンの種類が多いと Service Worker が落ちる

タイルを配る Service Worker には、マーカーのアイコンをビットマップで渡しています。渡すものを1つずつ集めて最後にまとめて送る作りだったので、キャッシュが持てる数より多くの種類のアイコンを使うと、先に集めたものは送る時点で既に解放されていました。解放済みの ImageBitmap を structured clone に通すと DataCloneError が飛び、Service Worker の登録ごと落ちます。

登録が落ちるとタイルが1枚も来ないので、症状は「地図が出ない」です。しかもアイコンの種類がキャッシュの大きさを超えるまで再現しないので、手元では動いていたものが本番で止まる、という出方をします。

直し方はキャッシュを大きくすることではありません。所有権を分けました ── 送る側は自分のコピーを持ち、送り終えてから自分のぶんだけ解放します。キャッシュが持っているものには触りません。

TomTom が MapLibre を 2 つ抱えなくなった

0.2.0 では react-for-tomtom だけ maplibre-gl を 5.x に据え置いていました。@tomtom-org/maps-sdkmaplibre-gl@^5.24.0 に依存していたためで、6.x に上げると MapLibre が 2 つ入り、TomTom SDK が想定していない map オブジェクトを渡すことになるからです。

TomTom 側が 0.51.5 で maplibre-gl@^6.4.0 に移りました。react-for-tomtom 0.2.1 はこれに合わせて ^6.6.0 にしてあります。react-for-maplibrereact-for-maptiler と同じ MapLibre を共有するようになり、0.2.0 の注記は要らなくなりました。

副産物として、react-for-tomtom は Node の ESM 解決器で読めるようになりました。0.2.0 では読めず、理由は「maplibre-gl/package.json を import attribute 無しに読む」でした。空のプロジェクトに公開済みパッケージを入れて確かめています。

importrequire()
react-for-tomtom 0.2.0nono
react-for-tomtom 0.2.1yesno

require() が通らないのは maplibre-gl v6 が ESM のみで CommonJS の入口を持たないためで、これは react-for-maplibrereact-for-maptiler と同じ理由です。この 3 つは ESM でお使いください。

中身の変わっていない 0.2.1 が 4 つあります

js-sdk-reactreact-geojsonreact-heatmapreact-marker-clustering の 0.2.1 は、JavaScript が 0.2.0 と同一です。 動いたのはパッケージに同梱しているビルド定義だけで、web のビルドはそれを読みません。急いで上げる理由はありませんが、上げても害はありません。

地図 SDK の更新

宣言レンジ内の最新に合わせています。動いたのは次のものです。

依存0.2.0今回
mapbox-gl^3.15.0^3.29.0
maplibre-gl^6.0.0^6.6.0
cesium^1.131.0^1.144.0
ol^10.2.1^10.10.0
@arcgis/core^5.1.16^5.1.20
@tomtom-org/maps-sdk^0.51.0^0.51.5
azure-maps-control^3.7.0^3.7.4
@turf/turf^7.3.5^7.4.0

据え置きは leaflet ^1.9.4、@googlemaps/js-api-loader ^2.1.1、mappls-web-maps ^3.8.1 の 3 つで、いずれも上流に動きがありません。React は peerDependencies が ^18.0.0 || ^19.0.0 のままで、変えたのは開発時に使う版(19.2.7 → 19.2.8)だけです。

パッケージと、それぞれが使う地図 SDK

パッケージバージョン地図 SDKリポジトリ
@mapconductor/js-sdk-core0.2.1js-sdk-core
@mapconductor/js-sdk-react0.2.1js-sdk-react
@mapconductor/react-for-arcgis0.2.1@arcgis/core ^5.1.20react-for-arcgis
@mapconductor/react-for-azuremaps0.2.1azure-maps-control ^3.7.4react-for-azuremaps
@mapconductor/react-for-cesium0.2.1cesium ^1.144.0react-for-cesium
@mapconductor/react-for-googlemaps0.2.1@googlemaps/js-api-loader ^2.1.1react-for-googlemaps
@mapconductor/react-for-here0.2.1CDN (global H)react-for-here
@mapconductor/react-for-leaflet0.2.1leaflet ^1.9.4react-for-leaflet
@mapconductor/react-for-longdo0.2.1CDN (api.longdo.com/map3)react-for-longdo
@mapconductor/react-for-mapbox0.2.1mapbox-gl ^3.29.0react-for-mapbox
@mapconductor/react-for-mapkit0.2.1CDN (cdn.apple-mapkit.com)react-for-mapkit
@mapconductor/react-for-maplibre0.2.2maplibre-gl ^6.6.0react-for-maplibre
@mapconductor/react-for-mappls0.2.1mappls-web-maps ^3.8.1react-for-mappls
@mapconductor/react-for-maptiler0.2.1maplibre-gl ^6.6.0react-for-maptiler
@mapconductor/react-for-openlayers0.2.2ol ^10.10.0react-for-openlayers
@mapconductor/react-for-tomtom0.2.1@tomtom-org/maps-sdk ^0.51.5, maplibre-gl ^6.6.0react-for-tomtom
@mapconductor/react-geojson0.2.1react-geojson
@mapconductor/react-heatmap0.2.1react-heatmap
@mapconductor/react-icons0.2.0react-icons
@mapconductor/react-kml0.2.0react-kml
@mapconductor/react-marker-clustering0.2.1react-marker-clustering

HEREApple MapKit JSLongdo の 3 つが依存に地図 SDK を持たないのは、SDK が npm で配られていないためです。実行時にベンダーの CDN から読み込まれるので、実際に動くバージョンはベンダーが配信しているものになります。

上げ方

bash
npm install @mapconductor/js-sdk-core@latest @mapconductor/js-sdk-react@latest \
  @mapconductor/react-for-maplibre@latest

js-sdk-core も 0.2.1 になっています。上の Service Worker の修正が入っているので、マーカーのアイコンを何種類も使っているなら、これは上げてください。

ESM・CommonJS・SSR の扱いは、上の react-for-tomtom を除いて 0.2.0 から変わっていません。どのパッケージが Node の解決器で読めて読めないかは React SDK 0.2.0 の記事 に一覧があります。各パッケージはそれぞれ独立したリポジトリにあります。github.com/MapConductor をご覧ください。