diff --git a/src/index.esm.ts b/src/index.esm.ts index 6fdb7b27933..c83f9e54fe3 100644 --- a/src/index.esm.ts +++ b/src/index.esm.ts @@ -173,7 +173,7 @@ export {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin export {setSdkInfo} from './util/mapbox'; export {addTileProvider} from './source/tile_provider'; export {prewarm, clearPrewarmedResources} from './util/worker_pool_factory'; -export {setWorkerUrl} from './util/worker_class'; +export {setWorkerUrl, setWorkerClass, setWorkerFactory} from './util/worker_class'; export {getWorkerCount, setWorkerCount} from './util/worker_pool'; export {setAccessToken, setBaseApiUrl, setMaxParallelImageRequests, getDracoUrl, setDracoUrl, getMeshoptUrl, setMeshoptUrl, getBuildingGenUrl, setBuildingGenUrl} from './util/config'; diff --git a/src/util/web_worker_esm_npm.ts b/src/util/web_worker_esm_npm.ts index c922fcb1b7c..01316ce51e4 100644 --- a/src/util/web_worker_esm_npm.ts +++ b/src/util/web_worker_esm_npm.ts @@ -5,6 +5,11 @@ import WorkerClass from './worker_class'; // its dependencies into a separate chunk. Also works when self-hosted // same-origin without a bundler. export function createWorker(): Worker { + if (WorkerClass.workerFactory) return WorkerClass.workerFactory(); + + // eslint-disable-next-line new-cap + if (WorkerClass.workerClass != null) return new WorkerClass.workerClass(); + if (WorkerClass.workerUrl) { return new Worker(WorkerClass.workerUrl, {type: 'module'}); } diff --git a/src/util/worker_class.ts b/src/util/worker_class.ts index 046df32d74a..b941c4776a7 100644 --- a/src/util/worker_class.ts +++ b/src/util/worker_class.ts @@ -3,12 +3,14 @@ import type {Class} from '../types/class'; type WorkerState = { workerUrl: string; workerClass: Class | null; + workerFactory: (() => Worker) | null; workerParams?: WorkerOptions; // Internal, test-only: extra options passed to `new Worker()` }; const WorkerClass: WorkerState = { workerUrl: '', workerClass: null, + workerFactory: null, workerParams: undefined, }; @@ -24,4 +26,37 @@ export function setWorkerUrl(url: string) { WorkerClass.workerUrl = url; } +/** + * Sets a constructor used to create the WebWorker. Must be set once, before the + * first `new Map(...)`. Counterpart to `mapboxgl.workerClass` on the default + * bundle, and takes precedence over {@link setWorkerUrl}. + * + * Bundlers that only rewrite the `new Worker(new URL(...), ...)` pattern in the + * application's own sources — Angular's esbuild-based builder, for example — + * never see the equivalent expression inside this package and therefore emit no + * worker chunk for it. Passing a constructor lets the application spell that + * pattern out itself and hand the resulting worker back. + * + * @param {Class} klass A constructor returning a WebWorker running the GL JS worker bundle. + */ +export function setWorkerClass(klass: Class | null) { + WorkerClass.workerClass = klass; +} + +/** + * Sets a function used to create the WebWorker. Must be set once, before the + * first `new Map(...)`. Takes precedence over {@link setWorkerClass} and + * {@link setWorkerUrl}. + * + * Serves the same purpose as {@link setWorkerClass}, in a shape that type-checks: + * an application that has to construct the worker itself can only return an + * instance, and a constructor returning an unrelated object cannot be expressed + * in TypeScript, so every `workerClass` caller ends up asserting the type. + * + * @param {Function} create A function returning a WebWorker running the GL JS worker bundle. + */ +export function setWorkerFactory(create: (() => Worker) | null) { + WorkerClass.workerFactory = create; +} + export default WorkerClass;