Skip to content

[pull] canary from vercel:canary#1030

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

[pull] canary from vercel:canary#1030
pull[bot] merged 6 commits into
code:canaryfrom
vercel:canary

Conversation

@pull

@pull pull Bot commented May 7, 2026

Copy link
Copy Markdown

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 : )

sokra and others added 6 commits May 7, 2026 10:37
### What?

Reorder the imports emitted by the app-page loader-tree builder so that
they are grouped by their depth in the loader tree (shallow segments
first, deep segments last) instead of the somewhat arbitrary order
produced by recursive walking.

### Why?

The loader tree is built by walking the app router tree recursively, and
each segment contributes a few `require(...)` declarations (layout,
error, loading, page, metadata, ...) to a shared list of imports that is
later concatenated into the generated entry module. The order of that
list is what ends up in the bundle, and walking the tree recursively
interleaves segments at different depths in a way that depends on
traversal order rather than on the structure of the route.

For chunking and readability we want a more predictable order: *outer
(shallower) layouts/segments before inner (deeper) ones*, with imports
inside the same depth keeping their original relative order. This gives
downstream chunking a more useful signal.

### How?

In `crates/next-core/src/base_loader_tree.rs`:

- `BaseLoaderTreeBuilder::imports` is now `Vec<(u32, RcStr)>` instead of
`Vec<RcStr>`. The `u32` is a sort key (the depth at which the import was
produced).
- `create_module_tuple_code` takes a new `position: u32` argument and
stores it alongside the generated `require` line.

In `crates/next-core/src/app_page_loader_tree.rs`:

- `walk_tree` takes a new `depth: u32` argument. The initial call from
`build()` passes `0`.
- `depth` is threaded through `write_modules_entry`, `write_metadata`,
`write_metadata_items`, `write_metadata_item`, and
`write_static_metadata_item`, so the three other places that push
directly into `self.base.imports` (dynamic image metadata, static
metadata items, and their alt-text companions) also tag their entries
with the current depth.
- When recursing into `parallel_routes`, depth is incremented only for
the `"children"` slot. Named parallel routes (e.g. `@modal`) are sibling
slots of the same segment and therefore stay at the same depth.
- In `AppPageLoaderTreeBuilder::build`, the collected `(depth, import)`
pairs are stable-sorted by depth (`sort_by_key`, which is stable in
Rust, preserving the original relative order within each depth) and then
stripped back to `Vec<RcStr>` before being placed on
`AppPageLoaderTreeModule.imports`.

The public type of `AppPageLoaderTreeModule.imports` (`Vec<RcStr>`) is
unchanged, so consumers in
`crates/next-core/src/next_app/app_page_entry.rs` need no adjustments.

Co-authored-by: v-work-app[bot] <262237222+v-work-app[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
…#93550)

### What?

The dev overlay's code frame loses syntax highlighting whenever `next
dev` runs under a non-TTY parent (VS Code / Cursor's integrated
terminal, Docker without `-t`, `concurrently`, the JavaScript Debug
Terminal, CI logs, etc.). Tokens render as plain monochrome text instead
of the usual colored output.

### Why?

The "syntax highlighting" in the overlay isn't real highlighting. The
server embeds ANSI escape codes into the code frame string, and the
browser overlay parses them via Anser to render colored `<span>`s.
Whether the dev server's stdout is a TTY is irrelevant on this path —
the consumer is the browser.

Since #71860, `getOriginalCodeFrame` defaults `colors` to
`process.stdout.isTTY`, and the overlay middlewares relied on that
default. The moment stdout was a pipe, the overlay had nothing to color.

### How?

`createOriginalStackFrame` and `getOriginalStackFrames` are shared by
five consumers (overlay HTTP endpoint, webpack `Module not found`
plugin, browser-log forwarding, MCP error formatter, hot-reloader
resolver). Hardcoding `colors: true` would leak escape sequences into
the four non-overlay paths.

Thread a `codeFrameOptions: { colors?, maxWidth? }` parameter through
both functions. The overlay HTTP middlewares opt in to `colors: true`
and `maxWidth: DEVTOOLS_CODE_FRAME_MAX_WIDTH`; everyone else keeps the
existing TTY-gated colors and terminal-width wrapping. (The 1000-column
override from #92621 was suffering from the same leak — also scoped to
the overlay now.)

Tested from Cursor's integrated terminal with/without fix:

Before:
<img width="1174" height="1070" alt="CleanShot 2026-05-06 at 22 34
45@2x"
src="https://github.com/user-attachments/assets/16ef1ac2-749b-443d-9e76-42f1318e9cd1"
/>


After:
<img width="1254" height="1066" alt="CleanShot 2026-05-06 at 22 34
30@2x"
src="https://github.com/user-attachments/assets/577ef3c2-3f02-4bb5-9825-63aa5564c593"
/>
@pull pull Bot locked and limited conversation to collaborators May 7, 2026
@pull pull Bot added the ⤵️ pull label May 7, 2026
@pull pull Bot merged commit 225be6a into code:canary May 7, 2026
13 of 18 checks passed
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.

5 participants