Skip to content

Web worker support

Cloudpack provides built-in support for web workers with import maps, allowing you to use modern ES modules and the same bundled dependencies in your worker code as in your main application.

Overview

Web workers in Cloudpack work by automatically shimming import maps and providing the necessary polyfills to enable ES module imports within worker contexts. This allows you to:

  • Use the same bundled packages in workers as in your main application
  • Import modules directly in worker code
  • Maintain consistent dependency versions across main thread and worker threads
  • Leverage Cloudpack's bundling and caching for worker scripts

Enabling web worker support

Web worker support is enabled by default through the enableModuleWorkers feature flag:

json
{
  "$schema": "https://unpkg.com/@ms-cloudpack/cli/schema/AppConfig.json",
  "features": {
    "enableModuleWorkers": true
  }
}

How it works

When a worker script is requested, Cloudpack automatically:

  1. Detects worker files - Identifies when a bundled file is intended to be used as a Web Worker
  2. Injects import map shim - Adds the es-module-shims library to enable import map support
  3. Provides window polyfills - Shims window, and process.browser for compatibility
  4. Handles messages - Sets up temporary message handlers to queue messages during worker initialization as it loads asynchronously
  5. Loads the worker module - Uses importShim() to load your worker code with full import map support

Basic usage

Worker code example

You can see an example in Cloudpack's onedrive-mock app.

Package configuration

For packages that contain Web Workers, you may need specific bundler configuration:

json
{
  "packageSettings": [
    {
      "match": "my-worker-package",
      "bundler": "rspack"
    }
  ]
}

Note: Currently, only webpack and rspack bundlers fully support worker detection through the isWorker flag in bundle results.

Advanced Features

Shared Dependencies

Workers automatically have access to the same import map as your main application, meaning:

typescript
// Both main thread and worker can import the same version
import { library } from 'shared-dependency'

Window object shimming

Cloudpack provides window object compatibility:

typescript
// These all work in workers
console.log(typeof window !== 'undefined') // true
console.log(process.browser) // true

Define flags

Worker contexts automatically receive the same define flags as your main application:

json
{
  "define": {
    "FEATURE_FLAGS.enableNewFeature": true,
    "API_ENDPOINT": "https://api.example.com"
  }
}
typescript
// Available in worker code
if (FEATURE_FLAGS.enableNewFeature) {
  // Use new feature
}

Message queue handling

Cloudpack handles the timing issue where messages might be sent before the worker is fully initialized:

typescript
// Messages sent immediately after worker creation are queued
const worker = new Worker('/worker')
worker.postMessage('immediate message') // This won't be lost

// The worker's initialization code handles this automatically

Troubleshooting

Worker not loading

  1. Check the enableModuleWorkers is enabled - Ensure this feature is enabled in your configuration
  2. Verify bundler support - Use webpack or rspack for worker support
  3. Check console errors - Look for import or initialization errors
  4. Debug worker script - Use edge://inspect/#workers in Edge or chrome://inspect/#workers in Chrome to debug worker scripts

Worker hangs or never executes

When your package shares code between worker and main thread contexts, you may encounter issues with webpack's chunk splitting optimization. Web workers cannot load additional chunks at runtime, which can cause the worker to hang while waiting for shared chunks to load.

Symptoms:

  • Worker code never executes
  • No error messages, but worker remains idle
  • Network tab shows worker script loading but not executing
  • Worker shows up in the browser's DevTools but does not respond

Solutions:

Disable chunk splitting using bundlerOptions:

json
{
  "packageSettings": [
    {
      "match": "my-worker-package",
      "bundler": "rspack",
      "bundlerOptions": {
        "optimization": {
          "splitChunks": false
        }
      }
    }
  ]
}

Alternative approaches:

  • Use dynamic imports in workers: const { sharedCode } = await import('../shared.js');
  • Move shared utilities to separate packages that can be imported independently by both contexts

Worker fails with browser API errors

If your worker or its dependencies attempt to use browser-only APIs (like DOMParser, document properties), you may need to configure the bundler target for web worker compatibility. This setting is only available for webpack and rspack bundlers.

Symptoms:

  • ReferenceError: DOMParser is not defined
  • ReferenceError: document is not defined
  • Other browser API not available errors in worker context

Solution:

Configure the bundler target to webworker for packages that have browser API dependencies:

json
{
  "packageSettings": [
    {
      "match": "my-worker-package",
      "bundler": "rspack",
      "bundlerOptions": {
        "target": "webworker"
      }
    }
  ]
}

This setting is also needed for packages that are dependencies of worker packages and contain browser-specific code.

Note: The webworker target tells the bundler to exclude or polyfill browser APIs that are not available in the worker context, preventing runtime errors.

CSS loader

Web workers cannot access the DOM, which causes issues when packages attempt to import CSS files. This is particularly problematic with the ori bundler, which does not properly handle CSS imports in worker contexts.

Symptoms:

  • Error occurs when loading CSS files like import 'styles.css'
  • Uncaught ReferenceError: document is not defined at styles.css in worker context
  • Worker fails to initialize when dependencies include CSS imports

General workarounds:

  • Use rspack, webpack, or rollup as the bundler, which handle CSS imports in workers more gracefully