next.js
75d2ed53 - turbopack: emit assets before duplicate check in emit_assets (#93875)

Commit
62 days ago
turbopack: emit assets before duplicate check in emit_assets (#93875) ### What? Reworks `emit_assets` in `crates/next-core/src/emit.rs` so that: - The actual `emit()` / `emit_rebase()` write runs **before** `check_duplicates()`. - Per-duplicate diff checks inside `check_duplicates` run in parallel. - At most one `EmitConflictIssue` is emitted per asset path, even when multiple duplicates conflict. - The conflict-issue emission is wrapped in a `turbo_tasks::function` and reported via `as_side_effect()`. ### Why? Under eventual consistency, all children might be removed from `emit_assets`. This causes all `content()` and `emit()` tasks being inactive and keeping the stale value (which is potentially an error). On recomputation of `emit_assets()` this only makes very few `content()` tasks active, as tasks are still in a stale error state and make `emit_assets()` exit early. So it takes N recomputations of `emit_assets()` to be up-to-date, where N is the number of duplicate assets. This can slow down builds drastically and did cause build time up to 30min. Emitting the asset first ensures the file is written to disk even if the subsequent duplicate diagnostic crashes under eventual consistency. Doing the diff checks in parallel and folding conflicts into a single issue keeps the diagnostic path cheap and avoids noisy duplicate output for the same path. Tracking the issue emission as a turbo-tasks side effect means it is properly attributed to a task instead of being emitted ad-hoc from `emit_assets`. ### How? - Swap the order in both branches of `emit_assets` (server-side `emit` and client-side `emit_rebase`) so the write happens first, then `check_duplicates` runs as a diagnostic afterwards. A comment is left explaining the ordering requirement. - `check_duplicates` now returns `Result<()>` instead of returning the picked asset. The caller picks `assets.first()` directly, since `check_duplicates` only validated equivalence — it didn't choose a different asset. - The inner loop in `check_duplicates` uses `iter.map(async |next| { ... }).try_flat_join().await?` to run the diff comparisons in parallel and collect conflict details. `ext` is computed once before the loop and cloned into each task. - Only the first conflict from the collected list is reported, instead of one issue per conflicting asset. - The `EmitConflictIssue` emission is moved into a local `#[turbo_tasks::function] fn emit_conflict_issue(...)` invoked via `.as_side_effect().await?`, so the issue is tracked as a task side effect. <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Parents
Loading