diff --git a/.gitignore b/.gitignore index e69485b2..f39dce80 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .docusaurus node_modules/ build/ +static/maplibre/ diff --git a/eslint.config.mjs b/eslint.config.mjs index 1add0b56..ab745cbc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -37,6 +37,6 @@ export default [ }, prettier, { - ignores: ['build/', '.docusaurus/', 'node_modules/'], + ignores: ['build/', '.docusaurus/', 'node_modules/', 'static/maplibre/'], }, ]; diff --git a/package.json b/package.json index 79b50296..bc4649a7 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,12 @@ "private": true, "scripts": { "fetch-og": "node scripts/fetch-og-images.mjs", + "copy-maplibre-worker": "node scripts/copy-maplibre-worker.mjs", "docusaurus": "docusaurus", - "start": "npm run docusaurus start", - "build": "npm run docusaurus build", + "start": "npm run copy-maplibre-worker && npm run docusaurus start", + "build": "npm run copy-maplibre-worker && npm run docusaurus build", "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", + "deploy": "npm run copy-maplibre-worker && docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", diff --git a/scripts/copy-maplibre-worker.mjs b/scripts/copy-maplibre-worker.mjs new file mode 100644 index 00000000..d1209d11 --- /dev/null +++ b/scripts/copy-maplibre-worker.mjs @@ -0,0 +1,22 @@ +// MapLibre GL JS v6 ships ESM-only, and its worker (maplibre-gl-worker.mjs) +// statically imports a sibling chunk (maplibre-gl-shared.mjs). Neither Rspack +// nor webpack's `new URL(..., import.meta.url)` asset handling follows that +// relative import when copying the worker as a build asset, so the sibling +// never lands next to it and the worker fails to load at runtime with an +// opaque error. Copying both files verbatim into static/ (served as-is, +// unprocessed, by Docusaurus) keeps them side by side and importable. +// See src/components/map.js and src/components/buildings-map.js, and the +// MapLibre v5->v6 migration guide's ESM/worker section. +import { copyFileSync, mkdirSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const source = join(__dirname, '..', 'node_modules', 'maplibre-gl', 'dist'); +const destination = join(__dirname, '..', 'static', 'maplibre'); + +mkdirSync(destination, { recursive: true }); + +for (const file of ['maplibre-gl-worker.mjs', 'maplibre-gl-shared.mjs']) { + copyFileSync(join(source, file), join(destination, file)); +} diff --git a/src/components/buildings-map.js b/src/components/buildings-map.js index 2497b684..209b30c0 100644 --- a/src/components/buildings-map.js +++ b/src/components/buildings-map.js @@ -1,6 +1,7 @@ import { useRef, useEffect, useState } from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import useBaseUrl from '@docusaurus/useBaseUrl'; import * as maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { Protocol } from 'pmtiles'; @@ -15,10 +16,17 @@ function GlobalBuildingsMapInner() { const { siteConfig: { customFields }, } = useDocusaurusContext(); + const workerUrl = useBaseUrl('/maplibre/maplibre-gl-worker.mjs'); useEffect(() => { if (map.current) return; // stops map from intializing more than once + // MapLibre GL JS v6 ships ESM-only and can no longer locate its worker + // via import.meta.url inside a bundled chunk, so it must be pointed at + // a statically-served copy explicitly. See scripts/copy-maplibre-worker.mjs + // and the MapLibre v5->v6 migration guide. + maplibregl.setWorkerUrl(workerUrl); + let protocol = new Protocol(); maplibregl.addProtocol('pmtiles', protocol.tile); diff --git a/src/components/map.js b/src/components/map.js index ee3e85e8..8495acd4 100644 --- a/src/components/map.js +++ b/src/components/map.js @@ -1,5 +1,6 @@ import { useRef, useEffect, useState } from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; +import useBaseUrl from '@docusaurus/useBaseUrl'; import * as maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { Protocol } from 'pmtiles'; @@ -12,10 +13,17 @@ function MapInner() { const [lng] = useState(-122.33); const [lat] = useState(47.6); const [zoom] = useState(13); + const workerUrl = useBaseUrl('/maplibre/maplibre-gl-worker.mjs'); useEffect(() => { if (map.current) return; // stops map from intializing more than once + // MapLibre GL JS v6 ships ESM-only and can no longer locate its worker + // via import.meta.url inside a bundled chunk, so it must be pointed at + // a statically-served copy explicitly. See scripts/copy-maplibre-worker.mjs + // and the MapLibre v5->v6 migration guide. + maplibregl.setWorkerUrl(workerUrl); + let protocol = new Protocol(); maplibregl.addProtocol('pmtiles', protocol.tile);