Skip to content

[pull] canary from vercel:canary#1059

Merged
pull[bot] merged 7 commits into
code:canaryfrom
vercel:canary
May 20, 2026
Merged

[pull] canary from vercel:canary#1059
pull[bot] merged 7 commits into
code:canaryfrom
vercel:canary

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 20, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

unstubbable and others added 7 commits May 19, 2026 22:08
### What?

Splits the previously unified `PrehashedString` (which held a `Payload`
enum of `String | &'static str`) into two separate types:
`StaticPrehashedString { value: &'static str, hash: u64 }` for atoms
produced by `rcstr!` / `make_const_prehashed_string`, and
`DynamicPrehashedString { value: Box<str>, hash: u64 }` for atoms held
in an `Arc`. The static and dynamic paths were already distinguished by
the `STATIC_TAG` / `DYNAMIC_TAG` bits in `RcStr`, so the runtime branch
on enum discriminant was redundant.

### Why?

- **16 bytes saved per heap-allocated `RcStr` value .** Dynamic atoms
drop the `String::capacity` field (which was always equal to `len` since
the contents are immutable) and because they are stored in a
`triomphe::Arc` this drops the Arc payload to 32 bytes instead of 40
which matches a mimalloc bucket (previously we were rounded up to a 48
byte bucket) so we save 16 bytes.
- **8 bytes saved per static `RcStr` value.** The linker will optimally
align our 24 byte struct.
- Removes a layer of dispatch on the hot path (`as_str`, `==`, `Hash`) —
typed deref to the correct variant instead of matching on `Payload`.
(i.o.w. one 'descreminent' traversal instead of 2)

### How?

- `dynamic.rs`: `Payload` enum removed; two structs replace
`PrehashedString`. `deref_from` split into `deref_static` and
`deref_dynamic`. `restore_arc` returns `Arc<DynamicPrehashedString>`.
- `lib.rs`: `as_str` and `into_owned` dispatch on `tag()` (STATIC vs
DYNAMIC vs INLINE) rather than `location()`. New `heap_hash_and_str`
helper for `PartialEq` and `Hash` to share the static/dynamic branch.
`into_owned`'s `try_unwrap` arm uses `String::from(Box<str>)` which
reuses the box allocation (still O(1)).
- `turbo-rcstr-macros`: emit `::turbo_rcstr::StaticPrehashedString`
instead of `::turbo_rcstr::PrehashedString`.
- Added a comment on `DynamicPrehashedString` noting the future move to
`triomphe::ThinArc` to fold the two heap allocations (Arc header + boxed
bytes) into one. Deferred because that change would make
`RcStr::from(String)` copy the bytes, invalidating the documented cheap
`String -> RcStr -> String` round-trip — wants a separate evaluation.


<!-- NEXT_JS_LLM_PR -->
### What?

Adds a `compile_route` MCP tool that triggers on-demand compilation of a
specific route (app or pages) without issuing an HTTP request, and
returns any compilation issues.

### Why?

Coding agents and benchmarking workflows need a way to warm the module
graph or measure compile time for a route without standing up a live
backend to satisfy the request. The existing path — hitting the URL —
requires the route's runtime dependencies to be available and couples
compile timing to request handling.

### How?

- New tool `mcp/compile_route` registered in
`get-or-create-mcp-server.ts`, backed by a `compileRoute({ page,
clientOnly })` callback plumbed from the Turbopack hot reloader
- Reuses the dev server's existing on-demand entry path (`ensurePage` /
`handleRouteType`), so the call path matches a first navigation.
- Adds a `subscribeToChanges` opt-out on `ensurePage` and threads it
through `handleRouteType` / `handlePagesErrorRoute`. One-shot MCP
compilations skip HMR subscription wiring — without this, each call
would leak a subscription that fires on every subsequent file change for
the life of the dev server.
- Telemetry: registers `mcp/compile_route` in the `McpToolName` union.
- e2e test in
`test/development/mcp-server/mcp-server-compile-route.test.ts`.

<!-- NEXT_JS_LLM_PR -->
…93338)

Expose memory data to the MCP tool for the turbo trace server
 (#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 -->
Record simple counters for how many items were persisted.

This should be cheap enough to always do
@pull pull Bot locked and limited conversation to collaborators May 20, 2026
@pull pull Bot added the ⤵️ pull label May 20, 2026
@pull pull Bot merged commit 338d59c into code:canary May 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants