From b135a40064c820a408697c685f7cecfbf34d627a Mon Sep 17 00:00:00 2001 From: codecalm Date: Sun, 4 Oct 2026 17:43:33 +0200 Subject: [PATCH] Move to MapLibre GL 6, loaded as an ES module - 5.x has a critical XSS advisory (GHSA-jrc7-96c5-q579), fixed only in 6.4.1+ --- core/libs.json | 5 +- core/package.json | 2 +- docs/content/ui/components/map.mdx | 34 ++++++++--- docs/layouts/DocsLayout.astro | 6 +- pnpm-lock.yaml | 61 ++++++++----------- screenshots/layouts/ScreenshotAppLayout.astro | 5 +- screenshots/layouts/ScreenshotLayout.astro | 5 +- shared/layouts/BaseLayout.astro | 7 ++- 8 files changed, 73 insertions(+), 52 deletions(-) diff --git a/core/libs.json b/core/libs.json index 77ef71cb1..5d0e423d9 100644 --- a/core/libs.json +++ b/core/libs.json @@ -57,8 +57,9 @@ }, "maplibre-gl": { "npm": "maplibre-gl", - "js": ["dist/maplibre-gl.js"], - "css": ["dist/maplibre-gl.css"] + "module": { "file": "dist/maplibre-gl.mjs", "global": "maplibregl" }, + "css": ["dist/maplibre-gl.css"], + "extra": ["dist/maplibre-gl.mjs", "dist/maplibre-gl-shared.mjs", "dist/maplibre-gl-worker.mjs"] }, "jsvectormap": { "npm": "jsvectormap", diff --git a/core/package.json b/core/package.json index 7b5aece3d..d4945725e 100644 --- a/core/package.json +++ b/core/package.json @@ -276,7 +276,7 @@ "jsvectormap": "^1.7.0", "list.js": "^2.3.1", "litepicker": "^2.0.12", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.11.2", "nouislider": "^15.8.1", "playwright": "1.62.1", "plyr": "^3.8.4", diff --git a/docs/content/ui/components/map.mdx b/docs/content/ui/components/map.mdx index 609d1bf27..b40d110eb 100644 --- a/docs/content/ui/components/map.mdx +++ b/docs/content/ui/components/map.mdx @@ -48,20 +48,38 @@ The demos load their map tiles from [OpenFreeMap](https://openfreemap.org/). It ## Installation -Install MapLibre GL JS with npm. Tabler uses version 5, the last one that ships a classic script file: - -```bash -npm install maplibre-gl@5 -``` - -Or include it from a CDN. You need two files: the stylesheet and the library. +MapLibre GL JS is published as an ES module. Tabler uses version 6. Load the stylesheet, then import the library in a module script: -`} +`} /> +A module script runs before the `DOMContentLoaded` event, so the code examples on this page can use the `maplibregl` global. The library loads two more files from the same folder, `maplibre-gl-shared.mjs` and `maplibre-gl-worker.mjs`, so keep the three together when you host them yourself. + +With a bundler, install the package and import it in your code: + +```bash +npm install maplibre-gl +``` + +```js +import * as maplibregl from 'maplibre-gl' +import 'maplibre-gl/dist/maplibre-gl.css' +``` + + + Older versions, including all of version 5, have a known security issue in the code that cleans the attribution text. + + +Browsers do not load ES modules from a page opened straight from the disk. Use a local server to see the maps while you work. + Tabler styles the map controls, popups and attribution to match the interface, in light and dark mode. These styles are in `tabler-vendors.css`, so load it after the MapLibre stylesheet: ); const libUrl = (lib: Lib, file: string) => isExternal(file) @@ -151,6 +151,9 @@ const libUrl = (lib: Lib, file: string) => const docsLibCss = libEntries .filter(([name]) => docsLibs.includes(name)) .flatMap(([, lib]) => (lib.css ?? []).map((file) => libUrl(lib, file))); +const docsLibModules = libEntries + .filter(([name]) => docsLibs.includes(name)) + .flatMap(([, lib]) => (lib.module ? [`import * as lib from '${libUrl(lib, lib.module.file)}'; window.${lib.module.global} = lib`] : [])); const docsLibJs = libEntries .filter(([name]) => docsLibs.includes(name)) .flatMap(([, lib]) => (lib.js ?? []).map((file) => libUrl(lib, file))); @@ -517,6 +520,7 @@ const relatedPages = await Promise.all( ) } + {docsLibModules.map((code) => ${code}`} />)} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24aa05a44..ae82392ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,8 +199,8 @@ importers: specifier: ^2.0.12 version: 2.0.12 maplibre-gl: - specifier: ^5.24.0 - version: 5.24.0 + specifier: ^6.11.2 + version: 6.11.2 nouislider: specifier: ^15.8.1 version: 15.8.1 @@ -1289,24 +1289,17 @@ packages: '@mapbox/tiny-sdf@2.2.0': resolution: {integrity: sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==} - '@mapbox/unitbezier@0.0.1': - resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} - '@mapbox/unitbezier@1.0.0': resolution: {integrity: sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==} - '@mapbox/vector-tile@2.0.5': - resolution: {integrity: sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==} - - '@mapbox/whoots-js@3.1.0': - resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} - engines: {node: '>=6.0.0'} + '@mapbox/vector-tile@3.0.0': + resolution: {integrity: sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==} '@maplibre/geojson-vt@6.1.1': resolution: {integrity: sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==} - '@maplibre/maplibre-gl-style-spec@24.10.0': - resolution: {integrity: sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==} + '@maplibre/maplibre-gl-style-spec@26.4.4': + resolution: {integrity: sha512-hG4RZR4PBd/+6A3ODdZ3AnQRF106ZQk1y3bhwDg8bH/A4Oz2tnRUJVVWs9BZa6gXZ1d3UKe89C+Mm+olGBELvA==} hasBin: true '@maplibre/mlt@1.3.0': @@ -2210,6 +2203,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + bidi-js@1.1.0: + resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -3498,8 +3494,8 @@ packages: make-synchronized@0.8.0: resolution: {integrity: sha512-DZu4lwc0ffoFz581BSQa/BJl+1ZqIkoRQ+VejMlH0VrP4E86StAODnZujZ4sepumQj8rcP7wUnUBGM8Gu+zKUA==} - maplibre-gl@5.24.0: - resolution: {integrity: sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==} + maplibre-gl@6.11.2: + resolution: {integrity: sha512-Xh06pxoipjX/Ad1sUPGhiNh9go9naCNGZZ1IJm3sIeqK1kYGuMaMDSTIGDQ2igOLfVyukJU7kvEOA089VO7Z7g==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} markdown-extensions@2.0.0: @@ -3964,10 +3960,6 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pbf@4.0.2: - resolution: {integrity: sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==} - hasBin: true - pbf@5.1.2: resolution: {integrity: sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==} hasBin: true @@ -6153,23 +6145,19 @@ snapshots: '@mapbox/tiny-sdf@2.2.0': {} - '@mapbox/unitbezier@0.0.1': {} - '@mapbox/unitbezier@1.0.0': {} - '@mapbox/vector-tile@2.0.5': + '@mapbox/vector-tile@3.0.0': dependencies: '@mapbox/point-geometry': 1.1.0 '@types/geojson': 7946.0.16 - pbf: 4.0.2 - - '@mapbox/whoots-js@3.1.0': {} + pbf: 5.1.2 '@maplibre/geojson-vt@6.1.1': dependencies: kdbush: 4.1.0 - '@maplibre/maplibre-gl-style-spec@24.10.0': + '@maplibre/maplibre-gl-style-spec@26.4.4': dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/unitbezier': 1.0.0 @@ -7133,6 +7121,10 @@ snapshots: baseline-browser-mapping@2.11.6: {} + bidi-js@1.1.0: + dependencies: + require-from-string: 2.0.2 + binary-extensions@2.3.0: {} bindings@1.5.0: @@ -8455,24 +8447,23 @@ snapshots: make-synchronized@0.8.0: {} - maplibre-gl@5.24.0: + maplibre-gl@6.11.2: dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/point-geometry': 1.1.0 '@mapbox/tiny-sdf': 2.2.0 - '@mapbox/unitbezier': 0.0.1 - '@mapbox/vector-tile': 2.0.5 - '@mapbox/whoots-js': 3.1.0 + '@mapbox/unitbezier': 1.0.0 + '@mapbox/vector-tile': 3.0.0 '@maplibre/geojson-vt': 6.1.1 - '@maplibre/maplibre-gl-style-spec': 24.10.0 + '@maplibre/maplibre-gl-style-spec': 26.4.4 '@maplibre/mlt': 1.3.0 '@maplibre/vt-pbf': 4.3.2 '@types/geojson': 7946.0.16 + bidi-js: 1.1.0 earcut: 3.2.4 gl-matrix: 3.4.4 kdbush: 4.1.0 murmurhash-js: 1.0.0 - pbf: 4.0.2 + pbf: 5.1.2 potpack: 2.1.0 quickselect: 3.0.0 tinyqueue: 3.0.0 @@ -9227,10 +9218,6 @@ snapshots: pathe@2.0.3: {} - pbf@4.0.2: - dependencies: - resolve-protobuf-schema: 2.1.0 - pbf@5.1.2: dependencies: resolve-protobuf-schema: 2.1.0 diff --git a/screenshots/layouts/ScreenshotAppLayout.astro b/screenshots/layouts/ScreenshotAppLayout.astro index 0792e4847..f671dfc55 100644 --- a/screenshots/layouts/ScreenshotAppLayout.astro +++ b/screenshots/layouts/ScreenshotAppLayout.astro @@ -73,11 +73,13 @@ const themeAttributes = themeDataAttributes({ ...theme, 'navbar-position': navba // whatever core's own watcher last emitted, which is unminified. const min = import.meta.env.DEV ? '' : '.min' -type Lib = { npm?: string; js?: string[]; css?: string[] } +type Lib = { npm?: string; js?: string[]; css?: string[]; module?: { file: string; global: string } } const pageLibEntries = Object.entries(libs as Record).filter(([name]) => pageLibs.includes(name)) const libHref = (lib: Lib, file: string) => `/dist/libs/${lib.npm}/${file}` const libCssFiles = pageLibEntries.flatMap(([, lib]) => (lib.css ?? []).map((file) => libHref(lib, file))) const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file) => libHref(lib, file))) +// ES-module-only libraries: imported in a module script and exposed as a global (see BaseLayout) +const libModules = pageLibEntries.flatMap(([, lib]) => (lib.module ? [`import * as lib from '${libHref(lib, lib.module.file)}'; window.${lib.module.global} = lib`] : [])) --- @@ -177,6 +179,7 @@ const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file) {libJsFiles.map((src) => `} />)} diff --git a/screenshots/layouts/ScreenshotLayout.astro b/screenshots/layouts/ScreenshotLayout.astro index bcf3e618e..1dbcef68b 100644 --- a/screenshots/layouts/ScreenshotLayout.astro +++ b/screenshots/layouts/ScreenshotLayout.astro @@ -45,11 +45,13 @@ const rtlSuffix = rtl ? '.rtl' : '' // build instead of hardcoding one variant. const min = import.meta.env.DEV ? '' : '.min' -type Lib = { npm?: string; js?: string[]; css?: string[] } +type Lib = { npm?: string; js?: string[]; css?: string[]; module?: { file: string; global: string } } const pageLibEntries = Object.entries(libs as Record).filter(([name]) => pageLibs.includes(name)) const libHref = (lib: Lib, file: string) => `/dist/libs/${lib.npm}/${file}` const libCssFiles = pageLibEntries.flatMap(([, lib]) => (lib.css ?? []).map((file) => libHref(lib, file))) const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file) => libHref(lib, file))) +// ES-module-only libraries: imported in a module script and exposed as a global (see BaseLayout) +const libModules = pageLibEntries.flatMap(([, lib]) => (lib.module ? [`import * as lib from '${libHref(lib, lib.module.file)}'; window.${lib.module.global} = lib`] : [])) --- @@ -168,6 +170,7 @@ const libJsFiles = pageLibEntries.flatMap(([, lib]) => (lib.js ?? []).map((file) {libJsFiles.map((src) => `} />)}