Skip to content

Expose a worker override from the ESM entry - #13719

Open
tg-freigmbh wants to merge 1 commit into
mapbox:mainfrom
tg-freigmbh:esm-set-worker-class
Open

Expose a worker override from the ESM entry#13719
tg-freigmbh wants to merge 1 commit into
mapbox:mainfrom
tg-freigmbh:esm-set-worker-class

Conversation

@tg-freigmbh

Copy link
Copy Markdown
Contributor

This is a bandaid regarding the problems when trying to use mapbox + 'new' angular build + zones.
See issues #13684
and also technical details in #13700

UMD already exposed setWorkerClass, ESM did not.
However due to more bundler/typescript complexity this API would require ugly casts for every valid use case.
So this pr also adds a 'factory' method, which is slighly more bundler friendly.

The default bundle exposes `mapboxgl.workerUrl` and `mapboxgl.workerClass` as
documented accessors. The ESM entry has the counterpart to the first one only,
`setWorkerUrl`; there is no way to hand GL JS a worker the application built
itself. That gap is what makes the ESM entry unusable under some bundlers.

`dist/esm/core.js` spawns its worker with `new Worker(new URL('worker.js',
import.meta.url))`, which relies on the host bundler recognising the pattern and
emitting the worker as its own chunk. Vite and webpack 5 do that for
dependencies. Angular's esbuild-based `@angular/build:application` builder
implements it as a TypeScript transformer, so it only ever runs over the
application's own sources and never over prebuilt JS in node_modules. The
expression then survives verbatim into the output, no worker file is emitted, and
the request resolves against the emitted chunk's URL. Behind an SPA fallback that
answers with index.html and status 200 the worker dies silently: the main thread
keeps running and only tile parsing stops.

`setWorkerUrl` can paper over this if the application copies `dist/esm` out as
static assets, but such copies carry no content hash and go stale on upgrade.

Two shapes are implemented here; happy to drop either one.

`setWorkerClass(klass)` mirrors the existing `workerClass` field that
`web_worker.ts` already honours, so it adds no new concept.

`setWorkerFactory(create)` does the same in a shape that type-checks. An
application that has to construct the worker itself can only return an instance,
and TypeScript cannot express a constructor returning an unrelated object, so
every `workerClass` caller ends up asserting the type — including the workaround
in mapbox#13700:

    mapboxgl.workerClass = class {
      constructor() { return new Worker(new URL(...), {type: 'module'}); }
    } as unknown as new () => Worker;

Precedence in the ESM `createWorker` is factory, then class, then url, then the
existing `import.meta.url` default. If the factory would be welcome on the
default entry as well, that is a `mapboxgl.workerFactory` accessor in
src/index.ts plus the same check in web_worker.ts, and it would remove the
assertion from the mapbox#13700 workaround too.

Refs mapbox#13700, mapbox#13678
@tg-freigmbh
tg-freigmbh requested a review from a team as a code owner August 26, 2026 14:08
@tg-freigmbh
tg-freigmbh requested review from Xdudu and removed request for a team August 26, 2026 14:08
@github-actions

Copy link
Copy Markdown

Hey, @tg-freigmbh 👋 Thanks for your contribution to Mapbox GL JS!

Important: This repository does not accept direct merges. All changes go through our internal review process.

What happens next:

  1. A team member will review your PR here first
  2. If it looks good, they will import it to our internal repository for further review
  3. If approved, changes will be synced back here via our release process

Please respond to any review comments on this PR. For more details, see CONTRIBUTING.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant