Files
Luke SandbergandGitHub a9323da293 Stop pinning compiled chunk source on EcmascriptBuildNodeChunkVersion.
(#93807)

## What

Stop pinning compiled chunk source on `EcmascriptBuildNodeChunkVersion`.

The struct previously held a `Vec<ReadRef<CodeAndIds>>` for every module
in the chunk, even though the HMR update path only needs the bytes for
the *changed* subset and the bytes are already on disk. That field was
also forcing the upstream `CodeAndIds` / `BatchGroupCodeAndIds` tasks to
stay `serialization = "skip"`, so warm restarts had to re-walk every
module and re-hash its source to rebuild `entries_hashes`.

This change mirrors the browser-side pattern: a new
`EcmascriptBuildNodeChunkContentEntries` task lives on the chunk content
and holds `ResolvedVc<Code>` + `ResolvedVc<u64>` per module. The version
struct shrinks to `{ chunk_path, minify_type, entries_hashes }`, drops
`serialization = "skip"`, and switches `chunk_path` from `String` to
`RcStr`.
`update_ecmascript_node_chunk_content` now resolves entries lazily, only
when an added or modified module actually needs its code shipped.

## Why

Two wins, both for dev sessions starting against a warm filesystem
cache:

- **Memory.** The version no longer transitively pins every module's
compiled `Rope` in heap — those bytes can stay on disk until HMR
actually needs them.
- **Warm-restart CPU.** `entries_hashes` is sourced from the per-module
`Code::source_code_hash()` task (already cached) and the version itself
now round-trips through the persistent cache, so we don't re-hash
anything on warm start.

The HMR payload shape is unchanged.

## Perf
This should speed up warm builds a bit but the major benefit is not
recomputing node outputs and keeping them in ram

measuring v0 after loading the main route
Branch: Cold: 12.3G Warm: 7.34G
Canary: Cold 12.3G Warm: 8.5G

The trace file confirms the recomputations are gone and the heap
measurements confirm we trimming ~1.1g of ram

Using the devlow benchmarks i was able to confirm a possible small
progression

```
# canary
chat dev startup build=warm: root page = 23.96 s (from root page/start)
chat dev startup build=warm: root page = 21.21 s (from root page/start)
chat dev startup build=warm: root page = 22.70 s (from root page/start)

# branch
chat dev startup build=warm: root page = 20.94 s (from root page/start)
chat dev startup build=warm: root page = 19.43 s (from root page/start)
chat dev startup build=warm: root page = 23.49 s (from root page/start)

```


## Tests

I added a new integration test to ensure we don't accidentally regress
here. Which confirms that 'clean warm builds' run nothing and 'clean
warm dev sessions' just set up HMR session infra
<!-- NEXT_JS_LLM_PR -->
2026-05-19 15:07:46 -07:00

42 lines
1.0 KiB
TOML

[package]
name = "turbopack-nodejs"
version = "0.1.0"
description = "TBD"
license = "MIT"
edition = "2024"
autobenches = false
[lib]
bench = false
[features]
# enable "HMR" for embedded assets
dynamic_embed_contents = ["turbo-tasks-fs/dynamic_embed_contents"]
# enable test utilities such as `RuntimeType::Dummy`
test = ["turbopack-ecmascript-runtime/test"]
[lints]
workspace = true
[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
either = { workspace = true }
indoc = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_qs = { workspace = true }
tracing = { workspace = true }
urlencoding = { workspace = true }
turbo-bincode = { workspace = true }
turbo-rcstr = { workspace = true }
turbo-tasks = { workspace = true }
turbo-tasks-fs = { workspace = true }
turbo-tasks-hash = { workspace = true }
turbopack = { workspace = true }
turbopack-core = { workspace = true }
turbopack-ecmascript = { workspace = true }
turbopack-ecmascript-runtime = { workspace = true }