You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by jl...@apache.org on 2026/10/16 20:15:33 UTC

(superset) 01/01: fix(deck.gl): set maplibre-gl 6's worker URL so vector basemaps load

This is an automated email from the ASF dual-hosted git repository.

sadpandajoe pushed a commit to branch ultraset-deckgl-maplibre-worker-url-unset
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 2fb69bebb0d3c1e6d2eccf6435766d8bd9a7a4c0
Author: Joe Li <47...@users.noreply.github.com>
AuthorDate: Fri Oct 16 20:15:19 2026 +0000

    fix(deck.gl): set maplibre-gl 6's worker URL so vector basemaps load
    
    maplibre-gl 6 derives its worker URL from `import.meta.url`, which webpack
    rewrites to a build-time file:// path, so the worker URL resolves to "" and
    no vector tiles are ever requested. #44042 fixed this for
    plugin-chart-point-cluster-map only; preset-chart-deckgl never called
    setWorkerUrl(), leaving Carto/OpenFreeMap basemaps blank on deck.gl charts.
    
    Point maplibre-gl at the worker asset CopyPlugin already emits, before the
    first MapLibre map mounts, mirroring the point-cluster-map fix.
    
    Fixes #45373
    
    Co-Authored-By: Claude Opus 5.5 <no...@anthropic.com>
---
 .../src/CategoricalDeckGLContainer.test.tsx        |  6 +++++
 .../src/DeckGLContainer.test.tsx                   | 28 ++++++++++++++++++++++
 .../preset-chart-deckgl/src/DeckGLContainer.tsx    | 11 +++++++++
 .../preset-chart-deckgl/src/layers/Arc/Arc.test.ts |  6 +++++
 .../plugins/preset-chart-deckgl/types/global.d.ts  | 23 ++++++++++++++++++
 5 files changed, 74 insertions(+)

diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.test.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.test.tsx
index 199f8a9be5a..f27c9106ce7 100644
--- a/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.test.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.test.tsx
@@ -21,6 +21,12 @@ import { getCategories } from './CategoricalDeckGLContainer';
 import { addColorToFeatures } from './utils/addColor';
 import { COLOR_SCHEME_TYPES } from './utilities/utils';
 
+// maplibre-gl 6 is ESM-only, so jest can't resolve it; DeckGLContainer.tsx
+// calls setWorkerUrl() on it at import time.
+jest.mock('maplibre-gl', () => ({ setWorkerUrl: jest.fn() }), {
+  virtual: true,
+});
+
 // Record every (label, sliceId) pair the categorical color scale is asked to
 // resolve, so we can assert the legend and point-color paths key the scale on
 // the same slice id.
diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.test.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.test.tsx
index ddc66e81645..ee6951f465e 100644
--- a/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.test.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.test.tsx
@@ -27,8 +27,21 @@ import {
   OSM_TILE_STYLE_URL,
 } from '@superset-ui/core/utils/mapStyles';
 import mapboxgl from 'mapbox-gl';
+import * as maplibregl from 'maplibre-gl';
 import { DeckGLContainer, DeckGLContainerHandle } from './DeckGLContainer';
 
+// maplibre-gl 6 is an ESM-only package with no "require"/"default" export
+// condition, so jest's (CJS-based) resolver can't locate the real module to
+// mock over it by name; { virtual: true } skips that resolution step. The
+// jest.fn() is created inline because DeckGLContainer.tsx calls
+// maplibregl.setWorkerUrl() at import time, before any hoisted `const`
+// declared here would be initialized.
+jest.mock(
+  'maplibre-gl',
+  () => ({ __esModule: true, setWorkerUrl: jest.fn() }),
+  { virtual: true },
+);
+
 jest.mock('react-map-gl/maplibre', () => ({
   Map: ({
     children,
@@ -130,6 +143,12 @@ const renderContainer = (
     </ThemeProvider>,
   );
 
+// Snapshot the import-time calls before afterEach's jest.clearAllMocks()
+// wipes them.
+const setWorkerUrlCallsAtImport = [
+  ...jest.mocked(maplibregl.setWorkerUrl).mock.calls,
+];
+
 afterEach(() => {
   jest.useRealTimers();
   jest.clearAllMocks();
@@ -139,6 +158,15 @@ afterEach(() => {
   });
 });
 
+test('DeckGLContainer points maplibre-gl at the CopyPlugin-emitted worker asset before any map can mount', () => {
+  // maplibre-gl 6 derives its worker URL from `import.meta.url`, which
+  // webpack rewrites to a build-time path that fails maplibre's `^https?:`
+  // check, so vector tiles never load unless setWorkerUrl() is called first.
+  expect(setWorkerUrlCallsAtImport).toEqual([
+    ['/static/assets/maplibre-gl-worker.mjs'],
+  ]);
+});
+
 test('DeckGLContainer converts OSM raster tile templates into MapLibre style objects', () => {
   renderContainer({ mapProvider: 'maplibre', mapStyle: OSM_TILE_STYLE_URL });
 
diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
index e49d547ea77..0bfcc85f1eb 100644
--- a/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
@@ -31,6 +31,7 @@ import { isEqual } from 'lodash-es';
 import { Map as MapLibreMap } from 'react-map-gl/maplibre';
 import { Map as MapboxMap } from 'react-map-gl/mapbox';
 import mapboxgl from 'mapbox-gl';
+import * as maplibregl from 'maplibre-gl';
 import type { Layer } from '@deck.gl/core';
 import { JsonObject, JsonValue, usePrevious } from '@superset-ui/core';
 import {
@@ -46,6 +47,16 @@ import Tooltip, { TooltipProps } from './components/Tooltip';
 import 'maplibre-gl/dist/maplibre-gl.css';
 import { Viewport } from './utils/fitViewport';
 
+// maplibre-gl 6's ESM-only build derives its worker URL from
+// `import.meta.url`, which our (non-ESM-output) webpack bundle rewrites to
+// a build-time path that doesn't match maplibre's `^https?:` check, so
+// `WORKER_URL` silently resolves to `''` and vector tiles never load. Point
+// it at the copy CopyPlugin emits alongside the rest of our static assets
+// (see webpack.config.js), using the runtime-configurable public path so
+// this also works behind an APPLICATION_ROOT prefix. This must run before
+// the first MapLibre map mounts.
+maplibregl.setWorkerUrl(`${__webpack_public_path__}maplibre-gl-worker.mjs`);
+
 const TICK = 250; // milliseconds
 
 const DEFAULT_MAP_STYLE =
diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.test.ts
index ca369d48bce..8a4331aaa81 100644
--- a/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.test.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.test.ts
@@ -19,6 +19,12 @@
 import { COLOR_SCHEME_TYPES } from '../../utilities/utils';
 import { getLayer } from './Arc';
 
+// maplibre-gl 6 is ESM-only, so jest can't resolve it; DeckGLContainer.tsx
+// (pulled in via factory.tsx) calls setWorkerUrl() on it at import time.
+jest.mock('maplibre-gl', () => ({ setWorkerUrl: jest.fn() }), {
+  virtual: true,
+});
+
 const baseArgs = {
   payload: { data: { features: [] } },
   setTooltip: () => {},
diff --git a/superset-frontend/plugins/preset-chart-deckgl/types/global.d.ts b/superset-frontend/plugins/preset-chart-deckgl/types/global.d.ts
new file mode 100644
index 00000000000..cf9bb08e6d4
--- /dev/null
+++ b/superset-frontend/plugins/preset-chart-deckgl/types/global.d.ts
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// webpack's runtime-configurable public path, mirroring src/globals.d.ts.
+// Used to point maplibre-gl at its CopyPlugin-emitted worker asset (see
+// DeckGLContainer.tsx) with the correct app-root / reverse-proxy prefix applied.
+declare var __webpack_public_path__: string;