mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
### What? Adds `experimental.turbopackWorkerAssetPrefix` — a Turbopack counterpart to webpack's [`output.workerPublicPath`](https://webpack.js.org/configuration/output/#outputworkerpublicpath). Opt-in, off by default, fully backward compatible. ```js // next.config.js module.exports = { assetPrefix: 'https://cdn.example.com', experimental: { // Same shape as `assetPrefix`: bare prefix, no trailing slash, no `/_next`. // `/_next/` is appended automatically. `''` → same-origin `/_next/...`. turbopackWorkerAssetPrefix: '', }, } ``` ### Why? When `assetPrefix` points to a cross-origin CDN (common production setup), Turbopack emits Worker URLs under that CDN origin. Browsers reject cross-origin `new Worker(url)` with `SecurityError`, breaking every feature that uses `new Worker(new URL('./worker.ts', import.meta.url))` — image decoders, WebP/GIF renderers, compression, OffscreenCanvas, etc. webpack solved this with `output.workerPublicPath`. Turbopack ignores the `webpack()` callback in `next.config.js`, so the existing same-origin workaround silently breaks when projects switch to Turbopack. Full design doc, alternatives considered, related issues (#88602, #92676, #74621, #29468): **#93044** Repro repo (`pnpm dev` reproduces, `pnpm dev:webpack` works): https://github.com/GuinsooRocky/next-turbopack-worker-cdn-repro ### How? Threads a new optional `turbopack_worker_asset_prefix: Option<RcStr>` from `ExperimentalConfig` → `BrowserChunkingContext` → the JS runtime, where `createWorker` reads it via a new `WORKER_BASE_PATH` global emitted alongside the existing `CHUNK_BASE_PATH`. When unset, behavior is identical to today. Semantics, modeled on `assetPrefix`: - **Bare prefix.** `/_next/` is appended automatically (same as `computed_asset_prefix`); the user supplies just the origin (or empty string for same-origin). - **`undefined` vs `''`.** `undefined` falls back to the regular chunk base path; `''` is a literal empty prefix that resolves to same-origin `/_next/...`. The runtime distinguishes the two by injecting a JS `null` literal vs a quoted string, and uses `WORKER_BASE_PATH ?? CHUNK_BASE_PATH` (not `||`). - **Applies to entrypoint and module chunks.** The Turbopack worker bootstrap rejects cross-origin module chunks (`Refusing to load script from foreign origin`), so the override has to cover both — overriding only the entrypoint would leave the worker unable to load any chunks under a cross-origin `assetPrefix`. Touchpoints: - `packages/next/src/server/config-{shared,schema}.ts` — public option + zod under `experimental` - `crates/next-{api,core}/...` — wiring through to `ClientChunkingContextOptions`; `NextConfig::turbopack_worker_asset_prefix()` resolves to `Some("<prefix>/_next/")` or `None` - `turbopack/crates/turbopack-browser/src/chunking_context.rs` — new field + builder + getter - `turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs` — emit `WORKER_BASE_PATH` declaration as `null` or a quoted string - `turbopack/.../runtime-base.ts` — `createWorker` resolves `workerBasePath` and uses it for both the entrypoint URL and module-chunk URLs delivered via params - `test/e2e/turbopack-worker-asset-prefix/` — e2e fixture using real cross-origin (`localhost` page, `127.0.0.1` `assetPrefix`); intercepts `new Worker()` to assert on the URL the runtime helper resolved. Three cases: no override (cross-origin URL → `SecurityError`), explicit override origin, and `''` (relative `/_next/...`). - `docs/01-app/03-api-reference/08-turbopack.mdx` — option reference - `telemetry/events/version.ts` — `useTurbopackWorkerAssetPrefix: boolean` (set on any explicit value, including empty string) ### Notes - Opening as **Draft** to invite API-shape feedback before going to review. Per the contributing guide, the design discussion is at #93044. - All checklist items the PR template requires for a feature are covered: e2e tests, docs, telemetry, no error-link path applies. cc @sokra Closes #93044 <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <[email protected]>