Repository navigation
Conversation
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
Build Cache
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URLCommit: 3830695 |
Tests PassedCommit: b732ade |
d412716 to
a855dda
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
|
There are a lot of failing tests. |
|
Thanks — those were real, and the cause is now understood. The failures you saw were from
Web/shared workers are untouched and keep the enclosing availability, which is what unrolls the content-hash cycle this PR is actually about. This one is verified against the reproducer rather than by inference: |
20bd7bd to
9587bef
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
9587bef to
f5d2f34
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
f66aff2 to
819fe75
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
819fe75 to
269164c
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
269164c to
c636a0d
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
83b170b to
c70c815
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
b09667c to
54f8b52
Compare
7b0b674 to
656b569
Compare
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…::Worker Part 1 of the worker-availability-cycle fix (see requirements.md): - Move enum from turbopack-ecmascript to turbopack-core so it can be referenced by . - Add variant. - Handle in all exhaustive matches across core: Display, without_inherit_async, introspect, chunk-group inheritance, and module-graph edge tracing. No behavior change yet — Worker is treated identically to Isolated. The variant is needed for the late WorkerLoaderModule creation that will thread availability info to break the self-referencing worker deadlock (issue #97399). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…on't deadlock
Production builds parked forever at "Creating an optimized production
build ..." at 0% CPU whenever a web worker spawned itself.
`WorkerLoaderModule::chunk_group` hard-coded `AvailabilityInfo::root()`,
so no module was ever "already available" inside a worker chunk group.
A worker that spawns itself therefore kept creating a fresh nested chunk
group, and because the loader's generated code embeds the chunk group's
(content-hashed) chunk paths, that closed an await ring turbo-tasks
cannot detect:
chunk path -> content hash -> chunk content -> worker loader code
-> chunks_data -> chunk_group -> chunk path -> ...
Fix it the way `import()` cycles are already unrolled: build the worker
chunk group with the *enclosing* chunk group's availability info, so a
self-reference sees its own entry as available and stops recursing.
To do that the loader has to be created once the availability info is
known, i.e. during chunking rather than while building the module graph:
- Move `WorkerType` to turbopack-core and add `ChunkingType::Worker`.
- `WorkerAssetReference` now resolves to a new `WorkerEntryModule`
marker (carrying the inner module, worker type and asset context) and
reports `ChunkingType::Worker`. Tracing-only references keep resolving
to the raw module and keep their old parallel chunking type.
- `chunk_group_content_operation` collects worker edges into
`worker_modules`. This runs before the `ModuleOrBatch::None` and
chunkability checks because the marker is deliberately not chunkable,
and reads the module off the edge for the same reason.
- `make_chunk_group` creates the `WorkerLoaderModule` from each marker
via the new `ChunkingContext::worker_loader_chunk_item`, passing
`new_availability_info`.
- The loader gained an `availability_info` field (excluded from `ident()`,
like `AsyncLoaderModule`) and no longer carries the isolated edge to
the worker entry — that lives on the marker now.
Unlike an async loader, the loader must *not* collapse to an empty asset
list when the target is available: `parentImport(id)` works because it
runs in the same runtime, whereas a worker gets a fresh realm and still
needs its evaluate chunk to instantiate the entry. The factories for
already-available modules instead reach the worker at runtime:
- Each browser runtime records the JS chunks whose module factories it
installed, exposed as `__turbopack_get_loaded_chunk_paths__`.
- `createWorker` sends those paths in a new params slot, separate from
the worker's own chunks. Module factories are functions and cannot be
structured-cloned across realms, so the worker re-imports the chunks —
cheap, since the browser has them cached.
- The worker bootstrap loads those chunks *first*, before the evaluate
chunk runs the entry module, and still loads the shared runtime last.
`TURBOPACK_NEXT_CHUNK_URLS` stays exactly the reverse of the
registration order it is popped in.
Content hashing and chunk identity are untouched.
Adds production regression tests for a worker that spawns itself, both
inline and through a separate module file.
Fixes #97399
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…s ident
The previous commit made `WorkerAssetReference` resolve to a non-chunkable
`WorkerEntryModule` marker that is excluded from the enclosing chunk group,
but left the `new Worker(...)` code generation resolving through
`PatternMapping` with `ResolveType::ChunkItem`. That path requires the
resolved module to be a chunkable module with a module id in the current
chunk group, so it rejected the marker:
non-ecmascript placeable asset
... (ecmascript, worker loader) is not placeable in ESM chunks
which broke every worker: the `require()` for the loader was never emitted,
so no worker ever started.
Dynamic imports don't hit this because their codegen never asks
`resolve_reference()` for the id — it looks the loader up by ident via
`ChunkingContext::async_loader_chunk_item_ident`. Add the worker equivalent:
- Add `ResolveType::WorkerLoader` and handle it in
`to_single_pattern_mapping` *before* the chunkable downcast, deriving a
`ModuleLoader` id from the marker's ident.
- Use it from `WorkerAssetReferenceCodeGen`, so the generated code still
emits `__turbopack_require__(loader_id)`.
- Add `WorkerEntryModule::asset_ident_for(inner, worker_type)` and route
*both* the marker's and the late `WorkerLoaderModule`'s `ident()` through
it.
That last part is load-bearing: `ModuleIdStrategy` keys ids by the resolved
`Vc<AssetIdent>`, not by ident content, so an equal-but-separately-computed
ident would miss the lookup and fail with "ModuleId not found for ident".
The marker is the module that appears in the module graph (and is therefore
registered in the global module id map), while the loader is what actually
becomes the chunk item, so the two have to share one memoized ident. This
mirrors `AsyncLoaderModule::asset_ident_for`.
Also document why the worker bootstrap must load the worker's own chunks
last: a worker loader has the same module id in every chunk group but a
different chunk list per group, so loading its own chunks last is what makes
a nested worker pick up the correctly-pruned list.
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Workers still failed at runtime with:
Module .../worker/browser/createWorker.ts ... was instantiated because
it was required from ... (ecmascript, worker loader), but the module
factory is not available.
`WorkerLoaderModule` is created during `make_chunk_group`, i.e. after the
module graph has been built, so its `Module::references()` are never
traversed. The `createWorker` runtime helper it referenced was therefore
never discovered by graph construction: it got no module id and was never
chunked, even though the loader's generated code `require()`s it.
Move that dependency onto the `WorkerEntryModule` marker, which *is* part of
the graph:
- Extract `create_worker_module(asset_context, worker_type)` as a shared
memoized function. The marker references it and the loader delegates to
it, so both resolve to the identical module and therefore the identical
chunk item id.
- `WorkerEntryModule::references()` now declares both the isolated edge to
the worker's own entry module and a parallel reference to the helper.
- `WorkerLoaderModule::references()` is now empty, with a comment
explaining that a module created during chunking cannot contribute graph
edges (declaring them there was silently dead).
Moving the reference alone was not sufficient: `ChunkingType::Worker`
returned `GraphTraversalAction::Exclude`, which skips the node *and its
children*, so the helper still would not have been chunked. Worker edges now
fall through to the same handling as `Parallel`, placing the marker's
referenced modules in the chunk group that also receives the loader.
That fall-through also fixes a related subtlety: the marker is reached over a
non-parallel edge, so it starts its own pre-batch, and because the marker
itself is not chunkable that batch reduces to the *helper* rather than the
marker. The marker therefore has to be read from `edge.module` (not the
node), and the node has to continue into normal chunk-item collection. The
marker's reference to the worker entry stays `ChunkingType::Isolated` and is
still excluded, so the worker's own modules do not leak into the parent
group.
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Two CI failures, both mechanical:
- `runtime-base.ts` had a stray double blank line, failing `prettier-check`.
- Six snapshot tests embed runtime source this branch modifies, so their
expected output was stale.
The four non-worker snapshots (debug-ids/browser, imports/ignore-comments,
runtime/default_dev_runtime, swc_transforms/preset_env) embed
`browser/runtime/base/runtime-base.ts`, and pick up only the new
`loadedJsChunkPaths` set, `registerLoadedJsChunk`, `getLoadedChunkPaths` and
the `browserContextPrototype.G` assignment. No module ids or unrelated
runtime code change.
The two worker snapshots additionally cover `createWorker.ts` and the new
graph/chunking layout, and are worth reading as verification of the previous
two commits: the regenerated output shows the page chunk registering
`createWorker.ts` under the same id the worker loader requires, the worker
loader in a chunk that the page's evaluate chunk lists in `otherChunks`, and
the loader passing the worker entrypoint plus the worker's own chunk list.
That is exactly the wiring the "module factory is not available" failure was
reporting as missing.
Regenerated with:
UPDATE=1 cargo test -p turbopack-tests --test snapshot
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
…eload URLs first Two review comments from #97956: - `ChunkingType::Worker` was mapped onto `ChunkGroupKey::Isolated`, which conflated worker groups with isolated ones. Give workers their own `ChunkGroupKey::Worker` / `ChunkGroup::Worker`, handled in `entries()`, `entries_count()`, both debug-string impls and the key -> group conversion. `WorkerLoaderModule::chunk_group` now builds `ChunkGroup::Worker` for web/shared workers and Node worker threads, so worker groups describe themselves in chunk-group debugging instead of showing up as "Isolated". - Move the preloaded chunk URLs to the front of the worker bootstrap params, matching the order they are loaded in: [PRELOAD_CHUNK_URLS, TURBOPACK_NEXT_CHUNK_URLS, ASSET_SUFFIX, WORKER_CHUNK_BASE_PATH, ...forwarded globals] The Rust-generated bootstrap reads shift accordingly (`params[0]` / `params[1]`, `param(2)` / `param(3)`, forwarded globals from `param(4)`). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
The marker existed only to carry three things across the gap between module graph construction and chunking. Each of them now uses a mechanism that already exists, so the marker is gone and `WorkerAssetReference` resolves straight to the worker's entry module. 1. The loader's module id. Added `ChunkingContext::worker_loader_chunk_item_ident`, mirroring `async_loader_chunk_item_ident`. It and `WorkerLoaderModule::ident()` both route through the same memoized `worker_loader_asset_ident_for`, so the id the `new Worker(...)` codegen looks up and the id the chunk item is registered under still agree. `ResolveType::WorkerLoader` now carries the worker type and goes through the normal chunkable-module branch, since the resolved target is a real chunkable module again. 2. Graph reachability for the `createWorker` runtime helper. The loader is created during chunking, so its own `references()` are never traversed and it cannot put the helper into the graph. `add_create_worker_reference` now declares it as a `SingleChunkableModuleReference` next to each `WorkerAssetReference` during analysis — the same pattern `WebAssemblyModuleAsset` uses for its own runtime helper. The helper stays its own on-demand module (it is only chunked into apps that use workers) rather than moving into the common runtime. 3. The `AssetContext` the helper has to be processed with. `make_chunk_group` recovers it from the worker's entry module via `ResolveOrigin`. Both `url_resolve` (web workers) and the Node worker-thread branch resolve the worker through `origin.asset_context().process_resolve_result(...)`, so that is the same context the helper reference used — which is what makes the memoized `create_worker_module` return the identical `Vc`, and therefore the identical chunk item id, on both sides. Because worker edges no longer point at a non-chunkable node, the chunking traversal reverts to a plain `Exclude` for them, dropping the fall-through-to-`Parallel` workaround the marker required. `WorkerModuleReference` became unused and is removed. Generated output is unchanged: the worker snapshots needed no regeneration. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Production builds failed with:
ModuleId not found for ident:
[project]/app/browser/worker.js [app-client] (ecmascript, worker loader)
`get_global_module_id_strategy` builds the deterministic id map from the
modules in the module graph, plus a special case that registers the
*synthetic* idents of loaders that only come into existence during chunking
— previously just `AsyncLoaderModule`.
The `WorkerEntryModule` marker used to be a real graph module, so the worker
loader's id was registered as a side effect of the marker being traversed.
Removing the marker made that ident synthetic, exactly like the async
loader's, but without the matching registration — so the `new Worker(...)`
codegen looked up an id that was never put in the map.
Handle `ChunkingType::Worker` alongside `ChunkingType::Async` there, using
the same memoized `worker_loader_asset_ident_for` that both the codegen
lookup and `WorkerLoaderModule::ident()` go through. `async_idents` is
renamed to `loader_idents` since it now covers both.
Note on why this escaped local testing: the worker snapshot fixtures run
with `runtimeType: Development`, which uses `ModuleIdStrategy { module_id_map:
None, fallback: Ident }` and never consults the id map. The deterministic
path is only reached from next-api's production project setup and
`turbopack-cli build`, neither of which the turbopack-tests harness
exercises — so development snapshots were byte-identical while production
builds were broken.
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
`node-worker-threads` hung: 5 of 7 tests timed out at exactly 120s, starting with the self-spawning `new Worker(__filename)` case. The later timeouts are a cascade — it is a dev-mode test, so the blocked compilation stalls every later route request. Canary creates `WorkerLoaderModule` while building the module graph, without availability info in its identity, so a worker that references itself memoizes back to the same loader task and `chunk_group` runs once. This branch creates loaders during chunking *with* availability info in their identity, so each nesting level is a distinct task. Web workers terminate because the nested availability prunes the nested chunk group empty, but the Node path still went through `root_entry_chunk_group`, which hardcodes `AvailabilityInfo::root()` — so availability never accumulated and each level rediscovered the self-reference. Inheriting availability into the Node entry chunk group would fix the recursion but break the workers that currently pass: `entry_chunk_group` forwards availability to `make_chunk_group`, which prunes already-available modules, and a Node worker thread loads only its own entry chunk (plus what that requires relative to `__dirname`). It has no equivalent of the browser `createWorker` preload list, so a pruned entry chunk is simply missing module factories. So keep Node entry groups rooted and self-contained, and break the recursion explicitly instead: - Add `inner_is_available`, the same batch-aware availability probe `AsyncLoaderModule::chunk_group` uses. - When a nested Node worker loader finds its own entry already available, return no output assets instead of building a second entry chunk group. - Add memoized `node_worker_entry_path`, deriving the entry filename from the inner module's ident, and generate the loader's `__dirname + "/" + …` path from it rather than from `assets.last()`. The nested loader then names the file the outer level already emitted, without needing a chunk group of its own. Termination: the route group excludes the worker edge, so loader(w, A) has `w ∉ A` and builds the real entry group; that group's availability B contains `w`, so the self-reference's loader(w, B) short-circuits. Bottoms out at depth 2. Browser worker output is unchanged — re-running the snapshot suite with UPDATE=1 modifies no files. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
The previous attempt made the self-referencing `new Worker(__filename)` route return 500 instead of hanging: it skipped building the worker's entry chunk group whenever the worker target was already available, so the entry file was never emitted and the worker could not be required. That check cannot work. For `new Worker(__filename)` the worker target *is* the module containing the call, and a chunk group's availability always includes its own entry — so the target looks "already available" on the very first, non-recursive call, indistinguishable from the recursive one. Fix the recursion where it actually comes from instead. Node worker loaders keep building rooted, self-contained entry chunks (they run in a fresh thread with no browser-style preload list, so their entry chunk must not be pruned), and `make_chunk_group` now passes `AvailabilityInfo::root()` for `NodeWorkerThread` *before* calling `worker_loader_chunk_item`. That keeps the task's arguments — and therefore its memoization key — independent of nesting depth, so the rediscovery that happens while chunking the worker's own entry group lands on the same task instead of creating a new one per level. This is canary's termination-by-memoization, restored. Doing this at the call site matters: arguments are hashed to find the cached cell before a `#[turbo_tasks::function]` body runs, so normalizing inside `WorkerLoaderModule::new` (as the previous commit did) is too late to change the key. `new` keeps the normalization as defense-in-depth, with a comment saying so. Web and shared web workers are unchanged: they still get the enclosing chunk group's availability, which is what unrolls their content-hash cycle. Verified against the reproducer this time. `turbopack-tests::execution`'s `basic/worker-threads` fixture has a `should handle self-referencing worker (__filename)` case — the same failure CI reported. It needs only `jest-circus`, so it can be run locally: the test fails with the fix stashed and passes with it applied, and the full execution suite is green (279). Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Pure content drift from rebasing across ~105 canary commits (e.g. an unrelated DOM CSS-reload fix changed embedded runtime source, which shifts embedded source-map content in these fixtures). No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Pure content drift from rebasing across 20 canary commits. No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Pure content drift from rebasing across 16 canary commits. No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Pure content drift from rebasing across 64 canary commits: content-addressed output names and embedded source maps changed. No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Pure content drift from rebasing across 62 canary commits (shared HMR runtime refactor changes embedded runtime source). No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Content drift from rebasing onto canary: the DOM runtime now uses the findChunk* lookup helpers (#100047). No logic change. Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
656b569 to
b732ade
Compare
Fixes #97399
Replaces #97470 (moved to a branch in this repository so deploy tests run).
What?
Fix a Turbopack production-build deadlock when a web worker spawns its own entry, and test that the resulting workers actually run in both dev and production.
Why?
The worker loader originally built every worker chunk group with root availability. A self-reference repeatedly regenerated a content-hashed chunk group, making the chunk path depend on its own content. The first iteration of this fix gave every worker the page's available modules and forwarded all page chunks to it, although a worker runs in a separate realm.
How?
new Worker(...)references the on-demandcreateWorkerhelper and passes its exported function to the late loader at runtime. The loader does not resolve a helper or require its worker target to implementResolveOrigin(which fails for facade-wrapped modules).versioned_content()while keeping worker entry output unchanged.Tests
The new e2e passes locally in Turbopack, webpack, and rspack dev/build+start, and Turbopack cache-components dev/build+start. Existing browser/Node worker suites, Rust workspace checks, snapshots, and execution tests pass. Deployed-mode coverage remains enabled.