Commit Graph

35737 Commits

Author SHA1 Message Date
next-js-bot[bot] dea82ba4ee v16.4.0-canary.24 v16.4.0-canary.24 2026-09-09 16:06:38 +00:00
Will Binns-Smith de356c6d84 Re-enable @vercel/devlow-bench publishing (#98236)
Revert 699ee92b2b to publish
`@vercel/devlow-bench` again. Trusted publishing has now been set up, so
the package should publish correctly.


Co-authored-by: Sebastian "Sebbie" Silbermann <sebastian.silbermann@vercel.com>
2026-09-09 16:03:48 +00:00
Josh Story e0966df114 test: migrate caching deploy exclusions to force gates (#98154)
## Summary

- migrate Cache Components, PPR, prefetching, prerendering,
revalidation, and SSG deployment exclusions to `@force-gate`
- remove the corresponding `skipDeployment` options and `skipped`
control flow while preserving each suite's rationale

This keeps caching-related exclusions together so their deployed
semantics can be reviewed by the same owners.

## Verification

- verified on the combined top-of-stack tree with `pnpm typescript`
- compared ordinary-mode collection before and after: all 4,670 existing
test names matched
- collected all 510 affected files in deploy mode: 4,578 skipped tests,
zero failures

<!-- NEXT_JS_LLM -->
2026-09-09 08:25:36 -07:00
Tobias Koppers 6caa786072 Typecheck the Turbopack ECMAScript runtime in CI (#98422)
### What?

Integrate the Turbopack ECMAScript runtime package checks into the
repository TypeScript task graph and resolve the latent type errors
those checks expose.

### Why?

The runtime package already had checks for its environment-specific
TypeScript projects, but CI never executed them. This allowed its public
runtime context types to diverge from their implementations, including
the `EsmExport` binding representation.

### How?

The package now participates in the existing Turbo `typescript` task
used by CI. Its package-local Turbo configuration builds workspace
dependencies first, ensuring the React Refresh type output is available
on clean checkouts without adding build dependencies to every repository
typecheck.

The runtime declarations now reflect the actual flat ESM binding format
and HMR behavior. Browser development references and return types also
mirror the runtime files and concatenation order used by the Rust
runtime builder. Turbopack snapshots that embed these runtime sources
and source maps are regenerated to match.

### Verification

- `cargo nextest run -p turbopack-tests -E 'test(snapshot)'` (125
passed)
- `pnpm --dir turbopack/crates/turbopack-ecmascript-runtime/js check`
- `pnpm lint-typescript --filter=@vercel/turbopack-ecmascript-runtime
--log-order=stream --force` after removing
`packages/react-refresh-utils/dist`
- Focused Prettier and ESLint checks
- `git diff --check`

<!-- NEXT_JS_LLM -->

<!-- fleet 56074f5a-b0e2-4b27-ac56-a2b6cebe3c8e -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 15:08:14 +00:00
Tobias Koppers b145ad6f3f Fix initial Turbopack HMR update handling (#98385)
### What?

Prevent a real client HMR update from being discarded when it arrives as
the first subscription emission.

### Why?

Capturing the chunk baseline and installing the update subscription are
separate asynchronous operations. A change between them turns the first
emission into a real update, but the client handler previously assumed
that emission was always initialization-only and dropped it after the
server had advanced its version state.

### How?

Inspect the first subscription result and preserve the existing
issues-only initialization behavior while forwarding partial or restart
updates through the normal issue and browser-update paths. This is the
narrower option because it does not change handling of the usual initial
issues payload.

### Verification

- `pnpm --filter=next build`
- `HEADLESS=true pnpm test-dev-turbo
test/development/app-dir/hmr-dynamic-component/hmr-dynamic-component.test.ts`
(including a locally controlled baseline-to-subscription race)
- Prettier and ESLint on the changed source

<!-- NEXT_JS_LLM -->

<!-- fleet 3820fb8a-c662-4c74-8316-e2d7cd42bfbd -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 08:07:34 -07:00
Will Binns-Smith fc977d6df7 Skip bundler config validation in next/jest and under test (#98392)
## Summary

Skip active-bundler config validation when `next/jest` loads
`next.config.*` under `PHASE_TEST`. Jest uses the Next.js SWC
transformer without running Turbopack or webpack, so bundler-specific
options should remain available without requiring phase-dependent user
config.

Add regression coverage for `experimental.turbopackRustReactCompiler`
and `experimental.cssChunking: 'graph'` in the test phase.

## Verification

- `pnpm test-unit test/unit/isolated/config.test.ts -t "Should allow
bundler-specific options during the test phase"`

<!-- NEXT_JS_LLM -->
2026-09-09 08:03:58 -07:00
Sebastian "Sebbie" Silbermann 9db9b8eff1 Remove tsec (#98423)
tsec enforces nothing today. Of its 22 rules, 16 have no callsite in the
four packages it checks, 5 find violations that are all suppressed by
tsec-exemptions.json, and ban-element-innerhtml-assignments has been
inert since TypeScript dropped the `InnerHTML` mixin from lib.dom.d.ts,
so its matcher resolves to no type. Net enforced findings: zero.

font, third-parties and next-playwright never extended
tsconfig-tsec.json, so they ran all 22 rules over source with no DOM
sinks at all, paying a full extra type-check each for nothing.
2026-09-09 14:57:10 +00:00
Tobias Koppers f17979b00d Stabilize scope_unbounded panic test (#98416)
### What?

Make `test_unbounded_panic_propagates_and_abandons_queue`
deterministically exercise panic-driven queue abandonment.

### Why?

The test failed in two consecutive canary `test-cargo-unit` attempts
after all 10,000 fixed seed items ran before the panic's abort was
recorded. That outcome is legal scheduling: a fixed seed queue can drain
before its designated panicking item executes, so the assertion did not
prove that abort handling was broken.

### How?

The test now grows a bounded work cascade and panics after a fixed
amount of processing. Since each completed item adds more work, queued
items are guaranteed to exist when the panic occurs. The test therefore
continues to verify panic propagation and queue abandonment without
relying on worker timing. Production `scope_unbounded` behavior is
unchanged.

### Verification

- Changed panic test passed 100 consecutive runs with
`release-with-assertions`
- `cargo nextest run -p turbo-tasks -E 'test(~scope_unbounded::tests::)'
--cargo-profile release-with-assertions` (17 passed)
- `cargo fmt --all -- --check`
- `git diff --check`

<!-- NEXT_JS_LLM -->


<!-- fleet ae1d9ec9-156e-4b53-80c0-f24bf0b22f13 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 16:52:38 +02:00
next-js-bot[bot] 42779fe73a v16.4.0-canary.23 v16.4.0-canary.23 2026-09-09 14:48:47 +00:00
Niklas Mischkulnig f367ab3426 Turbopack: fix require handling with alternative (#98258)
Regression from 
- https://github.com/vercel/next.js/pull/97867 (which uncovered this bug
in this particular case with the default parameter assignment)
and
- https://github.com/vercel/next.js/pull/97697 (because previously
`unknown | WellKnownFunction` was just unknown. Now it stays `unknown |
WellKnownFunction`)

Fixes
```
./node_modules/.pnpm/@vue+compiler-sfc@3.5.26/node_modules/@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js:45756:20
Error: Module not found: Can't resolve 'less'
  45754 | );
  45755 | const less = (source, map, options, load = require) => {
> 45756 |   const nodeLess = load("less");
        |                    ^^^^^^^^^^^^
```

because basically
```js
const load = globalThis.foobar || require
const x = load("less");
```
would get replaced to
```js
const load = globalThis.foobar || require
const x = __turbopack_context__.require(191912); 
```
which would be wrong if `globalThis.foobar` isn't actually `require` at
runtime.

Instead, if we are inside an alternative, don't emit a codegen
replacement but only add reference
2026-09-09 16:46:27 +02:00
Aurora Scharff f8657de16c docs: clarify ISR with Suspense params (#98210)
## Summary

- warn that awaiting params inside Suspense without Partial Prefetching
prevents unlisted paths from using ISR
- clarify that route data may still be cached even though the
URL-specific route renders on each request
- link the migration warning to the ISR with Cache Components guide

## Why

This follows up on #98209. The migration eval showed that an agent can
preserve generateStaticParams but move params beneath Suspense while
Partial Prefetching is disabled. That produces a fallback shell, but
unlisted paths do not use ISR.

Context:
https://github.com/vercel/next.js/discussions/98177#discussioncomment-18252316

## Testing

- git diff --check
- Prettier check for both changed guides
- commit hooks: Prettier and ESLint
2026-09-09 14:34:25 +00:00
Sebastian "Sebbie" Silbermann 59b76e42ba [cd] Update peerDependencies in prereleases to accept the current prerelease (#98411)
https://github.com/vercel/next.js/pull/98330 revealed theat
`@next/third-parties` never worked with prereleases after 16.0. Now we
[update `peerDependencies` similar to what we do for preview builds
already](https://github.com/vercel/next.js/blob/27f679224e2660971377f855ced51093462734d8/scripts/create-preview-tarballs.js#L130-L132)
(we should unify both eventually to go through the same flow).

Fixes
```
  npm error code ERESOLVE
  npm error ERESOLVE unable to resolve dependency tree
  npm error
  npm error While resolving: undefined@undefined
  npm error Found: next@16.4.0-canary.22
  npm error node_modules/next
  npm error   next@"16.4.0-canary.22" from the root project
  npm error
  npm error Could not resolve dependency:
  npm error peer next@"^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0" from @next/third-parties@16.4.0-canary.22
  npm error node_modules/@next/third-parties
  npm error   @next/third-parties@"16.4.0-canary.22" from the root project
```
-- https://github.com/vercel/next.js/actions/runs/34291905918/attempts/2
2026-09-09 13:39:15 +00:00
Tobias Koppers 56b95f41a2 test: stabilize error recovery snapshot timing (#98414)
### What?

Stabilizes the pages-router `syntax > runtime error` acceptance test
without weakening its redbox assertions.

### Why?

The fixture emitted runtime errors every second and waited exactly one
second before capturing the first redbox. On slower release runners, a
second error could arrive while the asynchronous snapshot was being
collected, changing the expected single error into an array and causing
repeated retries.

The broader canary assertion cluster has several independent signatures;
this PR intentionally addresses only the timing race with a
reproducible, test-local cause.

### How?

The redbox matcher now performs the initial wait itself, while the
fixture uses a longer interval to leave a stable capture window. The
later wait remains long enough for another runtime error to occur,
preserving the test’s core assertion that a subsequent runtime error
does not replace the syntax/build error.

### Verification

- `pnpm build-all`
- `HEADLESS=true NEXT_TELEMETRY_DISABLED=1 NEXT_TEST_CI=true pnpm
test-dev-webpack test/development/acceptance/error-recovery.test.ts` (6
tests and 10 snapshots passed)
- Prettier and ESLint on the changed file

<!-- NEXT_JS_LLM -->

<!-- fleet ee9dd3ec-a271-4d0d-8165-d7872e226a6a -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 15:03:46 +02:00
Joseph 26b2bbe024 example: Use a Redis cacheHandler w/o dependencies (#95346)
Verify locally:

- Copy the example to a local directory
- Install deps
- docker compose up -d
- npm run build && npm run start 
- navigate to localhost:3000/cet (or gmt)
- open the Redis view (link in localhost:3000/cet)
- revalidate data on /cet or /gmt
- verify it on the Redis view

I had to update the app to async params, updateTag, and write the
handler in such a way that it solved:

```
⨯ TypeError: p.segmentData.get is not a function
    at ignore-listed frames
⨯ Error: failed to pipe response
```

Also adding docs edits with this caveat.

An agent review, and cross checking with a community implementation,
surfaced, that we had to read tags from
`data.headers['x-next-cache-tags']`. This agent review added a few
comments, I think they are useful, but can cut down if needed.

Last but not least, had to update the Time API endpoint (former no
longer worked).
2026-09-09 14:42:25 +02:00
Niklas Mischkulnig 3c8e9c3855 Support durable use-cache entries with client components (#98140)
The current setup is:
1. A use-cache function returns an RSC value that may contain client
reference proxies. Flight serializes each reference using the client
module `id`, export `name`, client `chunk` list, and `async`. The
resulting Flight payload is stored in the cache entry.
2. When decoding the cache entry, the client module `id` is resolved
through `rscModuleMapping` to obtain the corresponding RSC module `id`
and `export`.

Cache decoding does not need the client chunk list because it does not
load client chunks. The outer RSC serialization emits the client
reference again using the current client reference manifest, including
the current chunks. We can therefore serialize cached references with an
empty chunk list.

The client module `id` is also unstable across builds. Instead, cache
entries can store the stable client reference name. When decoding, we
resolve: client reference name -> current client module ID -> current
RSC module ID
This makes cached client references independent of build-specific module
IDs and chunk filenames.
2026-09-09 14:42:15 +02:00
next-js-bot[bot] f593b6c80a Upgrade React from f4e439e1-20260902 to 6c0e1047-20260908 (#98363) 2026-09-09 13:30:37 +02:00
Anuj Bolewar aad56b54f3 fix: preserve repeated --require/--import flags when forking workers (#96651)
Repeated --require/--import flags in NODE_OPTIONS are corrupted when
Next.js reparses and reformats the options for forked workers.

## What changed

- Declare `require` and `import` as repeatable options in
`parseNodeArgs` so `parseArgs` accumulates every occurrence into an
array instead of keeping only the last value (equals form) or joining
the space form into one bogus specifier.
- Emit one flag per array element in `formatArg`/round-trip.
- Widened `NodeOptions` and `getParsedDebugAddress` to accept
`string[]`.

## Why

Two preloads in `NODE_OPTIONS` (e.g. an APM agent plus an env loader)
silently lose one in forked workers (`--require=a --require=b`), or
crash the build (`--require a --require b`, joining paths into a single
MODULE_NOT_FOUND). Node itself runs every occurrence; the round-trip
through Next must preserve them.

Fixes #96582

## Validation

- Unit tests in `packages/next/src/server/lib/utils.test.ts` cover both
spellings, repeated `--import`, quoted values, and array formatting (22
passing).
- `pnpm --filter=next types` clean.

Signed-off-by: Anuj Bolewar <bolewara@gmail.com>
2026-09-09 11:59:51 +02:00
Tobias Koppers e44c5f192c ci: check that Turbopack compiles for wasm (#97588)
### What?

Adds a CI job that checks Turbopack compiles for
`wasm32-wasip1-threads`, so wasm portability
regressions are caught rather than rediscovered.

### Why?

Everything under `#[cfg(target_family = "wasm")]` is invisible to host
builds **and** to host clippy —
it is only checked when you deliberately build for the target. Four
separate defects in this stack were
caught only that way (a wrong build-script condition, two bad imports,
and a value that compiled but was
wrong). Without a gate, the next one lands unnoticed.

This also re-enables coverage that had been off for ~2 years: the old
`test-next-napi-bindings-wasi` job
was disabled with `if: false` pending napi-rs/napi-rs#2009, which closed
in April 2024.

### How?

Modelled on `rust-check` via `build_reusable.yml` (`needsRust`,
`skipInstallBuild`, `skipNativeBuild`),
so it does not pay for a JS build. Beyond `rustup target add` it needs
two things:

- **a WASI C toolchain**, because `lzzzz` (LZ4, via `turbo-persistence`)
and `zstd-sys` have C build
scripts. The SDK build is selected from `$RUNNER_ARCH` —
`build_reusable.yml` defaults to an arm64
runner, and an x86_64 clang fails there with `Exec format error` — with
a pinned sha256 per arch,
  unpacked under `$RUNNER_TEMP` so the workspace stays clean.
- **emnapi**, because `next-napi-bindings`' build script calls
`napi_build::setup()`, whose wasi path
panics without `EMNAPI_LINK_DIR` — so it is required even for `cargo
check`. It is installed into a
scratch directory rather than the root `package.json`, because this job
runs with `skipInstallBuild`
  and therefore never runs `pnpm install`.

Two things worth recording, both of which cost a CI round trip to find:

- pnpm is invoked from the repo root with `--dir`, not by `cd`-ing into
the scratch directory: corepack
resolves the pnpm version from the nearest `package.json`, and outside
the repo it picks the latest
  pnpm (11.x), which cannot run on the pinned Node 20
  (`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`).
- the job is added to `tests-pass`, whose `needs:` list is what actually
blocks a PR — a job that runs
but is absent from that list looks like coverage while blocking nothing.

The `emnapi@2.0.0-alpha.4` pin is deliberate and is the one fragility
here: the archive must define
`emnapi_create_env` / `emnapi_delete_env`, which exists only in emnapi
v2, still a prerelease. Move to
the stable release once it ships.


<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 10:25:45 +02:00
Tobias Koppers 73a26bfc84 feat(next-napi-bindings): build Turbopack for wasm (#97586)
### What?

Builds Turbopack for wasm, so the wasm fallback can support Turbopack
and not just SWC.

### Why?

`next-napi-bindings` gated every Turbopack dependency behind
`cfg(not(target_arch = "wasm32"))`, so the
wasm fallback shipped SWC only. That is the reason issues like #96960
have no answer today: when the
native binding cannot load there is no Turbopack at all.

### How?

Moves the Turbopack dependencies into the shared dependency block,
leaving genuinely native-only crates
gated:

- `lightningcss` / `turbopack-lightningcss-napi` and
`swc_ecma_react_compiler` back the `css` and
  `react_compiler` modules;
- `swc_plugin_backend_wasmtime` cannot run inside wasm;
- SWC's plugin-host features, gated in the previous layer;
- `tokio`'s `full` feature pulls in `process` and `signal`, which do not
exist on wasi, so the wasi build
  gets its own `tokio`.

This is the layer at which `swc` first gets compiled for wasm, which is
why the plugin-host gating has
to land **before** it: with that in place, `cargo check -p
next-napi-bindings --target
wasm32-wasip1-threads` passes here with no `[patch.crates-io]` override.

### What this does *not* do

It makes Turbopack **compile** for wasm. It does not make it run: still
outstanding are the
`env.read_custom_section` loader hook, a wasm `module_init` for the
tokio runtime and panic hook, a
host-provided fetch, the persistent cache (WASI preview1 has no
file-backed mmap or flock), and file
watching. Shipping is separately blocked on the unreleased emnapi v2.

`turbo-tasks-malloc`'s `custom_allocator` feature also stays off, so
there is no per-thread allocation
accounting on wasm and memory figures are absent from traces — an
observability gap, not a correctness
one.


<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 10:25:45 +02:00
Niklas Mischkulnig 8b037a6748 Add CSP nonce to script tags of loading and template files (#98398)
Recreation of https://github.com/vercel/next.js/pull/98152 so that
deployment test credentials are available in CI
Closes #98152
Closes https://github.com/vercel/next.js/pull/92803
Original description:

> `createComponentStylesAndScripts` renders the script tags for a
segment's loading, error, and templatefound files, but unlike
getLayerAssets it did not pass `ctx.nonce`. When such a file has a chunk
that no layout or client reference has already loaded, the tag is served
without a nonce and a nonce-based Content-Security-Policy blocks it.

### What?

Patches a bug we encountered while using
[nonces](https://nextjs.org/docs/app/guides/content-security-policy#nonces)
for a CSP with `strict-dynamic`, as in the docs example. In Turbopack,
when a `loading.tsx` (or `error` or `template`) file has a chunk that
isn’t already loaded by a layout or client component on the page, Next
renders a `<script>` tag for that chunk with no `nonce`. (the same chunk
script would’ve gotten a `nonce` if it were emitted for a layout or
page).

Supersedes #92803; this is the same fix but with a new test included. 

### Why?

While building out our `loading.tsx` pages, we encountered browsers
blocking a script and reporting CSP violations, because chunk scripts
did not properly carry a `nonce`.

### How?

One-line change to bring `createComponentStylesAndScripts` to parity
with `getLayerAssets`.

---------

Co-authored-by: Luke Taylor <luket@anthropic.com>
2026-09-09 10:13:05 +02:00
Tobias Koppers 49dc4c84b4 Turbopack: fix output tracing include glob traversal (#98382)
### What?

Make Turbopack's `outputFileTracingIncludes` file patterns root-relative
and targeted during output tracing. Reject unanchored partial globs when
code attempts directory-prefix matching. Add production coverage using a
real pnpm-installed WASM package, plus unit coverage for anchored
directory pruning and include-pattern expansion.

### Why?

Turbopack previously treated include file patterns as unanchored partial
matches. The directory-prefix matcher could therefore accept every
descendant after matching the first literal segment: an exact WASM
include under `node_modules/.pnpm` caused 2,093 `read_glob_inner`
executions instead of traversing only its 5 directory prefixes.

Unanchored patterns also cannot soundly answer whether a directory may
contain a future match, so allowing `contains: true` with
directory-prefix matching risks both over-traversal and missed files.
Include behavior also differed from webpack and the documented
project-root-relative semantics.

### How?

Build anchored alternatives for each include pattern: one for the direct
match and one for descendants of a matched directory. Anchoring lets
`read_glob` prune unrelated directories, while the recursive alternative
preserves existing behavior for matched directories and directory
symlinks, including their resolved target contents.

`can_match_in_directory` now fails immediately for `contains: true`,
while direct partial matching remains supported. Route-key and exclusion
matching continue to use direct partial matches and are unchanged.

The production fixture resolves
`lightningcss-wasm/lightningcss_node.wasm` from a real
`lightningcss-wasm@1.28.2` dependency, avoiding assumptions about pnpm's
store naming or patch hashes.

### Verification

- `CARGO_INCREMENTAL=0 pnpm build-all`
- `cargo fmt -p turbo-tasks-fs -p next-api -- --check`
- `cargo test -p turbo-tasks-fs glob` (84 passed)
- `cargo test -p next-api` (17 passed)
- `pnpm test-start-turbo
test/production/app-dir/output-file-tracing-includes-read-glob/output-file-tracing-includes-read-glob.test.ts
test/production/build-trace-extra-entries-turbo/build-trace-extra-entries-turbo.test.ts
test/production/build-trace-extra-entries-monorepo/build-trace-extra-entries-monorepo.test.ts`
(3 passed)
- `pnpm test-start-webpack
test/production/app-dir/output-file-tracing-includes-read-glob/output-file-tracing-includes-read-glob.test.ts`
- The shared fixture asserts that both bundlers include a root-relative
exact match and exclude a nested file with the same suffix
- Measured the exact reproduction at 5 include-specific
`read_glob_inner` executions, down from 2,093

<!-- NEXT_JS_LLM -->


<!-- fleet b3cefb44-e1ce-4bd6-83a9-da2f22848cad -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 09:39:19 +02:00
Josh Story 4cb547213b test: support deploy exclusions with force gates (#98149)
## Summary

- add static gate conditions for the deployment CI variants used by
existing test suites
- support `@force-gate` on parameterized `describe.each` suites
- document and test the force-gate infrastructure needed by the
migration

This establishes the gate support used by the deployment-exclusion
migration PRs above it.

## Verification

- `pnpm typescript`
- `pnpm jest test/unit/gate/pragma-transform.test.ts
test/unit/gate/runtime.test.ts --runInBand`

<!-- NEXT_JS_LLM -->
2026-09-08 19:41:08 -07:00
Spyros Ioakeimidis 2b11d01a8c Fix: Turbopack CSS HMR error when editing an unmounted merged CSS chunk (#94551)
### Fixing a bug

- [x] Related issues linked using `fixes #74749`.
- [x] Tests added (`test/development/css-hmr-unmounted-chunk`)
- [x] N/A — removes an erroneous thrown error

### What?

In `next dev` with Turbopack, editing a `.module.css` whose component
had unmounted threw `Error: No link element found for chunk
.../components_<hash>._.css` as an unhandled rejection. The edit
**silently failed to apply** — you had to reload the page manually to
see it.

### Why?

`applyChunkListUpdate` handles chunk updates asymmetrically: `'added'`
calls `loadChunkCached` (loads if missing), but `'total'` calls
`reloadChunk`, which is rejected when no `<link>` exists. When a lazy /
`dynamic(ssr:false)` component unmounts, `unloadChunk` removes its
merged-aggregate `<link>` but the chunk list stays subscribed; a later
CSS edit emits a `'total'` update for the now-unlinked aggregate →
`reloadChunk` rejects → the `'total'` branch discards the promise →
unhandled rejection, and the edit never applies.

### How?

`reloadChunk` now loads the fresh stylesheet when no `<link>` is present
(mirroring the `'added'` branch) instead of rejecting, so the edit
applies without a manual reload. Regression test reproduces with a
minimal `dynamic(ssr:false)` + 3 `React.lazy` routes fixture (red before
— the error is thrown and the edit doesn't apply; green after).

Fixes #74749

<!-- NEXT_JS_LLM_PR -->

---------

Co-authored-by: Benjamin Woodruff <benjamin.woodruff@vercel.com>
2026-09-09 01:37:30 +00:00
Benjamin Woodruff ‮ 02a429ae7c Dev server: More aggresively scold users for deleting .next when we detect it while dev is running (#98253)
Feedback:
https://vercel.slack.com/archives/C03KAR5DCKC/p1788491653257349?thread_ts=1788490509.032699&cid=C03KAR5DCKC

Message was added in https://github.com/vercel/next.js/pull/92135

We can't detect this in most other cases where users (or agents tbh)
delete `.next`, but in this case we can.
2026-09-09 01:03:03 +00:00
Tobias Koppers 705b326902 turbo-persistence: add TURBO_PERSISTENCE_MMAP=0 to disable mmap (#97873)
## What?

Add `TURBO_PERSISTENCE_MMAP=0` to disable memory-mapped persistence I/O.
The default remains mmap; file mode reads SST blocks positionally, keeps
block offsets in memory for random access, and reads meta/blob data
without creating mappings.

This is **1/3** in a dependent PR series.

- Next: #97888

## Why?

Some environments cannot use mmap reliably or have constrained virtual
address space. The flag provides an opt-out without changing the on-disk
format or penalizing the default mmap path.

## How?

- Thread a cached `AccessMode` through database configuration.
- Select mmap or file-backed storage independently for SST lookup,
compaction, metadata, and blobs.
- Keep canary's existing `ArcBytes`/`RcBytes` split, zero-copy mmap
filters, CRC-once bitmap, fast big-endian readers, mmap cache-bypass
behavior, and key-order handling for blocks that omit hashes.
- Exercise lookup, restore, compaction, metadata, and blob behavior in
both modes.

## Verification

- `cargo fmt --all -- --check`
- `cargo check -p turbo-persistence --all-targets --all-features`
- `cargo check -p turbo-tasks-backend`
- `cargo check -p turbo-persistence --benches`
- `cargo test -p turbo-persistence --all-features` — 109 passed

<!-- NEXT_JS_LLM -->


<!-- fleet 088efa77-2680-49f1-8941-92bb5f92b169 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 02:01:24 +02:00
Tobias Koppers e953bdaaaa Fix dev route discovery startup race (#97920)
### What?

Fix a dev-server startup race that could temporarily publish an empty
Pages Router route set when Watchpack's first filesystem aggregation
arrived before its route-directory scan completed.

The dev bundler now snapshots the initial `pages/` and `app/` files
before starting the watcher. During startup it ignores Watchpack
aggregations that are missing an existing snapshot page file, so it does
not publish route definitions or errors from a partial filesystem view.
Empty route directories are detected from the same snapshot rather than
from a separate callback that could race or mistake a read error for an
empty directory.

The `next-head` e2e fixture now delays Watchpack's initial `pages/` scan
in development mode, deterministically covering the failing ordering
while preserving every existing head-tag assertion and non-dev test
mode.

### Why?

PR #97905's build-and-test run 32911367895, job 98006962799 (`test cache
components dev (6/6) / build`) exposed this as a flaky
`test/e2e/next-head/index.test.ts` failure. In the raw job log around
lines 3135–3305, retry 0 reported `Ready in 420ms` and then served `/`
as a 404 four times. The returned document contained the `_error` page
title (`404: This page could not be found`) and `pages__error` chunks,
so four head-tag assertions failed downstream. Retry 1 passed about nine
seconds later.

The first Watchpack aggregation could run route discovery before
`pages/index.js` appeared in `getTimeInfoEntries()`. That published no
page route definitions and released the request handler, causing `/` to
fall through to `_error` until a later aggregation repaired the route
table. This change waits to publish any initial route state until
Watchpack has a complete view, rather than adding request retries,
sleeps, or broader timeouts.

### How?

- Snapshot route directories once before watcher startup, tolerating a
directory removed during boot so Watchpack can report the removal
normally.
- Ignore incomplete startup aggregations until Watchpack observes every
snapshot page that still exists, with a 30-second safety deadline so an
unwatchable file cannot stall startup.
- Drive the empty-directory startup path from the snapshot, removing the
error-swallowing `fs.readdir` race.
- Delay the first Watchpack `pages/` scan in a separate dev-only
route-discovery case and assert that the injection fired. The focused
test reproduced `GET / 404` before the fix and returns `GET / 200` with
the fix.
- Preserve the original five `next/head` test cases unchanged; validate
the app/pages conflict path separately.

### Verification

- Exact CI environment: `__NEXT_CACHE_COMPONENTS=true
__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true
__NEXT_EXPERIMENTAL_SERVER_COMPONENTS_HMR_CANCELLATION=true
NEXT_EXTERNAL_TESTS_FILTERS=test/cache-components-tests-manifest.json
NEXT_TEST_MODE=dev IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm
test-dev-turbo test/e2e/next-head/index.test.ts` — 5/5 passed
- `IS_WEBPACK_TEST=1 NEXT_TEST_MODE=dev pnpm test-dev-webpack
test/e2e/next-head/index.test.ts` — 5/5 passed
- `pnpm --filter=next types`
- `pnpm --filter=next build`
- Prettier, ESLint, and `git diff --check`

<!-- NEXT_JS_LLM -->


<!-- fleet 4ac891b6-2d24-4103-95bf-9daf04359e3f -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 01:34:27 +02:00
next-js-bot[bot] 6cb63960b8 v16.4.0-canary.22 v16.4.0-canary.22 2026-09-08 23:21:08 +00:00
Jimmy Miller 1574d39ece Add version for loaders api (#98377)
I looked into it. This is webpack's current version and the apis we are
approximating.
2026-09-08 15:27:35 -07:00
Luke Sandberg 2994f6be40 turbo-tasks-backend: let a GC pass wind down when it is blocking an operation (#96676)
A GC pass holds total operation exclusion, so every invalidation, cell
update and child connect
blocks for its whole duration. In dev that is an HMR edit stalled behind
the cascade, and the pass
had no way to notice or yield.

This makes a gc pass interruptible so we are guaranteed to not stall too
much work. To ensure progress we have a minimum guaranteed execution
time `100ms`
2026-09-08 15:01:22 -07:00
Aurora Scharff 552dd3538d docs: preserve Next.js development state (#98276)
## Summary

Explain in the Building guide that `.next/dev` contains the active
development server state and incremental compilation caches. Deleting or
moving `.next` makes the development server recreate that state, while a
separate `distDir` provides isolated production output.

Make the `next-dev-loop` Skill explicit that moving `.next` to a backup
is still a reset. Keep the AI coding agents guide focused on discovering
and inspecting the running development server.

Update the agentic regression eval so it leaves two working routes and
their development server running before introducing an invalid
`generateStaticParams` signature. The production error points into
`.next/types`, but the source contains the defect. The eval
independently checks that the agent fixes the source, preserves `.next`,
and verifies the result through the existing development session.

## Eval results

| Model | Treatment | Result | Source fix | Preserve `.next` | Existing
dev verification |
| --- | --- | --- | --- | --- | --- |
| GPT-5.6 Luna | Baseline | 1/3 | Fail | Fail | Pass |
| GPT-5.6 Luna | Bundled Guide via `AGENTS.md` | 2/3 | Pass | Fail |
Pass |
| GPT-5.6 Luna | `next-dev-loop` Skill | 3/3 | Pass | Pass | Pass |

The baseline tried to clear `.next`, moved it aside when deletion was
blocked, and did not leave a valid source fix. The Guide treatment read
the relevant `generateStaticParams` API reference and fixed the source,
but still moved `.next` because it did not retrieve the general Building
guide. The Skill treatment read the explicit preservation guidance, kept
the original `.next` directory and development server active, used a
separate `distDir` for the production build, and verified both routes
through the running app.

## Verification

- `pnpm --filter=next build`
- Agentic eval with GPT-5.6 Luna: baseline, bundled Guide, and
`next-dev-loop` Skill
- Prettier and ESLint checks on the changed files
- `git diff --check`

<!-- NEXT_JS_LLM -->
2026-09-08 23:54:37 +02:00
Jimmy Miller afb0dbac6a Adds .resolve to loaders api (#98361)
This fully fixes handlebars loader in my tests and a number of other
loaders depend on this as well. The implementation for getResolve is a
bit different in turbopack, but this should be semantically the same.
2026-09-08 13:02:16 -07:00
next-js-bot[bot] 74d9f77566 v16.4.0-canary.21 v16.4.0-canary.21 2026-09-08 19:42:54 +00:00
Niklas Mischkulnig 84d55c5f34 Bump to swc v78 (#98268)
Notably, switches to the upstream React Compiler crates, as opposed to
the `forked-react-compiler`
2026-09-08 20:57:36 +02:00
Janka Uryga db66a5094d Create next.config.ts when generating apps with pnpm new-test (#98364)
Updates the template used by `pnpm new-test` for app-dir tests to create
a `next.config.ts` instead of `next.config.js`.
2026-09-08 18:38:36 +00:00
Aurora Scharff 4a34974ec2 test(evals): accept LayoutProps in App Router migration (#98365)
### What?

Updates the App Router migration eval to accept the generated
LayoutProps helper as valid typing for root layout children.

### Why?

The current docs support both an inline ReactNode annotation and the
globally available LayoutProps helper. Opus 5 followed that guidance in
all four AGENTS.md runs, but the eval rejected LayoutProps and reported
a false regression.

### How?

The assertion still requires children and now accepts either documented
type form.

### Testing

- Prettier check
- ESLint
- Focused assertion check covering both valid forms and an untyped
invalid form

<!-- NEXT_JS_LLM -->
2026-09-08 18:00:59 +00:00
Hendrik Liebau c11c0942f2 [test] Run deploy tests against local JavaScript changes by default (#98353)
Deploy tests now use the checkout's built JavaScript packages when
`NEXT_TEST_VERSION` is unset. The harness reuses the isolated-test
packer, includes the tarballs in the deployment source, and installs
them through relative `file:` references.

The previous published-build workflow remains available through
`NEXT_TEST_VERSION`. For example, `NEXT_TEST_VERSION=canary` skips local
packing and tests the published canary. Exact versions and CI preview
tarball URLs remain supported. Existing CI deploy jobs already set
`NEXT_TEST_VERSION`, so their package selection does not change. The
harness skips local package preparation when it reuses an existing
deployment or invokes a custom deploy script, preserving those
workflows.

Staged monorepo peer dependencies use matching local versions so npm
accepts prerelease builds without conflicting direct-package overrides.
Fixture overrides retain their nested rules, including qualified
identity overrides with or without an explicit `.` entry. Configuration
updates preserve linked source files.

Deployments of local JavaScript packages use published SWC binaries
matching the local Next.js version. They do not include locally built
native binaries because those may target a different platform.

**Verification**

```
VERCEL_TURBOPACK_TEST_TEAM=vtest314-next-turbo-e2e-tests \
  pnpm test-deploy-turbo test/e2e/app-dir/third-parties/basic.test.ts
```

```
VERCEL_TEST_TEAM=vtest314-next-turbo-e2e-tests \
  pnpm test-deploy-webpack test/e2e/app-dir/third-parties/basic.test.ts
```

Both deploy modes passed. Additional temporary checks, removed
afterward, verified that:

- Deployments install the local JavaScript packages and use matching
local versions for monorepo peers.
- Qualified npm overrides preserve a nested `sharp@0.34.5` pin, both
with and without an explicit `.` entry. The check resolved `sharp` from
Next's own dependency context.
- Archive preparation leaves checkout manifests and tarballs unchanged.
Repacked archives preserve other package files, permissions, links, and
external peer declarations.
2026-09-08 18:49:13 +02:00
Sebastian "Sebbie" Silbermann 662b0355eb [test] Resolve workspace:* packages to the version under test (#98330)
Test fixtures that depend on monorepo packages (for example
`@next/third-parties` and `@next/mdx`) declared them at the `canary`
dist-tag, which installs the published npm canary instead of the build
under test. In deploy tests this also broke the remote install entirely:
the published canary's `peerDependencies` ranges (for example
`^16.0.0-beta.0`) reject the prerelease preview versions that
`NEXT_TEST_VERSION` installs for `next` (for example
`16.4.0-preview-<sha>-<date>`), so `npm install` failed with ERESOLVE.

Test dependencies can now be declared as `workspace:*`, which resolves
to the build from the current checkout in every test mode:

- dev/start: the locally packed tarball from `pack-for-isolated-tests`,
with a descriptive error when the package has no pack task or is not
part of the repository.
- deploy: the preview tarball of the tested commit when
`NEXT_TEST_VERSION` points at a preview build (preview tarballs already
rewrite their monorepo peer dependencies to the same preview URLs, so
peer resolution succeeds), falling back to the version in the worktree
otherwise. Private packages without published preview tarballs fail with
a descriptive error.

All existing tests that used `canary` for monorepo packages are migrated
to `workspace:*`.
2026-09-08 18:17:29 +02:00
Luke Sandberg 97e7d901c8 turbo-tasks: explicit GC root anchoring + cross-session orphan reclamation (#96857)
### What?

Reworks how the reference-counting GC decides what is a **root** (a task
that must not be collected), and adds a durable path to reclaim
cross-session orphaned roots

Currently GC roots are persisted and the only way to get collected is
for another session to revive them, and then for their ref-counts to
drop. This provides a new mechanism.

1. **In-session** — Top level operations get reference counted so they
can be dropped in session.
2. **Cross-session** — live roots are recorded along with a simple aging
mechanism so they can still be collected even if a later session never
restores it.

### Why?
This closes the final GC gap. Top level operations are ambiguous
currently and need tracking. With cross session aging we close a basic
gap where if you delete a route between sessions we never clean up the
tasks. Now it will eventually age out.

### How?

**Roots are anchored explicitly**. Via pins and RAII guards like
`GcRoot`

**Persisted roots set + TTL age-out** A new Infra-keyspace `GcRoots`
entry persists `Vec<(TaskId, ttl)>`. That allows us to detect when roots
are stale and can be dropped.

**Dispose transient root tasks on drop.** `RootTask::Drop` now disposes
tasks and cleans up references allowing the GC to work
2026-09-08 09:03:09 -07:00
Janka Uryga b0b7a429b9 [PPF] Fix missing runtime-follow up if hints are stale (#98278)
if a page didn't use runtime data during build but started doing so
after the revalidation (i.e. the hints say its statically-prefetchable
but now it's not), we're supposed to be doing a follow-up runtime
request based on the `needsRuntimeRequest` promise (introduced in
#96095), but this was not actually happening.

the bug goes as follows:
1. if we get an entry from a static prefetch, but `needsRuntimeRequest`
is true, we save it as `PPR` (or `StaticShell` if it's a shell rewound
from the static response). otherwise, we'd have saved it as `PPRRuntime`
(or `RuntimeShell` for shells) to indicate that it satisfies those
requests.
2. `wouldRuntimeRequestProvideMore` compares the runtime strategy for
the current static walk to the entry's strategy to check if it should do
a runtime request. this boils down to a comparison of `RuntimeShell >
PPR` for a shell request
3. however, in the `FetchStrategy` enum, `RuntimeShell = 2` and `PPR =
3`, so `RuntimeShell > PPR` is false, which makes
`wouldRuntimeRequestProvideMore` return `false`. so, if we have a `PPR`
entry, **we'll never do a follow-up runtime shell request**.

This is fixed by swapping the order of the two values in the enum.

Now, if we end up with a `PPR` response where `needsRuntimeRequest` is
true due to runtime data, that indicates that we *should've done a
runtime request in the first place*, we just didn't know we should do so
because the prefetch hint was stale, so the new ordering achieves the
correct behavior, i.e. `pingSegmentBundle` decides that it needs a
runtime shell request, which makes it match the intended PPF prefetching
behavior.

The enum ordering is a bit of a lie, because there is no single correct
ordering -- a `PPR` entry *might actually provide more content* than a
`ShellRuntime` entry if the route uses static params. In practice this
is resolved by the vary path (shell responses are keyed with fallback
params, and a PPR entry with static params would have the actual param
values, and the param-ful entry would end up being preferred) so the
strategy order wouldn't matter.

Now that `RuntimeShell` beats `PPR`, i had to disable the strategy
upgrading (`StaticShell -> RuntimeShell` and `PPR -> PPRRuntime`) on
routes that don't use Partial Prefetching -- otherwise, a
rewound`ShellRuntime` entry from a different param was winning over
`PPR` in `pingSegmentBundle`, so it was preventing issuing new static
requests even if we needed to do them. However, without PPF, the runtime
tiers are not meaningful anyway, so this should be fine.

### Other bugs

This fix also revealed a bug where `FetchStrategy.Full` requests were
writing a shell as `RuntimeShell` even though the byte offset only marks
a *static* shell. I've refactored the logic for picking the strategies
to fix this and make it more explicit.

### Edge cases

there is one (somewhat involved) edge case to this that i know of.
if the post-revalidation static response has:

1. a shell that didn't access runtime data (i.e. `needsRuntimeRequest =
false` in the shell stage)
2. a prefetch that did access runtime data (i.e. `needsRuntimeRequest =
true` in the static stage)

then we'll:

1. write the shell as `ShellRuntime` (because `needsRuntimeRequest =
false`, and a runtime shell would not in fact provide more data)
2. write the prefetch as `PPR` (and not `PPRRuntime`, because
`needsRuntimeRequest = true`, and a runtime prefetch *would* provide
more data)

with the new ordering, this means we'll end up preferring the
`ShellRuntime` entry over the `PPR` entry even though the shell is a
strict subset of it and would be missing static params and content gated
behind `prefetch()` or `navigation()`. (note that this only happens if
they share the same vary path)

this can be observed as follows:
1. when a `<Link prefetch={true}>` to such a page is revealed, we won't
request a runtime shell (due to already having a `ShellRuntime` entry),
but we will request a runtime prefetch (because `PPR` is not enough). so
far this is correct
2. if the navigation happens **before** this runtime prefetch completes,
we will display what we already have. but we'll prefer the rewound
`ShellRuntime` shell entry over than the `PPR` entry it originated from
because of the enum ordering. so we may end up not showing content
despite having it in the cache

i'm not sure how to resolve this yet, but i think the current
`recordedFetchStrategy` trick can't handle this, and we need a more
involved mechanism that can detect when the `PPR` entry is actually
better. i don't consider this a blocker for this fix though
2026-09-08 16:22:18 +02:00
Janka Uryga f4449e8fa5 Instant validation for caches excluded from prerenders (#98342)
Follow-up to #98339. Turns out we were not testing how caches excluded
from static/runtime prerenders and app shells behave in Instant
Validation, and it was somewhat broken. This PR adds test coverage and
fixes some bugs I found along the way.

### Fixes

- `use-cache-wrapper` was incorrectly gating cache delays in "request"
stores on `NODE_ENV === "development"`, which does not include
build-time instant validation. The correct check is now implemented in
`isValidationRender`
- `use-cache-wrapper` has divergent behavior for caches with `stale <
MIN_SHELL_STALE` across app shells and PPR/static shells, which needs to
be tracked so that we know that the same render can't be used for both
Instant and Static Shell validation. When we see a cache entry like
that, we now call `trackIncompatibleShellContent()`
- A render that had a cache miss could still report that it's compatible
with both SSV and IV, because we only do the above for a cache hit. As a
result so we'd incorrectly reuse `LAZY_FULL_RENDER` for both. Cache
misses now result in a `trackIncompatibleShellContent()` call to avoid
this

### Tests

We now have tests for:
- `stale < MIN_SHELL_STALE` - excluded from app shells, but included in
static and runtime prefetches
- `stale < MIN_PREFETCHABLE_STALE` - excluded from all prerenders
- `expire < MIN_PRERENDERABLE_EXPIRE` - excluded from static prerenders,
but allowed in runtime prerenders

Due to bugs mentioned above, some of the added tests were failing before
the fixes (mostly the ones that expect an error -- passing a "no
validation errors" test is easy, just don't create any dynamic holes)

In build, these tests incorrectly reported no errors when they should've
failed IV:

- `invalid - unguarded non-prefetchable cache (with short stale)` (both
PPF and non-PPF)
- `non-app shell validation > invalid - unguarded non-prerenderable
cache with short expire` in build:
- `app shell validation > invalid - unguarded cache with a
shorter-than-shell staleTime` in build:

This is because we were missing cache delays in build-time instant
validation (now fixed with `isValidationRender`), so the caches weren't
dynamic holes at all.

The `stale < MIN_SHELL_STALE` tests (`app shell validation > invalid -
unguarded cache with a shorter-than-shell staleTime`) were also failing
in dev:
- **{initial load, client navigation} with cold caches**: Should be an
IV error, but is an SSV error. The initial render had cache misses, so
we did a warm-cache full rerender with runtime shells, which resolved
`await nonShellCache()` in the Runtime stage. But the initial render
**did not track incompatible data**, so we incorrectly re-used it for
SSV and IV. The cache was resolved in `Runtime` so SSV saw a runtime
hole and `await nonShellCache()` errored in SSV with `Next.js
encountered runtime data during prerendering.`
- **client navigation with warm caches**: same as above, except there
weren't cache misses, so incorrectly reused the *original* runtime-shell
render for SSV and IV
- **initial load with warm caches**: No redbox when IV should've
errored. The main render was an initial load and did not use runtime
shells, so it resolved `await nonShellCache()` in `PrefetchStatic`. We
had no cache misses and **did not track incompatible data**, so we
incorrectly re-used the main non-runtime-shell render for SSV and IV.
The cache resolved in `PrefetchStatic` and it wasn't a hole in
`ShellRuntime` so no error was reported.
2026-09-08 16:22:18 +02:00
Janka Uryga e974969ddd [PPF] Model caches with stale < MIN_SHELL_STALE as prefetch-only data (#98339)
Caches that have `stale < MIN_SHELL_STALE` were using
`makeStageHangingPromise` in prerenders, which (in theory) was meant to
track a runtime data access during a static prerender, which in turn
would make the page use runtime shells. This is semantically incorrect,
because both static and runtime shells would exclude the cache, and both
static and runtime prefetches would include it, so it does not actually
signal runtimeness, only prefetchness. The only thing we should be doing
for these caches is excluding the content, same as
`unstable_prefetch()`.

This PR replaces `makeStageHangingPromise` with
`makePrefetchHangingPromise`, which is not tracked as runtime data at
all. We also use it for `unstable_prefetch()` to signal the
correspondence.

Note that in practice, the usage of `makeStageHangingPromise` in
`use-cache-wrapper` was *technically* fine, because its runtime data
tracking was effectively dead code. We only returned it if
`stagedRendering.finalStage < stage`, but for static prerenders `stage =
staticLinkDataStage = PrefetchStatic` and `finalStage = Static`, so the
condition was never actually true, and we never actually tracked it as a
runtime data access. However semantically it should still not be tracked
as runtime data.
(The condition *can* be true in a runtime prerender that only prerenders
the shell. We don't do any tracking in runtime prerenders, so it was
also a no-op).

(this was extracted from #98129, where we separate runtime data access
tracking into session data and URL data. i couldn't figure which one
`makeStageHangingPromise` should be tracked as, but then i realized it
shouldn't be tracked at all)
2026-09-08 16:22:17 +02:00
Joseph aab9387128 docs: document excluding content from a prefetch in the prefetching g… (#98175)
Guide introduction of unstable_prefetch and unstable_navigation.
2026-09-08 16:17:14 +02:00
Joseph 68e3fb1d68 docs: add unstable_navigation and unstable_prefetch API references (#98122)
Adds API reference pages for the two render-interruption functions, both
marked `version: experimental`.

- `unstable_navigation()`, which keeps a subtree out of a route's
prefetches so it renders on the navigation.
- `unstable_prefetch()`, which keeps a subtree out of a route's App
Shell so it renders on a per-link prefetch or on the navigation.

Each page opens with the App Shell, per-link prefetch, navigation
ladder, states which boundary its function moves content past, and
cross-references the sibling function and the Adopting Partial
Prefetching guide.
2026-09-08 15:46:47 +02:00
Jimmy Miller 9d9e1d5b00 Fix turbopacks webpack loader setup to support options being optional (#98270)
If you look at the types for webpacks loaders, you can see that options
is optional.
https://github.com/webpack/webpack/blob/c17742f631eaeaf1ad880a70b105eedd96dd21b9/declarations/LoaderContext.d.ts#L83

The implementation between the two is slightly different so webpacks
impementation doens't jsut set it to {}. But with the way turbopack
works, this is the equivalent.

I saw a bunch of failures all related to this in real loaders, so figure
it was an easy quick fix.

(also extensionOptions is optional in webpack and needed to be here for
the the types to all checkout
https://github.com/webpack/webpack/blob/c17742f631eaeaf1ad880a70b105eedd96dd21b9/declarations/WebpackOptions.d.ts#L2175)
2026-09-08 06:38:33 -07:00
Sebastian "Sebbie" Silbermann 665d3458b6 [ci] Align build-and-deploy triggers with build-and-test (#98331)
Change detection (`git diff HEAD~1`) only covers the whole change set
when the checkout is the PR merge commit. `build-and-test` runs on
`pull_request` while `build-and-deploy` ran on `push` for upstream PR
branches, so a multi-commit push whose last commit was docs-only made
`build-and-deploy` skip the preview build while deploy tests in
`build-and-test` waited up to 30 minutes for a preview tarball that
never got published.

`build-and-deploy` now triggers on the same events as `build-and-test`
(`push` to the default branch and `pull_request`), while still accepting
release tags and workflow dispatch. The `deploy-target` fork guard is
removed since upstream PRs are no longer covered by a push run, and
`create-release-branch.js` now rewrites the workflow's branch list
alongside the existing `refs/heads/canary` replacement so release
branches keep their staging deploys.

Preview tarball creation now uses the PR head sha instead of falling
back to the merge-commit sha on `pull_request` `opened` events
(`github.event.after` only exists on `synchronize`), matching the sha
that `upload_preview_tarballs.yml` publishes under and that tests poll
for. This fallback was a pre-existing bug that only affected fork PRs;
it would have started affecting every upstream PR's first run once
previews moved to the `pull_request` event.
2026-09-08 10:55:44 +02:00
Tobias Koppers 7167297192 test: stabilize server action HMR update (#98322)
### What?

Stabilize the development-mode server action HMR test when an unrelated
Fast Refresh reload occurs while the patched action is being applied.

### Why?

The test retried by dispatching another click before the previous
asynchronous server action had settled. Under CI load, those requests
overlapped with HMR and an unrelated full-page Fast Refresh reload. The
reload reset the client counter, so the correctly updated action
returned exactly `1000`, which the test rejected because it assumed the
pre-reload state was preserved.

### How?

Wait for network idle after each action invocation so retries do not
observe an in-flight response, allow the valid post-reload value of
`1000`, and use a retry window that accommodates CI HMR latency. The
assertion still distinguishes the updated `+1000` action from the
original `+1` implementation without requiring client state to survive
an unrelated reload.

### Verification

- `pnpm test-dev-turbo
test/e2e/app-dir/actions/app-action-node-middleware.test.ts -t "should
support updating the action"` (10 consecutive passes)
- `pnpm test-dev-webpack
test/e2e/app-dir/actions/app-action-node-middleware.test.ts -t "should
support updating the action"` (4 consecutive passes)
- Prettier and ESLint on `test/e2e/app-dir/actions/app-action.test.ts`

<!-- NEXT_JS_LLM -->


<!-- fleet 5793f439-a569-44be-ad19-d456ce9b641c -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-08 10:40:35 +02:00
next-js-bot[bot] 969733cfb7 v16.4.0-canary.20 v16.4.0-canary.20 2026-09-07 23:21:53 +00:00
Luke Sandberg 3453f7b6ac test(turbopack): remove dead top-level task unmarks (#98246)
## Summary

Remove stale
`unmark_top_level_task_may_leak_eventually_consistent_state()` calls
from tests and a benchmark that already use strongly consistent
operation-root reads.

## Verification

- `cargo test -p turbo-tasks-backend --test detached --test eviction`
- `cargo check -p turbo-tasks-backend --benches`
- `cargo fmt --all -- --check`

<!-- NEXT_JS_LLM -->

<!-- fleet 475180cd-06a7-4669-89b5-a2029a3a18c9 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
2026-09-07 14:40:08 -07:00
Tobias Koppers e9c16094f2 Fix flaky successive HMR changes test (#97610)
### What?

Correct the fixture marker used to construct the second invalid source
state in the successive App Router HMR test.

### Why?

The test was replacing a marker that does not exist in its MDX fixture.
Because string replacement silently returned the original content,
supposedly successive patches sometimes wrote byte-identical source
while the test harness waited for an HMR completion callback.

A no-op write does not have to produce an HMR update, so the callback
could remain pending until the harness reported a misleading timeout. CI
logs showed Fast Refresh itself completing quickly, which ruled out an
insufficient timeout budget and made extending the timeout the wrong
fix.

### How?

Match the replacement to the fixture's actual marker. This restores four
genuinely distinct source states in every error/recovery cycle,
preserving the intended overlay coverage without changing Turbopack, the
shared HMR helper, or its timeout.

### Verification

- `pnpm test-dev-turbo
test/development/acceptance-app/app-hmr-changes.test.ts`
- Repeated the focused test three times under 14 CPU-contention workers
- Prettier and ESLint on the changed test file

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-07 15:27:50 +02:00
Joseph 6ef6e29db5 docs: Remove version label from Turbopack in Pages Router (#98316)
Remove the experimental flag from
https://nextjs.org/docs/pages/api-reference/config/next-config-js/turbopack
2026-09-07 11:43:50 +02:00