Visitar URL original
[browser] generate() never settles when one module has ~hundreds of static imports · Issue #11565 · rolldown/rolldown · GitHub
Skip to content

[browser] generate() never settles when one module has ~hundreds of static imports #11565

Open

Description

@madvib

Summary

bundle.generate() never settles when a single module fans out to ~hundreds of static imports. rolldown() resolves (scan completes, all plugin hooks return); the generate() promise then hangs forever with no rejection. Closing the wedged bundle throws RuntimeError: Atomics.wait cannot be called in this context, and subsequent builds in the same page never settle either — the WASM async runtime does not recover.

Minimal repro (standalone, no product code): https://gist.github.com/madvib/237054084eed4ff3d034b6e3eb27e139

bun install && bun run build
bun src/run.ts 100   # settles: done:1 chunks (~1s)
bun src/run.ts 600   # hangs: TIMEOUT at stage: scanned (exit 2)

Repro shape

  • One trivial plugin: sync resolveId + sync load from an in-memory map. No transforms, no async hooks, no registry, no fetch.
  • Star graph: src/main.ts -> src/index.ts -> N leaf modules (export const v{i} = {i}).
  • @rolldown/browser@1.3.0, Chromium, cross-origin isolated page (COOP: same-origin, COEP: require-corp).

Characterization (all with the trivial plugin unless noted)

graph result
star-100 settles (~1s)
star-600 generate() hangs (probabilistic, roughly 1 run in 2)
chain-600 (same module count) settles, 355ms
tree-600, 60 mids x 10 leaves settles, 191ms
star-600 + 30s deadline, retry x3 (fresh rolldown() each) all 3 hang; close() throws Atomics.wait

With two trivial scanning transform hooks added, star-300 hangs ~always and star-100 hangs ~half the time — probability rises with per-module hook traffic, but the trigger is single-module fan-out (scan-phase hook burst), not total modules or total hook calls (tree-600 makes more total calls than star-600 and settles instantly).

Environment

  • @rolldown/browser: 1.3.0 (latest at time of filing)
  • Browser: Chromium (Playwright bundled) with SharedArrayBuffer (isolated)
  • Page thread stays alive during the hang (rAF/setInterval keep firing); no diagnostic, no rejection.

Happy to run narrower probes (hook-call accounting, worker introspection) against a debug build if that helps localize the lost completion.

Activity

  1. madvib commented on Nov 7, 2026

    @madvib
    Author

    Follow-up isolating this to the browser build:

    • Same star-600 graph through native rolldown@1.3.0 (same trivial resolveId+load plugin shape): scans in ~1ms, generate() settles in ~53ms. No hang.
    • Same graph through @rolldown/browser@1.3.0: rolldown() resolves, generate() never settles (~1 run in 2 at width 600; ~always at width 300 with two trivial scanning transform hooks added).

    So the graph and the plugin calling pattern are fine — the completion is lost somewhere in the browser/WASI async bridge under scan-phase hook burst. Chain-600 and tree-600 (same module count, low per-module fan-out) settle in <1s in the browser build, so it tracks burst concurrency, not totals.

    Environment for the record: Linux x64, 10 cores, Chromium via Playwright 1.65, cross-origin isolated page, @rolldown/browser@1.3.0 (latest; no newer release or dist-tag at time of filing).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions