Skip to content

🔄 Sync from upstream - 2026-01-12#240

Merged
lakshit-hivel merged 42 commits intocanaryfrom
sync-upstream-20260112-132400
Jan 12, 2026
Merged

🔄 Sync from upstream - 2026-01-12#240
lakshit-hivel merged 42 commits intocanaryfrom
sync-upstream-20260112-132400

Conversation

@lakshit-hivel
Copy link
Copy Markdown

🔄 Upstream Sync

This PR syncs the latest changes from the upstream repository.

Changes: 42 new commit(s) from upstream

Recent Upstream Commits:

38ac035394 Pass loading boundary as part of RSC data (#87825)
72a80d7ff3 Read from segment cache unknown routes (#87293)
4a6d18c490 Pass RouteTree into navigation function (#87256)
90507a9ee8 Track search string as part of "refresh state" (#87203)
2df811a246 v16.1.1-canary.21
78c102f2e5 fix: skip stats-aggregate job for docs-only changes (#88330)
774b78a097 Stats Action: Add ready in time (#88283)
25046dfa1b v16.1.1-canary.20
778c53130d Update Rspack production test manifest (#88300)
d767729240 

This PR was automatically created by the multi-repo orchestrator.
Repository: next.js
Timestamp: 2026-01-12T13:24:08.468328

timneutkens and others added 30 commits January 8, 2026 12:00
…d process (vercel#88230)

## Eliminate duplicate `loadConfig` calls during development server
startup

### What?

Remove duplicate `loadConfig` calls from the parent process
(`next-dev.ts`) by passing `distDir` via IPC from the child process
where config is already loaded.

### Why?

Previously, when booting the development server:

1. The **child process** (`start-server.ts` → `router-server.ts`) would
call `loadConfig` to get the full configuration for the actual server
2. The **parent process** (`next-dev.ts`) would also call `loadConfig`
separately just to access `config.distDir` for telemetry and trace
uploads

Since these are separate processes, the in-memory config cache couldn't
be shared, resulting in redundant config loading work.

### How?

Extended the existing IPC messaging to include `distDir` when the child
process sends `nextServerReady`:

```
Parent (next-dev.ts)         Child (start-server.ts → router-server.ts)
        |                                    |
        |-------- fork() ------------------>|
        |<------- nextWorkerReady ----------|
        |-------- nextWorkerOptions ------->|
        |                                   | loadConfig() ← only called here now
        |<-- nextServerReady + distDir -----|
        |                                    |
   Store distDir for                         
   telemetry/trace uploads                   
```

**Changes:**

- **`render-server.ts`**: Added `distDir` to `ServerInitResult` type
- **`router-server.ts`**: Return `config.distDir` in `initialize()`
result and pass to render server opts
- **`start-server.ts`**: Return `distDir` from `startServer()` and
include it in IPC message
- **`next-dev.ts`**: Receive `distDir` from IPC, remove both
`loadConfig` calls, use `distDir` directly for telemetry/trace
…ge (vercel#88247)

This updates to expand our normalized interface of handler(req, ctx) to
edge runtime as well and middleware. This also applies some fixes for
outputs and routes for app router. Validated against our test suite here
vercel/vercel#14102 to start.

Once nextjs/adapter-vercel#6 is landed we can
start wiring up the full E2E test suite for validating.
## Move "Ready in X" log before config loading and eliminate duplicate
`loadConfig` calls

### What?

Reorder the dev server startup flow so the "Ready in X" log appears
before loading the user's `next.config.js`, and eliminate duplicate
`loadConfig` calls in both dev and build paths.

### Why?

Previously, the "Ready in X" time included the time spent loading and
evaluating the user's `next.config.js` file. This made the framework
appear slower than it actually is, especially for projects with complex
config files or slow config evaluation.

Additionally, config was being loaded twice:
- **Dev server**: Once in `getStartServerInfo()` just for logging, then
again in `getRequestHandlers()`
- **Build**: Once for the main build, then again in
`getStartServerInfo()` just for logging

### How?

**Before:**
```
1. getStartServerInfo() → loadConfig #1 (slow - blocks on next.config.js)
2. logStartInfo() → logs version, URLs, experimental features
3. "Ready in X" ← time includes config loading
4. getRequestHandlers() → loadConfig #2
```

**After:**
```
1. logStartInfo() → logs version, URLs, envInfo (no config needed)
2. "Ready in X" ← reflects actual framework startup time
3. getRequestHandlers() → loadConfig (once)
4. logExperimentalInfo() → logs experimental features
```

**Changes:**

- Split `logStartInfo` into two functions:
- `logStartInfo()` - basic info (version, URLs, env) - no config needed
  - `logExperimentalInfo()` - experimental features and cacheComponents
- Removed `getStartServerInfo()` which was loading config unnecessarily
- Added `experimentalFeatures` and `cacheComponents` to
`ServerInitResult` so they can be passed back after config loads
- In build, added `reportExperimentalFeatures` callback to the existing
`loadConfig` call instead of loading config twice

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
When self-hosting Next.js, it's possible to customize a cache handler
for ISR. Self-hosted Next.js also makes use of a similar cache interface
for `next/image`, but there's no way to customize this cache handler.

This plugs into the [existing
ability](https://nextjs.org/docs/app/api-reference/config/next-config-js/incrementalCacheHandlerPath)
to customize a `cacheHandler` to support the `IMAGE` type. This lets you
implement whatever constraints you might have on your self-hosted
environment, eg the need for an LRU cache, or storing optimized images
outside of disk.

This is currently opt-in because it's a breaking change for cache
handlers to start receiving `IMAGE` cache types, which we'll make
default in the next major.
Closes PACK-5850
Closes vercel#86132
Closes vercel#86714

The problem was that you get this with scope hoisting:

```js
"index.js": () => {

// foo.js

// exports object read here
const self_import = turbopack_import("foo.js")

// exports object created here
__turbopack_esm__([...], "foo.js")

// index.js

....
}
```

without scope hoisting, that exports object is already initialized before the module factory runs
```js
"foo.js": () => { // exports initialized here

const self_import = turbopack_import("foo.js")


__turbopack_esm__([....]);
}
....
```

so let's fix it by making it hoist the `__turbopack_esm__` statement (just as we did before always) in the case of self-imports


---


This bug is actually very similar to vercel#82827. It also executes `data.js` twice.

The problem is that if a module imports itself,
```js
"[project]/input/data.js [test] (ecmascript)", ((__turbopack_context__) => {
"use strict";

var __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/input/data.js [test] (ecmascript) <locals>");
var __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/input/data.js [test] (ecmascript)");
__turbopack_context__.s([
    "Data",
    0,
    __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__,
    "bar",
    ()=>__TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__$3c$locals$3e$__["bar"],
    "foo",
    ()=>__TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__$3c$locals$3e$__["foo"]
]);
}),
```
and with scope hoisting, the `import("id")` happens before the `esm_export(..., "id")`:
```js
module.exports = [
"[project]/index.js [test] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// MERGED MODULE: [project]/index.js [test] (ecmascript)
;
// MERGED MODULE: [project]/data.js [test] (ecmascript) <locals>
;
function foo() {
    return 'foo';
}
function bar() {
    return 'bar';
}
;
__turbopack_context__.s([
    "bar",
    ()=>bar,
    "foo",
    ()=>foo
], "[project]/data.js [test] (ecmascript) <locals>");
// MERGED MODULE: [project]/data.js [test] (ecmascript) <export * as Data>
;
// MERGED MODULE: [project]/data.js [test] (ecmascript)
;
var __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/data.js [test] (ecmascript) <locals>");
var __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/data.js [test] (ecmascript)");
__turbopack_context__.s([
    "Data",
    0,
    __TURBOPACK__imported__module__$5b$project$2f$input$2f$data$2e$js__$5b$test$5d$__$28$ecmascript$29$__,
    "bar",
    ()=>bar,
    "foo",
    ()=>foo
], "[project]/data.js [test] (ecmascript)");
```
https://doc.rust-lang.org/reference/attributes/derive.html#r-attributes.derive.automatically_derived

We discussed this during our morning team meeting. I had an LLM write this.  
  
This does not identify any new dead code.
This auto-generated PR updates the development integration test manifest used when testing Rspack.
…ercel#87993)

### What?

Adds a Claude Code plugin marketplace to the Next.js repository with an
initial `cache-components` plugin that provides expert guidance for
Cache Components and Partial Prerendering (PPR).

### Why?

Cache Components introduces a new programming model that combines
Partial Prerendering, Dynamic I/O, and the `"use cache"` directive. Team
members and contributors need guidance on:

- The `'use cache'` directive and its variants (`private`, `remote`)
- Cache lifetime configuration with `cacheLife()` and predefined
profiles
- Cache tagging and invalidation with `cacheTag()`, `updateTag()`,
`revalidateTag()`
- Parameter permutation rendering and subshell generation with
`generateStaticParams`
- Migration from deprecated `revalidate` and `dynamic` segment configs
- Build-time error resolution (dynamic data outside Suspense, uncached
data, etc.)

A Claude Code plugin allows this knowledge to be distributed directly
from the repo and proactively activated when working in projects with
`cacheComponents: true`.

### How?

Introduces the `.claude-plugin/` directory structure:

```
.claude-plugin/
├── marketplace.json                       ← Marketplace catalog (name: "nextjs")
└── plugins/
    ├── README.md                          ← Documentation for adding plugins
    └── cache-components/
        ├── .claude-plugin/plugin.json     ← Plugin manifest
        ├── README.md                      ← Plugin documentation
        └── skills/cache-components/
            ├── SKILL.md                   ← Core concepts, mental model (496 lines)
            ├── REFERENCE.md               ← API reference, migrations (875 lines)
            ├── PATTERNS.md                ← 12 production patterns (781 lines)
            └── TROUBLESHOOTING.md         ← Build errors, debugging (721 lines)
```

**Installation:**
```bash
/plugin marketplace add vercel/next.js
/plugin install cache-components@nextjs
```

**Key skill features:**
- **Proactive activation** when `cacheComponents: true` is detected in
next.config
- **Mental model decision tree** for thinking through caching decisions
- **Cache scope clarification** explaining what creates new cache
entries
- **updateTag vs revalidateTag guide** with decision table and examples
- **Migration scenarios** from deprecated `revalidate`/`dynamic` segment
configs
- **Quick debugging checklist** for common cache issues
- **Parameter permutation rendering** (how `generateStaticParams`
creates subshells)
- **12 production patterns** including e-commerce, multi-tenant SaaS,
and subshell composition

### Documentation Highlights

| Section | Description |
|---------|-------------|
| Mental Model | Decision tree for when to use `'use cache'`, `'use
cache: private'`, or `<Suspense>` |
| Cache Scope | What creates new cache entries (function identity,
arguments, file path) |
| updateTag vs revalidateTag | Decision guide with e-commerce examples |
| Migration Scenarios | Before/after examples for `revalidate`,
`force-dynamic`, and ISR patterns |
| Quick Debugging Checklist | Copy-paste checklists for common issues |
…ext::with_context` calls (vercel#88293)

`anyhow!` works here because we just need something that implements `Display`, but `format!` is better.
…8243)

### What?

Removes the `experimental.ppr` configuration flag and consolidates
Partial Prerendering (PPR) functionality into the `cacheComponents`
architecture.

### Why?

Having two separate systems (PPR flag and cacheComponents) created dual
code paths and complexity. PPR is now implicitly enabled when
cacheComponents is enabled, simplifying the codebase.

### How?

- Deleted `postpone.ts` and `ppr.ts` utility files
- Removed `PrerenderStorePPR` work unit type and all PPR-specific work
unit cases
- Removed React.unstable_postpone-based dynamic rendering logic
- Deprecated `experimental.ppr` config (kept for backwards
compatibility)
- Simplified build system to check only `cacheComponents` flag
- Updated tests to remove PPR-specific behavior expectations

This removes ~986 lines of code across 48 files.
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the PR.
- Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to understand the PR)
- When linking to a Slack thread, you might want to share details of the conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->
## What?

Explain in agent.md where the Next.js core code lives.
It didn't actually use `output:  "standalone"`

The list should be accurate now.
### What?

This makes database read spans being grouped per family
This removes quite a lot of files.

But some of them are simply needed because we load `build/swc/index.js`, e.g. the two following examples. Removing them would require some more refactoring.

<img width="1274" height="680" alt="Bildschirmfoto 2026-01-09 um 14 22 07" src="https://github.com/user-attachments/assets/2354b9f2-1bfd-438d-af54-e69e8934cbc4" />
<img width="1346" height="474" alt="Bildschirmfoto 2026-01-09 um 14 23 15" src="https://github.com/user-attachments/assets/8f516cd6-321e-4f9c-bade-19eed7093c88" />
…ever (vercel#88190)

This PR refactors the Resolution Plugin traits to add new `Always` or
`Never` enum variants to `resolve condition`. This allow plugins (like
[ExternalsPlugin](utooland/utoo#2494) when no
externals are configured) to skip the expensive `matches()` task calls
entirely. Testing on a medium project showed a reduction of ~1.4k
`resolve_call` tasks. I think this can be shared to upstream for future
improvement of `resolve plugin`

See the metrics for improvements.
<img width="1400" height="600" alt="image"
src="https://github.com/user-attachments/assets/06eae96e-070a-43c3-9f68-a3a27ee7dc0d"
/>

---------

Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
### What?

children need to be prefetched too, since connect_child accesses the child to check for scheduling
### What?

Add the point of stale detection to the trace
…fore erroring (vercel#88119)

### What?

The graph traversal used to early exit when there are errors. This leads to cycles to continuously executing the graph traversal to discover more task one by one. This leads to bad incremental performance.
## Fix: Remove unnecessary decimal place in millisecond duration
formatting

### What

Fixed `durationToString` to display whole milliseconds (e.g., "193ms")
instead of showing a redundant decimal place (e.g., "193.0ms").

### Why

The "Ready in X" message was displaying durations like "Ready in
193.0ms" which looks incorrect. The `.0` suffix provides no useful
information and makes the output look like a bug.

### How

Changed `.toFixed(1)` to `.toFixed(0)` for millisecond formatting in
`durationToString()`. This aligns with the behavior of the newer
`durationToStringWithNanoseconds()` function which already uses
`.toFixed(0)` for milliseconds.

**Before:** `✓ Ready in 193.0ms`  
**After:** `✓ Ready in 193ms`
mischnic and others added 12 commits January 9, 2026 20:57
…#86346)

Before:
```
Module not found: Can't resolve 'next/dist/esm/build/templates/helpers'
failed to analyze ecmascript module '[project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js [ssr] (ecmascript)'

Caused by:
- failed to parse [project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js
- Transforming and/or parsing of [project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js failed
- failed to deserialize `swc_common::plugin::diagnostics::PluginCorePkgDiagnostics`
- Mismatch { name: "array", found: 48 }

Debug info:
- Execution of <ModuleAssetContext as AssetContext>::process_resolve_result failed
- Execution of <ModuleAssetContext as AssetContext>::process failed
- Execution of <EcmascriptModuleAsset as Module>::side_effects failed
- Execution of analyze_ecmascript_module failed
- failed to analyze ecmascript module '[project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js [ssr] (ecmascript)'
- Execution of <EcmascriptModuleAsset as EcmascriptParsable>::failsafe_parse failed
- Execution of parse failed
- failed to parse [project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js
- Transforming and/or parsing of [project]/node_modules/.pnpm/next@file+..+next-repo-2bf56e024dbccc85257a8c308492f2375d690b206153b02d3bea2029707d4c38+packa_tacnzof7rtmb5bwqa7bdnfcdbq/node_modules/next/dist/esm/build/templates/helpers.js failed
- failed to deserialize `swc_common::plugin::diagnostics::PluginCorePkgDiagnostics`
- Mismatch { name: "array", found: 48 }
Import map: aliased to module 'next' with subpath '/dist/esm/build/templates/helpers' inside of [project]/


https://nextjs.org/docs/messages/module-not-found
```

After:
```
./packages/next/document.js
Failed to execute SWC plugin
An unexpected error occurred when executing an SWC EcmaScript transform plugin.
This might be due to a version mismatch between the plugin and Next.js.

Failed to execute @swc/plugin-react-remove-properties

Caused by:
    0: failed to deserialize `swc_common::plugin::diagnostics::PluginCorePkgDiagnostics`
    1: Mismatch { name: "array", found: 48 }
```
…7216)

This was disabled for the 16.1 release.  so re-enable it just to get rid of the slightly odd `!isStableBuild` pattern and also ensure i don't forget to ship it for 16.2
This auto-generated PR updates the development integration test manifest used when testing Rspack.
This auto-generated PR updates the production integration test manifest used when testing Rspack.
## What?

Measures the time to `Ready in X ms` in the PR stats action so that
improvements/regressions on it are highlighted on each PR.

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
## What?

Adds the docs-only change check to the `stats-aggregate` job.

## Why?

The `stats` job already skips work for docs-only changes using
`scripts/run-for-change.mjs`, but the `stats-aggregate` job was missing
this check. This caused unnecessary job runs when PRs only contain
documentation changes.

## How?

- Added the "Check non-docs only change" step (same pattern as the
`stats` job)
- Added conditional `if` checks to all subsequent steps
- Changed `fetch-depth` from 1 to 25 (required for the change detection
script to check commit history)
When a parallel route does not match the current URL, in its place we
render the render the last active route that matched that slot. When we
next refresh the page, we track some additional state to inform the
router how to refresh the old, "inactive" routes that aren't reachable
from the outermost, "active" route.

Before this PR, the only state we tracked was the URL of the page that
last matched the inactive segment. After this PR, I am also tracking the
search query string of the old route. This can't be inferred from the
URL alone because the URL may have been rewritten by the server.

We don't need the search query to perform a network request — that is
handled by the URL alone — but we do need it in order to construct a
cache key for the page segments. Today we read the cache key from the
FlightRouterState, which has all the params embedded inside of it, but
eventually I want to remove all uses of FlightRouterState that aren't
about sending/receiving data from the server or storing the tree as
state in the browser's history entry. So this is an incremental step
towards that goal.
RouteTree is the client version of FlightRouterState. It's the same
representation of the route tree, but it structured for optimized
lookups of the client cache. The plan is the make this the primary/only
type used for dealing with routes on the client; FlightRouterState will
be used a transport format only.
Based on:

- vercel#87203
- vercel#87256

---

Currently, there's a cliff when navigating to a route that is unknown to
the client. The prefetch cache is bypassed completely because we don't
know the structure of the target route, and therefore we don't know
which segments we might be able to use to construct a loading state (or
even a fully cached response).

However, because RSC responses are streaming, we can take advantage of
the fact that the first chunk of the response contains the route tree.
So as soon as we receive the first chunk, we can show a cached loading
state to the user (if available), even while we wait for the rest of the
response to arrive from the server.

To access a segment's cache, you need its corresponding RouteTree.
Previously this was not available during an unprefetched navigation
because those objects were only created during a prefetch. But now that
we've migrated the navigation implementation to use RouteTrees, too, we
can access the cache directly inside the main navigation function,
rather than having to do two separate passes like before.

In other words, the only difference between a navigation to an unknown
route versus a prefetched route is the extra latency required to get
those first bytes from the server. Once the route tree resolves, the
rest of the implementation and behavior is identical.
Based on:

- vercel#87203
- vercel#87256
- vercel#87293

---

Removes the `loading` as a separate field in the server response
payload. Instead, it is passed as part of the component data (the `rsc`
field).

This simplifies much of the logic on the client because, logically, they
were already coupled. Unifying the fields removes the possibility that
they'll get out of sync due to some implementation mistake.
@lakshit-hivel lakshit-hivel merged commit 38ac035 into canary Jan 12, 2026
162 of 455 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.