mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
codex/catchall-fallback-values
35737 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
086294e2e5 |
docs: add layout stability guidance for videos and iframes (#98070)
## Summary - Document how dimensions and poster images keep video layouts stable while media loads. - Add equivalent sizing guidance for lazy-loaded iframes. ## Verification - `prettier --check docs/01-app/02-guides/videos.mdx` <!-- NEXT_JS_LLM_PR --> |
||
|
|
a37a86acf0 |
tests: Improve use-cache-cross-deployment tests (#98049)
Followup to #95233 - unskip one client component test since that is actually expected behavior already, we just need to test the observable behavior instead - Fix instant insights warnings - Don't use console.log since that errors when trying to print server references |
||
|
|
2fe6f962a1 | v16.4.0-canary.12 v16.4.0-canary.12 | ||
|
|
7421a868ad |
[ci] Remove the popular workflow and its action (#97753)
The `popular` workflow posted weekly Slack digests of the most-reacted issues, PRs, and feature requests. That reporting is now handled by the Next.js maintainer agent, so this PR deletes the workflow along with its backing `next-repo-actions` action (sources and checked-in ncc bundles), which had no other consumer, and regenerates the `.github` lockfile without the action's dependencies. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
33a5d542e5 |
Store keys in key order in SST blocks that omit hashes (#97480)
## What Key blocks for short keys (≤ 32 bytes) don't store a per-entry hash, but were still sorted by `(hash, key)`. That forced every binary-search probe to recompute the entry's xxh3 hash just to compare it — roughly ten hashes per lookup. These blocks now store their entries in **key order**, so the search compares key bytes directly and hashes nothing. All the families `turbo-tasks-backend` produces hit this case, so this is the common path in practice. ## Why this is safe Hash-based routing is unchanged. Files are still assigned by hash, the index block still routes to a key block by hash, and `min_hash`/`max_hash`, the AMQF, and compaction's coverage model all work exactly as before. Thus only the order *within* a hash-less block changes. This adds a small cost to writing, which now must re-sort blocks of keys and a double cost to compaction which now must re-sort blocks in hash order to iterate them, and then sort them back to key order when writing the new file. ## Benchmarks Measured against `canary`, baseline and comparison run back to back on the same machine. **Lookups ** All 16 `static_sorted_file_lookup` configurations improved | entries | hit/uncached | hit/cached | miss/uncached | miss/cached | | --- | --- | --- | --- | --- | | 1 Ki | -30.4% | -24.8% | -29.1% | -29.4% | | 10 Ki | -25.2% | -19.7% | -22.7% | -23.7% | | 100 Ki | -17.3% | -19.3% | -15.2% | -16.8% | | 1000 Ki | -7.0% | -25.7% | -7.0% | -10.9% | **Commits ** Short keys pay for the added per-block sort. | config | change | | --- | --- | | `key_8` 85Ki entries | +3.3% | | `key_8` 853Ki entries | +3.1% | | `key_8` 8.33Mi entries | +8.5% | | `key_32Ki` / `key_4` (large-key configs) | -2.6% to -19.1% | Six of thirteen write configurations came out as noise (p >= 0.05) and are omitted. **Compaction** `StaticSortedFileIter` must yield `(hash, key)` order because `MergeIter` merges on it, so it reorders each hash-less block back into hash order. Plus the additional cost of the commit (above) | config | change | | --- | --- | | 4Mi entries / 8 commits | +6.0% (reproduced at +10.2%) | | 16Mi entries / 8 commits | +7.5% | | 4Mi entries / 32 commits | +7.8% | | 16Mi entries / 32 commits | +5.2% | | 16Mi entries / 128 commits | -9.5% | |
||
|
|
920a76735c |
Honor non-interactive mode in upgrade codemod prompts (#98030)
### Why? `@next/codemod upgrade --yes` accepts defaults for the upgrade command's prompts, but `next-request-geo-ip` asked its own deployment question inside `runTransform`. Because the non-interactive state was not forwarded, automated upgrades could stop at that nested prompt despite using `--yes`. ### How? Pass the resolved non-interactive state from `runUpgrade` into each transform. The geo/IP transform skips its deployment prompt in non-interactive mode while preserving the existing prompt for direct interactive runs. |
||
|
|
6fb98fae8a |
Transform .mjs files with Next.js codemods (#98029)
### Why? The shared codemod runner only passed `tsx`, `ts`, `jsx`, and `js` files to jscodeshift. As a result, config transforms that recognize the supported `next.config.mjs` format never received those files, leaving migrations such as `experimental.turbo` to `turbopack` incomplete. ### How? Add `mjs` to the existing jscodeshift `--extensions` argument. Existing parser selection and Next.js config detection already support this format. Unsupported `next.config.cjs` files remain excluded. |
||
|
|
0d4dc0c3a5 | v16.4.0-canary.11 v16.4.0-canary.11 | ||
|
|
a41e98e725 |
Fix optimistic routing for encoded dynamic params (#97948)
## Summary Fixes #97842 by canonicalizing encoded pathname parts before using them as dynamic segment cache keys in optimistic route predictions. This keeps predicted route trees consistent with server Flight data and prevents query-only navigation from remounting pages when regular or catch-all params contain encoded characters. Add coverage that verifies bfcache IDs and form state are preserved for `@` in a regular dynamic param and for `@` plus `%2F` in a catch-all param. ## Verification - Existing encoded-slash route-cache E2E suite in Turbopack and webpack production modes - Existing optimistic-routing E2E suite in Turbopack and webpack production modes - `pnpm test-types` - `pnpm types` - `pnpm prettier-check` - `pnpm lint-ast-grep` - `pnpm lint-language` - Changed-file Prettier and ESLint checks via the pre-commit hook Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> |
||
|
|
4d36801fdb |
Fix intercepted route params after Proxy rewrites (#97953)
## What? Preserve the final internal rewrite pathname in the RSC response when an earlier Proxy rewrite has already populated the rewritten-path header. This also adds an end-to-end regression assertion for a dynamic interception route behind a locale-injecting middleware rewrite. ## Why? The route resolver set the final marker-bearing pathname directly on the response, but router-server later reapplied the accumulated Proxy response headers. That restored the earlier markerless pathname. The client then parsed an intercepted dynamic param against that stale path and removed three real characters from the value. ## How? Mirror the final internal rewrite pathname into the accumulated response-header map. When router-server applies that map after route resolution, the last rewrite remains authoritative. Fixes #97939 ## Tests - Production interception regression suite: 4/4 passed - Development interception regression suite: 4/4 passed - Related production rewrite/interception suites: 43/43 passed - Next package build, targeted ESLint, Prettier, and git diff checks passed |
||
|
|
d8f95c12de |
[PPF] Fix navigation() in prospective runtime prerenders (#98000)
### Long story short Fixes an analogous bug to #95665, but affecting `prefetch()/navigation()`. Those two were accidentally always resolving in the **prospective** runtime prerender, so we were warming content that isn't needed for the final runtime prerender. We now guard them properly. Also, I replaced `PrerenderStoreModernRuntime.isSessionShell` with `finalStage` (the same stage we're gonna use for the final prerender), so we can include/exclude `navigation` properly -- the boolean only distinguished shell/prefetch, not navigation. ### Long story long We use runtime prerenders for three things: runtime shells, runtime prefetches, and embedding a prefetch in navigations. These are aborted after `ShellRuntime`, `Runtime`, and `NavigationRuntime` respectively (accessible in `stageController.finalStage`). If we're doing the **final** prerender for a shell, promises that would resolve in e.g. `NavigationRuntime` would never resolve, because we abort the prerender before we get there. However, the **prospective** runtime prerender has no `StagedRenderingController` and needs to model this separately (so that the same APIs hang in both). This was [previously done via `prerenderStore.isSessionShell`](https://github.com/vercel/next.js/pull/95665) which we checked in URL data apis i.e. `params/searchParams` and the metadata`pathname` to do this. `isSessionShell: true` meant they should hang because it's a shell, and `isSessionShell: false` meant they should resolve. However, I forgot about this case when implementing `navigation`/`prefetch`, and made them resolve unconditionally in the prospective prerender. This means we'd potentially warm content that would not actually be reached in the final prerender. In other words, ```ts await navigation() return <Expensive /> ``` would needlessly render `<Expensive />` during the prospective render but not in the final one. This kinda defeats `navigation`'s main purpose, i.e. avoiding expensive work. This PR fixes the bug by replacing `isSessionShell: boolean` with `finalStage: AdvanceableRenderStage`, which is set to the same value we put in `stageController.finalStage`. This lets us keep the logic of what hangs and what resolves relatively close in both prerenders. --- On a meta level, the fact that the same bug happened twice tells me that having a `StageRenderingController` that is sometimes `null` is error-prone, and we should figure out a better solution, but i'm gonna keep this fix targeted and figure out a more holistic solution in a follow-up |
||
|
|
357e514cf7 |
Revert "test: re-enable sync IO root coverage" (#98050)
Reverts vercel/next.js#97996 The fix does not seem to help with the flakiness. |
||
|
|
96b1b06d0b | Turbopack: drop rust analyzer skip annotation (#98047) | ||
|
|
7a15847525 |
Turbopack: add next_config.use_react_experimental getter (#98032)
Refactor to unify the multiple places where this computation was happening |
||
|
|
e3790aa911 |
More granular cache keys for use-cache entries (#95233)
Caveats: - I had to skip `react[-dom][/*]` and `private-next-rsc-server-reference` and `private-next-rsc-cache-wrapper` imports in the code hash and env-var tracking. Because those all end up pulling in app-page-turbo.runtime.prod.js which reads many env vars and would cause constant deopting. But this should still be correct. The code of these imports is included via the Next.js version, and no env vars should change the semantics of any of those imports. - Static env var reads are collected, but too dynamic accesses are silently ignored and don't lead to deopts (same goes for env var reads in native NAPI addons). This means that this cache reuse is not guaranteed to be 100% guaranteed to never lead to stale caches. - Code hashing and env var collection happens on a per-module basis. So if you put all use-cache functions into a single file and/or together with react components, then you will see extraneous invalidations. This will be fixed by either generally enabling module splitting for all of Turbopack, or by adding a special transform that does it for use-cache functions. Followups: - There are some env var static analysis gaps that will be immediate followups before broader testing. These are the various TODOs added in https://github.com/vercel/next.js/pull/95310 - Client components invalidation is very coarse grained right now (statically imports any client component anywhere -> deopt completely). Followup for the future. But the current setup is correct. This would just improve effectiveness further - Do this in dev as well. Currently there is no NFT at all in dev (for performance reasons) - Module splitting for more granular tracking - Include entropy when serializing server reference arguments for cache key Todo: - [x] Use implementation code hash (includes inlined env vars): from #94234 - [x] Include non-inlined runtime env vars: from #95310 - [x] Include client reference manifest (very coarse for now) - [x] Include Next.js version (for wire format, etc) - [x] This is now done for all use-cache entries now. Not just for `use cache: remote`. Is that the intended behavior? Yes - [ ] ~~if `NEXT_DEPLOYMENT_ID` is in the env vars. just deopt and don't care about stringifing and hashing the env vars~~ - [x] Is cache key size a problem? Currently you can get this: (values are always hashed) `CustomCacheHandler::get ["80e6f6560092f0078775e7e787c1c10ecf6dea0bc4",[],["d984fbaa996274737f3b59345a300a20","16.4.0-canary.5","__NEXT_NO_MIDDLEWARE_URL_NORMALIZE=undefined","NEXT_OTEL_PERFORMANCE_PREFIX=undefined","__NEXT_PRIVATE_ORIGIN=a04f4b9d6a8f42724740a480e9a2bc67c053dc04377be831c0e2c407a1422004","NEXT_PRIVATE_RESPONSE_CACHE_TTL=undefined","NEXT_PRIVATE_RESPONSE_CACHE_MAX_SIZE=undefined","__NEXT_CACHE_COMPONENTS=b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b","__NEXT_ROUTER_BASEPATH=undefined","__NEXT_MANUAL_CLIENT_BASE_PATH=undefined","__NEXT_INSTRUMENTATION_CLIENT_ROUTER_TRANSITION_EVENTS=undefined","__NEXT_APP_NAV_FAIL_HANDLING=undefined","__NEXT_GESTURE_TRANSITION=undefined","__NEXT_USE_OFFLINE=undefined","NEXT_DEBUG_BUILD=undefined","__NEXT_VERBOSE_LOGGING=undefined...]] [["_N_T_/layout","_N_T_/page","_N_T_/","_N_T_/index"]]` |
||
|
|
28195e9b75 |
[ci] Remove dormant code_freeze workflow (#97756)
Code freezes are now managed by toggling the existing `code freeze` repository ruleset in the repository settings, so the `code_freeze` workflow is no longer needed. It was also broken beyond repair: it targeted the legacy branch-protection API with a `CODE_FREEZE_TOKEN` secret that no longer exists instead of the rulesets the repository uses now, and it referenced a `releaseType` input that was never declared. This PR deletes the workflow and `scripts/code-freeze.js`, which had no other consumer. Note on the investigated alternative: resurrecting the workflow on top of the rulesets API with just `GITHUB_TOKEN` is not possible. Creating or updating repository rulesets requires the "Administration" repository permission, which the workflow token cannot be granted (`permissions: administration: write` is rejected as an invalid workflow, and even `write-all` gets `403 Resource not accessible by integration`). Verified empirically in https://github.com/eps1lon/github-actions-repository-dispatch-admin-permissions. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
5e390edb7f |
test: re-enable sync IO root coverage (#97996)
## Summary Re-enable the `sync-io-blocks-root` production suite now that #97900 waits for child stdio to close before capturing build CLI output. This reverts the temporary skip from #97986 so CI flake detection exercises the route-specific sync I/O diagnostics again. ## Verification - `HEADLESS=true IS_WEBPACK_TEST=1 pnpm test-start-webpack test/production/app-dir/sync-io-blocks-root/sync-io-blocks-root.test.ts` <!-- NEXT_JS_LLM --> |
||
|
|
bfc33e2bb0 |
[ci] Remove the unused wrong-issue-template workflow and action (#97749)
The `issue_wrong_template.yml` workflow closed issues labeled `please use the correct issue template`. That label was only ever applied manually by maintainers and has not been used since January 2025 (9 issues total, ever), so every recent run of the workflow was skipped. This change removes the workflow along with its backing `wrong-issue-template` action (source, checked-in ncc bundle, and build script). Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
1cb20a1d14 |
[image-optimizer] Refactor into lightweight transform module (#97988)
This PR is strictly a refactor, no logic should change. The idea is that we eventually want to run the image optimizer transform step in a child process. This PR refactors the code to make a new `transform.ts` that is lightweight and should reduce some of the overhead of loading a large module graph when spawning a new process. |
||
|
|
f889820802 |
[ci] Remove the stale-issue workflow (#97758)
The `issue_stale` workflow ran `actions/stale` once a day to mark issues with no activity in 545 days (~1.5 years) as stale and close them a week later, and to auto-close issues labeled `please add a complete reproduction` (2 days), `please simplify reproduction` (14 days), or `please verify canary` (14 days). Marking and closing issues based on inactivity is no longer wanted, so the workflow is deleted. The `STALE_TOKEN` secret has already been revoked. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
a18361694e |
[ci] Use presigned URLs to upload preview builds (#97922)
Follow-up to #97256, pairing with [vercel/vercel-packages#107](https://github.com/vercel/vercel-packages/pull/107). The token exchange endpoint no longer returns a client-upload token derived from a store read-write token; it presigns the upload URL with the deployment's own Vercel OIDC identity and returns the URL, pinning the pathname, expiry, size cap, and access and overwrite policy server-side. No store credential exists anywhere after this lands, and no Blob SDK is needed here for the new path — the script PUTs the tarball bytes to the URL directly. The client only uses presigned-URL. The server still supports client tokens until we landed this and synced the mirror. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
f6c48e199f |
Turbopack: allow get_definable_name to return a list (#97984)
Followup to #95310
Now we can correctly track env vars in cases like these were a
`JsValue::Alternative` is involved
```js
// An alternative (process|unknown).env
let p2
if (foo) {
p2 = foo
} else {
p2 = process
}
console.log(p2.env.FOO8)
```
The inlining logic behaves the same as before:
- If `get_definable_name` returns exactly one result, we can inline.
- Otherwise, we can't inline (previously this case returned `None`)
A bit of trivia why we need that boolean in the return type in the first
place (because conceptually it should be possible to just model it via
`JsValue::Alternative` anyway:
> but why is that a problem? if the value is reassigned, then there
should just an alternative, then get_definable_name returns multiple
values and it should still not be inlining
>
> Because module is a `FreeVar`, not a normal `Variable`.
>
> The assignment is stored separately in `VarGraph`:
>
> ```
> free_var_ids["module"] -> id
> values[id] -> assigned function
> ```
>
> But later reads still evaluate to:
>
> `JsValue::FreeVar("module")`
>
> The linker expands JsValue::Variable, but not JsValue::FreeVar.
Therefore there is no JsValue::Alternatives at this call site.
>
> Instead, `get_definable_name()` detects the graph entry and returns:
>
> `[Some((["module", TypeOf], true))]`
>
> That boolean is how reassignment is represented for free variables.
Cardinality remains one, so ignoring the boolean at
references/mod.rs:4007 incorrectly permits inlining.
>
> Actual `Alternatives` primarily represent multiple values assigned to
tracked variables. For free globals, the original ambient value plus
reassignment is represented by `FreeVar` plus `potentially_reassigned`.
No perf impact:
```
commit
|
||
|
|
027205875b |
[ci] Run flake detection and new deploy tests when merged and on backport branches (#97991)
The previous detection mechanism special-cased `canary` which lead to large diffs when backport branches ran CI. We stop special casing `canary` reducing complexity and giving us another flake-detecting attempt when the change is merged. That way we can safely enable flake detection on backport branches without having to test large diffs when PRs are merged targetting backport branches. New backport branches will no longer have flake detecton and new deploy test runs disabled. |
||
|
|
9a7061053e |
docs(examples): document env var handling in the Docker examples (#97968)
Improve with-docker example env var usage documentation. Closes: https://github.com/vercel/next.js/issues/97959 |
||
|
|
978a0809bb |
Turbopack: unify member and in handling (#97985)
Followup to #95310 Purely a refactoring. These functions were 99% identical |
||
|
|
cfc7da3049 |
Update outdated snapshots (#98011)
I ran `UPDATE=1 cargo nextest run -p turbopack-tests` 1. https://github.com/vercel/next.js/pull/97672 was merged 2. But https://github.com/vercel/next.js/pull/97676 was merged without rebasing, leading to outdated snapshots |
||
|
|
17a901a74f |
Guard filesystem reads against unresolved symlinks (#97902)
### What? Adds debug-only OS realpath validation to successful `DiskFileSystem` file and directory reads. When a successfully canonicalized path differs from the supplied path, the read returns a normal task error naming both paths. Fixes pattern/glob traversal and NFT tracing so physical filesystem access uses resolved paths while logical paths remain available for user-visible specifiers and complete symlink-chain recovery. ### Why? Reading through an unresolved symlink parent gives the same filesystem object multiple path identities. That can make Turbo Tasks dependency tracking and invalidation inconsistent and can produce invalid deployment ZIPs when NFT output contains files below unresolved links. The checks return errors rather than asserting because paths can disagree temporarily under eventual consistency. Propagating a task error avoids panicking a worker thread while still exposing invalid callers during development. ### How? The validation lives directly in `DiskFileSystem::read` and `DiskFileSystem::raw_read_dir`. It calls the OS canonicalization API inline instead of the Turbo Tasks realpath task, keeping the diagnostic out of the task dependency graph. The guard runs only after the OS read succeeds, so missing/non-directory probes preserve their existing behavior. `read_matches` resolves each physical directory immediately before enumeration while retaining logical `PatternMatch` paths. `read_glob` and `track_glob` now resolve their initial directory before enumeration. Symlinks discovered later through wildcard segments are also traversed through resolved targets. `ReadGlobResult` deliberately retains logical paths rooted at the supplied base, allowing consumers to call `realpath_with_links` and recover the complete symlink chain. Consumers follow that contract explicitly: - NFT includes expand each logical match with `realpath_with_links`, emit resolved files and every traversed symlink, skip resolved directory targets, and deterministically deduplicate/sort output. - `import.meta.glob` uses recursive logical keys as the source of user-visible requests, while module resolution follows and tracks symlinks. - the hash-glob example resolves returned logical paths before reading. Webpack-loader context dependencies are covered for both `path/to/symlink/inner/path/*` and `path/to/*/inner/path/*`. The loader fixture performs its directory read with Node `fs`, reports the directory using `addContextDependency`, and Turbopack tracks the resolved target. ### Verification - `cargo fmt -p turbo-tasks-fs -p turbopack-ecmascript -p next-api -- --check` - `cargo clippy -p turbo-tasks-fs -p turbopack-ecmascript -p next-api --all-targets` - `cargo test -p turbo-tasks-fs` (128 passed) - `cargo test -p next-api` (7 passed) - `cargo check -p turbo-tasks-fs --examples` - `import.meta.glob` symlink execution fixture (1 passed) - Nine targeted node-file-trace CI cases with `release-with-assertions` (9 passed) - `pnpm build-all` - `webpack-loader-fs` Turbopack dev e2e (1 passed) - `build-trace-extra-entries-turbo` Turbopack production e2e (1 passed) - twoslash Turbopack production, normal mode (4 passed) - twoslash Turbopack production, cache-components mode (4 passed) - `bench/heavy-npm-deps` Turbopack development smoke test (HTTP 200) ### Notes The disk guard is cross-platform, while its symlink-parent regression test is Unix-only, matching neighbouring symlink tests. On Windows, OS canonicalization can also normalize casing and 8.3 short names; a debug read using a non-canonical spelling will therefore return the same diagnostic error. <!-- NEXT_JS_LLM --> <!-- fleet 1d32e12c-f4ec-4f22-862a-c85f0005805c --> --------- 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> |
||
|
|
6f44fd34a1 |
Turbopack: enable export mangling by default in production builds (#97676)
Stacked on #97672 — enable export mangling by default on canary releases. ### What? Two defaults change: - **Next.js builds.** `experimental.turbopackMangleExportNames` is pinned to `false` for stable releases and left *unset* on canary, where Turbopack then defaults it from the build mode: on for production builds, off in development. Explicitly setting the option wins in either direction, so setting it to `true` in development is honoured. - **Turbopack's own execution test suite.** `TestOptions::mangle_export_names` defaults to `true`, so all 279 fixtures exercise mangling instead of only the handful that opt in. ### Why? The feature is verified by turbopack's own fixtures, targeted e2e suites, and a couple of bundle-size measurements — a narrow slice of what Next.js's test suites actually cover. Defaulting it on for canary puts every production-mode e2e and integration test through the mangled code path for real users of the canary channel, without committing stable users to it yet. Turning it on for stable is a separate, later decision that can stack on top of this once canary has soaked it. Broad exposure has already earned its keep several times over. Turning mangling on in CI and in the fixture suite surfaced bugs that no hand-picked suite had found, including a module-fragments helper handing out another module's exports value (#97672's `EcmascriptExports::borrowed()`), a client-reference proxy memoized per wrapper instance rather than per content, and a code-elimination bug for export-less modules (fixed in the follow-up PR). ### How? `defaultConfig` cannot see the build mode, so it only expresses the stable/canary split (`isStableBuild() ? false : undefined`) and `NextConfig::turbopack_mangle_export_names(mode)` supplies the mode-dependent default on the Turbopack side. This is deliberately *not* the shape of the neighbouring `turbopackSharedRuntime: !isStableBuild()`: mangling should not apply in development, and hard-forcing `false` there in Rust — as an earlier revision of this PR did — would silently ignore a user who asked for it explicitly. Mangling does **not** depend on minification. An earlier version of this PR gated it on `minify(mode)`, which was wrong and has been removed; `--no-mangling` is a minifier flag and does not affect export mangling. Verified with a real `next build` A/B on a small app: the option left unset mangles (959,174 B total emitted JS, 293,407 B gzipped) while explicitly setting it to `false` does not (966,519 B / 294,963 B gzipped), confirming both the default and the override. All 279 execution fixtures pass with the suite default flipped, with no fixture needing an opt-out. Note this layer does not change the `turbopack-emit-collect` snapshots on its own: making a module without re-exports split additionally requires the split trigger added in the follow-up PR, so those snapshots move there. This is the layer to revert if canary turns up problems specific to running with the option on by default; #97672 remains useful as an opt-in feature either way. <!-- NEXT_JS_LLM --> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com> <!-- fleet ecdfa248-cd54-41ac-b4a2-c9d49e2a67ee --> --------- 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> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com> |
||
|
|
a84bc8de2a |
Turbopack: mangle exported names for smaller bundle sizes (#97672)
### What? Adds export-name mangling to Turbopack, behind a new experimental option `experimental.turbopackMangleExportNames` (default `false`). It is independent of minification: `--no-mangling` is a minifier flag and does not affect it. When enabled, each ECMAScript module's *used* export names — including `default` and `__esModule` — are replaced by short keys in the emitted output, both where the module registers its exports and where every consumer reads them. Modules whose export names could be observed by user code keep their original names, decided per module. This is a reland of #89060 (on top of the already-merged #89406), originally written by Matt Mastracci, who is credited as a co-author on the commit. Stacked on top: #97676 flips the default to `true` on canary releases, so Next.js's own CI exercises the feature broadly before it is considered for stable. ### Why? Bundle size. A module's export keys exist only to link modules together: the producing module emits `{ someVeryLongExportName: … }` and every consumer reads `ns["someVeryLongExportName"]`. Both sides are generated by us, so as long as producer and consumer agree — and the name isn't observable from user code — the key can be a single character. Long export names are extremely common in real dependency graphs (icon sets, utility packages, barrel files), and each one is paid for once in the module that defines it and once per importing module. ### How? **Ported, not rebased.** `canary` is ~2500 commits past the original stack's base, and the files it touched were independently rewritten in the meantime (export-analysis refactor #92781, CJS analysis for scope hoisting #95826, the `module_fragments` subsystem #95978). A probe rebase produced 16 conflicting files on the first commit alone, so the original branches were used as a reference implementation — for intent, the identifier alphabet, and test coverage — and the feature was rebuilt on today's infrastructure. #89561 from the original stack (erasing the Next.js wrapper module types) is deliberately **not** part of this change; it turned out to be unnecessary, because those modules already declare whole-module export usage and therefore back off on their own. **The name table** (`references/esm/mangle/table.rs`) hashes each name into a table of all valid JS identifiers of the smallest length that fits the name set — 15 exports get single-character keys — and resolves collisions by open addressing. Hashing rather than assigning `a`, `b`, `c`, … is what keeps names stable: an unrelated edit elsewhere in the module doesn't renumber every other export, and a collision only perturbs its own cluster. Assignment happens in two passes: every name that is *already* a valid identifier at the chosen length keeps itself and reserves its bucket first, and only then is anything hashed — so an export called `a` keeps `a`, and nothing else can be assigned it. Both passes iterate in sorted order, so the mapping depends only on the set of names. A module with exactly **one** mangleable export is special-cased to a fixed key, `f`, rather than a hashed one. `f` is the most common character in JS keywords (`if`, `for`, `function`), and every single-export module in the graph then emits the same `.f` / `.f()` byte sequences, which gzip's back-references pick up across the whole bundle — a bigger win than hashing, at the cost of that one key changing when a second export is added. A fixed list, `RESERVED_KEYS`, is withheld from every table for two different reasons: JS reserved words (`if`, `in`, `do`, `for`, `let`, `new`, `try`, `var`) are legal as quoted property keys but a minifier will not fold `ns["if"]` into the shorter `ns.if`, so handing one out costs bytes instead of saving them; and `__esModule` is withheld because the runtime's `esm()` helper defines that property on every module's exports object regardless of what the module itself exports, so an assigned key landing on it would collide. (`default` needs no such protection — once it is mangled like any other export, nothing else emits a property under that literal name.) **One source of truth for the mapping.** `mangled_export_names(module, chunking_context)` is a turbo-task that both the producing side (`EsmExports::code_generation`) and the consuming side (`ReferencedAssetIdent::Module`, the single place a cross-module export access is materialized) ask for the *target* module's map. Neither side computes a table of its own, so they cannot disagree, and the task derives export usage from the chunking context itself rather than accepting it as an argument, so a caller can't supply usage from the wrong graph. Re-export chains need no special handling, because the consumer side already resolves through re-exports to the module that produces the binding. The mangling decision itself lives on `EsmExports` as a `mangle_export_names: bool` field, rather than a separate trait method every module type has to override. A module that derives its exports from another one (a facade, a locals module, a part, a rename) inherits the flag with the data, which removed seven hand-written delegations and the possibility of a new wrapper type forgetting one. **A mangling decision must not cross module identities.** A few module types hand out *another* module's exports value as their own (the WASM loader module, the module-fragments side-effects wrapper, the client-reference proxy). If that borrowed value carried a real mangling decision, the producing and consuming sides would key their lookups on two different modules and could compute two different keys for the same export — this actually broke every WASM- and `@vercel/og`-based test once the default-on layer exercised it in CI. `EcmascriptExports::borrowed()` is the one place this is handled: it always returns an unmangled view, and every such pass-through site uses it. **Back-off is per module**, built on the export-usage information that landed after the original PR (`BindingUsageInfo` / `ModuleExportUsageInfo`) rather than the original's locals/facade-split heuristic. A module keeps its names when its usage is `All` (a namespace import that couldn't be lowered, a computed property access, an unresolvable `export *`, or a chunk-group entry — which covers client references and the Next.js wrapper modules), when it is read through a namespace value at all, when its exports are dynamic or not statically known ESM, or when names aren't being mangled in this build. `__webpack_exports_info__` gains `canMangle` and `mangledName` per export, which is how a running test can observe the mapping; with the option off it emits exactly what it emitted before. ### Testing - `turbo-tasks-hash`/table unit tests: encode/decode round-trip, degenerate-name rejection, table sizing, the single-export fixed key (including its own reservation), reserved-word withholding (including a reserved bucket-count test that stays in sync with the reserved list), the preserved-name pass running before any hashing, uniqueness under heavy collision, order independence, wrap-around probing, and same-tier stability. - 13 `turbopack-tests` execution fixtures under `tests/execution/turbopack/exports/mangle-*`, several ported from the original PR and from webpack's `test/configCases/mangle`: named imports, re-export chains and default exports (including one literally named `__esModule`), escaping namespaces (`Object.keys`, `delete ns.missing`, `export * as`, CJS interop), destructuring, prototype-shadowing names (`toString`, `$1`, `__1`), a 60-export two-character table, dynamic `import()` with `webpackExports` / `turbopackExports`, a CommonJS consumer of an ESM module, dynamic re-exports, scope hoisting on and off, and a control with the option off. - 3 committed snapshot fixtures under `tests/snapshot/mangle-exports`, so the emitted keys, the back-off, and the fixed single-export key are visible in review. - One fixture under `__skipped__`, which the harness asserts *fails*, recording the namespace-materialization gap below. - Full suite: 546 unit + 272 execution + 125 snapshot tests pass, with no snapshot churn across the several refactors this PR went through in review. - Verified against real builds: targeted app-dir, worker, WASM, and `@vercel/og`-based e2e suites pass with the option forced on (the failures that remain are external-network tests that fail identically with it off), and a small two-page app shrank by 0.76% of total emitted JS / 0.53% gzipped. ### Known limitations, each intentional - **A module read through `import * as ns` is never mangled**, even when every read is statically tracked, because the analysis doesn't yet distinguish a lowered named read from a materialized namespace object. Namespace imports are common, so this leaves real wins on the table; unlocking it is the highest-value follow-up. - **Escaping namespaces back off entirely.** Webpack instead keeps mangling and materializes a namespace object keyed by the original names. That is the `__skipped__` fixture: implementing it turns the suite red until the fixture is moved out. - **CommonJS export mangling is out of scope.** The producing side is reachable, but the consuming accesses live in user source and nothing rewrites them today; it needs its own design pass. Closes PACK-435 <!-- NEXT_JS_LLM --> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com> <!-- fleet ecdfa248-cd54-41ac-b4a2-c9d49e2a67ee --> --------- 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> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com> |
||
|
|
451985dcde | v16.4.0-canary.10 v16.4.0-canary.10 | ||
|
|
ee3a50ccb9 |
Upgrade Turbopack to hashbrown 0.15 (#97808)
### What? Upgrade Turbopack's workspace dependencies to DashMap 7.0.0-rc2 and Hashbrown 0.15.4, removing Hashbrown 0.14 from the `turbo-tasks-backend` dependency graph. ### Why? `turbo-tasks-backend` previously compiled two Hashbrown versions because its custom DashMap helpers shared Hashbrown 0.14's private raw table types. Converging the backend graph on Hashbrown 0.15 removes that duplicate backend codegen path and slightly reduces the shipped native addon's size. A clean release A/B of the unstripped x86-64 `next-napi-bindings` cdylib measured: | Revision | `libnext_napi_bindings.so` | | --- | ---: | | canary base `80576037` | 147,824,920 bytes | | PR | 147,818,576 bytes | | **Delta** | **−6,344 bytes (−0.0043%)** | The final gain is small because unrelated SWC dependencies still pull DashMap 6 / Hashbrown 0.14 into the same addon; this change removes only backend-specific Hashbrown 0.14 codegen. Plus... nearly everything is monomorphized anyway ### How? DashMap 7 replaces its Hashbrown 0.14 raw table with Hashbrown 0.15's public `HashTable` API. The backend helpers now use that public API while retaining their existing shard locking and allocation behavior: - heterogeneous cache lookup and insertion still reuse a pre-located shard and precomputed hash; - the entry helper passes Hashbrown's native `hash_table::Entry` into a closure, preserving precise lock-release timing without a custom self-referential entry type or cloned hasher; - nonblocking removal still avoids lock-order deadlocks; - two-key mutation uses a release-mode disjointness assertion followed by `get_many_unchecked_mut` to avoid a redundant duplicate-pointer scan; - snapshot draining still transfers ownership of each shard table and releases task memory incrementally; - custom mutable guards remain non-`Send`. The migration also replaces several raw-pointer iteration and removal paths with safe `HashTable` operations. Existing raw-entry users in `auto-hash-map` and `turbopack-trace-server` explicitly enable Hashbrown 0.15's compatibility feature. ### Performance Performance testing with the overhead.rs benchmark revealed no regression/progression <!-- NEXT_JS_LLM --> <!-- fleet 53ff0bed-46a9-4361-befa-a1791d585f27 --> --------- 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> |
||
|
|
49d3043e61 |
ci: skip framework tests for agent evals and skills (#97998)
Agent evals, skills, and the eval runner do not affect the framework runtime, but they currently make `Determine changes` schedule the full Rust, native, deployment, and framework test matrices. Classify changes confined to `evals/`, `skills/`, and `run-evals.js` with the existing lightweight documentation path. The always-on build and lint checks still run. Validated with Node syntax checking, Prettier, ESLint, and `git diff --check`. |
||
|
|
12bf495e0e |
Expand Turbopack dev cleanup (#97833)
## Summary Builds on #97591. Webpack currently removes most of `.next` on devserver startup. We can remove more as well. This keeps the age-based removal introduced in #97591 and expands it to `traces`, logs, etc. However, for any other directory that is not `cache` or `lock`, completely remove it on startup. This includes types, etc. Apply age-based deletion to Turbopack-owned output that can be restored from cache or recomputed, including route entries, chunks, assets, service workers, middleware, and instrumentation. Completely remove other dev output so logs, traces, generated types, diagnostics, and future framework-owned output cannot be left as mixed generations. Use exact-path exclusion sets in `recursiveDeleteSyncWithAsyncRetries`. The complete pass retains `cache`, `lock`, and Turbopack-owned paths; the age-based pass then sweeps those Turbopack paths. Both passes finish before project creation. ## Verification - `pnpm test-unit test/unit/recursive-delete.test.ts` - `pnpm --filter=next types` --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Will Binns-Smith <755844+wbinnssmith@users.noreply.github.com> |
||
|
|
b882af0899 |
Add Cache Components option to create-next-app (#97695)
When initializing a new project with create-next-app, the interactive prompt will now ask the user if they want to enable Cache Components. For now the option defaults to off. We'll flip the default back on once there's a flow that guides users to the right recommendation based on the kind of site they're building. The option can still be enabled interactively, or with the `--cache-components` flag. If the user chooses to enable Cache Components, the project will also be initialized with Partial Prefetching enabled. There's no separate prompt for Partial Prefetching; the only reason they are separate configs is because Cache Components shipped in 16.0 before Partial Prefetching existed. This is not a breaking change because it does not change any behavior in Next.js itself, only the create-next-app CLI. It has no impact on any existing Next.js projects. |
||
|
|
89ba4a32df |
Mark deploy release test skill as internal (#97961)
## Summary - mark the deploy-release-test repository skill as internal - prevent public skill discovery and telemetry publication for this repo-only workflow ## Testing - validated the SKILL.md frontmatter parses with metadata.internal set to true - git diff --check Co-authored-by: Aurora Scharff <66901228+aurorascharff@users.noreply.github.com> |
||
|
|
1f52cb42ad |
Turbopack: shorten CSS module class names (#97944)
The class names were unnecessarily long. Use the same as the lightningcss default: `[hash]_[local]` which is `<hash of the full file path>_<original class name or identifier>` Keep the previous longer mechanism to aid in debugging in dev Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> |
||
|
|
6d6228c0c6 |
Prune incomplete parallel route matchers (#97108)
## Summary
This adds an experimental `strictRouteMatching` flag that leaves a
matcher out when its finalized loader tree contains a synthesized
default that will always call `notFound()` for a slot actually declared
by the owning layout. Adding a `default.tsx` keeps the matcher, and
structural router-state branches that are not real slots of that layout
do not make it incomplete. The flag defaults to `false` so matcher
pruning can roll out independently from the preceding loader-tree
correction.
The main goal is to make `children` and named slots behave the same way.
If a URL cannot construct a complete route tree on its own, it should be
treated as an unmatched URL instead of sometimes showing a slot 404 and
sometimes producing a missing page or default error.
## Semantics
For example:
```text
app/split-matcher/
├── layout.tsx
├── foo/page.tsx
├── bar/page.tsx
└── @slot/[...parts]/page.tsx
```
With strict route matching disabled, Next.js emits matchers for
`/split-matcher/foo`, `/split-matcher/bar`, and the broad
`/split-matcher/[...parts]`. The broad matcher can match any URL, but
its `children` branch has neither a matching page nor a default, so it
can only construct a permanent 404 tree. With strict route matching
enabled, that broad matcher is omitted while `/foo` and `/bar` remain
because each combines a real `children` page with the named-slot
catchall. Adding `app/split-matcher/default.tsx` deliberately makes the
broad matcher complete and keeps it.
The same rule applies without a catchall:
```text
app/disagreeing-slots/
├── layout.tsx
├── @first/foo/page.tsx
└── @second/bar/page.tsx
```
`/foo` is incomplete because `@second` has no matching page or default,
and `/bar` is incomplete for the corresponding reason in `@first`, so
neither matcher is emitted. A later PR in this stack reports the
now-unreachable page files as a project misconfiguration.
A declared `children` route is treated like any named slot:
```text
app/declared-children/
├── layout.tsx
├── page.tsx
└── @panel/
├── default.tsx
└── details/page.tsx
```
`/declared-children` is complete because `children` uses `page.tsx` and
`@panel` uses its default. `/declared-children/details` is incomplete
because `@panel` matches its page while the declared `children` slot has
no matching page or default at `/details`, so that matcher is pruned.
A route composed entirely from declared named slots is also complete:
```text
app/named-only/
├── layout.tsx
├── @left/[...slug]/page.tsx
└── @right/[...slug]/page.tsx
```
The preceding PR's default-on `explicitParallelRouteChildren` behavior
means this loader tree contains only `left` and `right`. Strict matching
keeps `/named-only/[...slug]` because both declared slots match; it does
not invent `children` and then prune the route for failing to satisfy
that nonexistent slot.
We got here incrementally.
[#47872](https://github.com/vercel/next.js/pull/47872) introduced the
404 fallback for unmatched parallel slots,
[#60186](https://github.com/vercel/next.js/pull/60186) added a
development warning because this was confusing in practice and linked
[#51805](https://github.com/vercel/next.js/issues/51805) and
[#49569](https://github.com/vercel/next.js/issues/49569), and
[#84702](https://github.com/vercel/next.js/pull/84702) later made a
missing default a build error for named slots while leaving `children`
on the old fallback for backwards compatibility. Strict matching takes
the next step and treats a matcher that can only construct a permanent
404 tree as unmatched.
This changes soft navigations that only worked by preserving a
previously active slot even though the URL could not be loaded directly,
which is why matcher pruning remains behind its own experimental flag.
The interception retention markers backed by `default-null`, including
named host slots from the first PR in this stack, are complete route
patches and are not pruned. This change however is well motivated
because if you did client nav to a route that only matches a named slot
and then hard refresh you will end up getting a 404. This is a sign our
current semantics are actually broken.
If you want to preserve the perma 404 behavior of the
slot-without-default you can just add a default and make it call
`notFound()` unconditionally.
## Verification
- `pnpm build-all`
- Focused `normalize-catchall-routes` unit coverage
- Production and development e2e coverage on Turbopack and Webpack
- The pruning e2e matrix with Cache Components enabled
<!-- NEXT_JS_LLM -->
|
||
|
|
3243c970fc |
Upgrade React from f789f203-20260825 to 29d9d318-20260826 (#97995)
|
||
|
|
24ed66abe9 |
docs(skills): preserve prefetched UI during Partial Prefetching adoption (#97712)
## What
- Documents how legacy `prefetch={true}` behavior changes with Partial
Prefetching and how to choose the UI worth preserving.
- Updates the adoption skill to capture a passing flag-off `instant()`
baseline, use the same assertions to drive the migration, and retain
them as regression coverage.
- Adds a reusable production test rig, keeps additional per-link
optimization separate from adoption, and surfaces `prefetch={false}`
links that may no longer be needed.
- Applies the shared test-rig fixes discovered during dogfooding to the
Cache Components optimizer.
## Why
Legacy `prefetch={true}` could include uncached dynamic content. With
Partial Prefetching, it includes the shared App Shell and cached
URL-specific content instead. A passing build or clean insight sweep
does not prove that the intended prefetched UI survived the migration,
so the skill now records that contract before adoption and verifies it
unchanged afterward. The guide presents the same test workflow as an
optional programmatic check for manual adopters.
## Testing
- Prettier, Alex, and skill frontmatter validation
- Dogfooded on v0 in https://github.com/vercel/v0/pull/28842
- Partial Prefetching adoption eval: 6/6 in
https://github.com/vercel/next.js/pull/97759
|
||
|
|
05ed7c1be2 |
Omit undeclared children slots from app routes (#97184)
## Summary Parallel route layouts can be composed entirely from named slots, but loader tree construction currently synthesizes a `children` fallback whenever any named slot exists. This makes `children` semantically required even when no page, default, or ordinary route branch declares it. This adds `experimental.explicitParallelRouteChildren` and enables it by default. When enabled, `children` is included in a layout slot set only when the filesystem declares an ordinary route that can render at that level. A layout by itself is only structure and does not declare a route target. Ordinary descendants are traced until they reach a page or default, including through deeper named slots, before they cause `children` to be included. Setting the flag to `false` temporarily restores the legacy implicit `children` fallback. This flag only controls whether `children` exists in the loader tree. It does not prune incomplete matchers; that is the separate `experimental.strictRouteMatching` behavior in the next PR. Named slots keep their existing default and soft navigation semantics. The preceding PR retains the slots owned by an interception host without treating `children` specially, so an undeclared child is no longer needed for that behavior. A real `children` branch still uses the retention marker when it is one of the host layout slots. ## Semantics For example, this layout declares only named slots: ```text app/dashboard/layout.tsx app/dashboard/@left/page.tsx app/dashboard/@right/page.tsx ``` With `explicitParallelRouteChildren` disabled, Next.js adds a synthetic `children` branch whose built-in default calls `notFound()`, even though the layout never declared or rendered it. With the default behavior enabled, the loader tree contains only `left` and `right`, so `/dashboard` is matched from the route targets that actually exist. This is different from an ordinary branch whose route targets are deeper in the tree: ```text app/nested/layout.tsx app/nested/@sidebar/[...slug]/page.tsx app/nested/content/layout.tsx app/nested/content/@left/[...slug]/page.tsx app/nested/content/@right/[...slug]/page.tsx ``` Here `content` really is the `children` branch of `nested`. The scan follows `content` through its layout and deeper named slots, so `children` remains required. `/nested/content/anything` can construct every declared slot, while `/nested/incomplete` only matches `sidebar` and is still incomplete. The distinction is whether the ordinary descendant eventually reaches a page or default, not whether a layout happens to exist along the way. The focused children detection coverage proves that a layout-only descendant does not synthesize `children`, while an ordinary branch whose route targets live inside deeper named slots still does. The limitation coverage also proves that named-only trees render pages, CSS, metadata, and regular error boundaries. It intentionally asserts the current broken behavior for HTTP access fallbacks and metadata or viewport failures so those expectations can be flipped when renderer ownership no longer depends on `children`. ## Verification - `pnpm build-all` - Turbopack and webpack development and production coverage for `explicit-parallel-route-children-detection` - Turbopack and webpack production coverage for `interception-dynamic-segment`, `parallel-routes-layouts`, and `explicit-parallel-route-children-legacy` - The same existing production coverage with Cache Components enabled - Turbopack and webpack production coverage for the documented named-only limitations <!-- NEXT_JS_LLM --> |
||
|
|
cbe4687bf0 |
Turbopack: widen the chunk ident hash from 7 to 13 base38 chars (#97945)
Closes #97766. ### What? Widen the chunk ident hash in `turbopack-core/src/ident.rs` from 7 to 13 base38 characters. ### Why? 7 base38 characters is ≈36 bits nominally, and closer to ~32 in practice because the leading character is bounded — an effective space of roughly 6×10⁹. Any population of chunks that shares a readable name prefix and is distinguished **only** by that hash is therefore a birthday problem: at n names the collision probability is ≈ n²/2M, so around 10⁵ such chunks collide with probability of order 1 and the build aborts with: ``` Two or more assets with different content were emitted to the same output path ``` This is not hypothetical at real app sizes: - #97765 — deterministic reproduction (this PR fixes it): 120,000 chunks sharing one prefix, fails byte-identically on every run, same collided path and same rejected content hashes across consecutive builds. - #96976 — production App Router app, ~69,000 emitted server chunks, ≈9% failure probability per build measured, growing quadratically with chunk count. The ergonomics are the painful part: the colliding pair is chosen by the hash, not by any structural property. The failure lands on whichever branch last touched the module graph and looks like that branch's fault; bisecting finds a commit but never a responsible file; and moving a module only reshuffles which pair collides. We verified that a module renamed *inside* a colliding chunk does not change the chunk's name at all — same two chunks, same 7-character name, renamed file listed inside. ### How? ```diff let hash = encode_base38(hasher.finish()); - // 7 base38 chars ≈ 36 bits of collision resistance - let truncated_hash = &hash[..7]; - write!(name, "_{truncated_hash}")?; + // Use the complete fixed-width base38 encoding of the 64-bit hash. + // + // This suffix is what distinguishes idents that share a readable + // name prefix, so truncating it makes distinct output paths collide + // for large module graphs, failing the build with "Two or more + // assets with different content were emitted to the same output + // path". Keeping all 64 bits removes that failure mode, at the cost + // of a slightly longer file name. + write!(name, "_{hash}")?; ``` The complete 13-character base38 encoding preserves all 64 bits of the identity hash. Cost: 6 extra characters per emitted chunk filename. ### Notes for reviewers - Output filenames change wholesale on upgrade, as with any naming change; nothing reads these names across versions. - Snapshot tests that pin emitted chunk names have been regenerated. - If you would rather solve this by hashing more distinguishing input rather than widening the truncation, I am glad to rework it; the length is the smallest change that removes the failure mode. <!-- NEXT_JS_LLM --> <!-- fleet daf6a1b0-ebda-4eed-a89f-03e5ec7dc357 --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: jonnyuniverse <jon@fractionalventure.partners> Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com> Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> |
||
|
|
c5b960e2e5 |
Fix request-context retention in the default use cache handler (#97941)
Fixes #97934. The default use cache handler uses live ReadableStream objects. Those streams retain references to the request’s async context and closed HTTP response, causing memory to grow with cached entries and potentially produce OOMs. The fix converts each stream into a plain Buffer before caching it. Every cache hit then gets a fresh temporary stream created from those bytes. The content remains cached, but completed requests can now be garbage-collected. --- Basically - the LRU cache stores live ReadableStream objects, which keep completed request contexts and closed responses in memory until that cache is evicted. |
||
|
|
e80a85bcff |
docs: improve discovery summaries (#97982)
This PR improves the meta descriptions on certain pages. <!-- 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 We encourage you to use AI to assist you in researching, creating, and reviewing changes. However, you must review and deeply understand the contributions you are making. For this reason, **pull request descriptions from external contributors must be written by a human**. ### 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 ### Signed commits - This repository requires verified commit signatures on protected branches. - If this pull request is blocked for unsigned commits, re-sign the commits and force-push the branch. - A `Signed-off-by` line in the commit message is not enough. ## 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 # --> |
||
|
|
04e96a5bfa |
test: add test for local font with deployment id (#97987)
Follow up to a previous PR: - https://github.com/vercel/next.js/pull/82488 - https://github.com/vercel/next.js/pull/82384 Closes: - https://github.com/vercel/next.js/pull/82547 |
||
|
|
cce7bf2fc0 |
Retain interception route host slots (#97242)
## Summary
Interception routes represent a partial update to the layout that hosts
them. Today we model this for `children` by synthesizing a `__DEFAULT__`
route backed by `default-null`, but named siblings still use normal
default matching even though they should retain their active state too.
This makes retention relative to the interception host instead of the
`children` key. Every non-intercepting sibling gets the existing
`__DEFAULT__` marker backed by `default-null`, while slots inside the
newly selected interception subtree continue to use normal matching and
real defaults.
The coverage uses a named-only host to verify that named siblings retain
client state without evaluating a user default, that a sibling without a
default also retains, and that a hard refresh still loads the canonical
route.
## Example
Consider this route tree:
```text
app/
├── named-host/
│ ├── layout.tsx # renders canonical, content, secondary, and modal
│ ├── @canonical/
│ │ └── page.tsx
│ ├── @content/
│ │ ├── page.tsx # renders a stateful counter
│ │ └── default.tsx # throws if evaluated
│ ├── @secondary/
│ │ └── page.tsx # has no default.tsx
│ └── @modal/
│ ├── default.tsx
│ └── (..)named-target/
│ └── page.tsx # intercepted target
└── named-target/
└── page.tsx # canonical route
```
Before this change, a soft navigation from `/named-host` to
`/named-target` could replace `@content` with its throwing default and
treat `@secondary` as missing because only `children` had retain
semantics. After this change, only `@modal` switches to the intercepted
page; `@content` and `@secondary` keep their existing UI and the counter
keeps its state. A hard refresh still renders the canonical
`/named-target` route instead of the interception host.
## Verification
- Turbopack and webpack production coverage in
`interception-dynamic-segment`
- The same production coverage with Cache Components enabled
<!-- NEXT_JS_LLM -->
|
||
|
|
0a2bd1d531 |
test: disable flaky sync-io-blocks-root.test.ts (#97986)
Something made this test very flaky on Aug 24/Aug 25 https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.status%3A%22fail%22%20%40test.suite%3A%22sync%20IO%20that%20blocks%20the%20root%22%20-%40test.type%3Aturbopack&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=citest&start=1787237730805&end=1787842530805&paused=true Disabling until we can investigate. |
||
|
|
d42ea11842 |
[test] Drain build output before start (#97947)
### What? Make the production test harness wait for a build process's piped stdio to close before `NextStartInstance.start()` continues to launch the server or reports a build failure. ### Why? A child process's `exit` event can fire before trailing stdout and stderr have been delivered to the parent. This can make start-mode tests intermittently miss diagnostics emitted near the end of a production build, even though the build itself reported them correctly. The standalone `build()` helper already uses the later `close` lifecycle event to avoid this race. Applying the same rule to the build phase inside `start()` removes the equivalent latent output-drain hazard and makes child-process handling consistent across the harness. ### How? Resolve the build phase on the child process's `close` event. Unlike `exit`, `close` is emitted after piped stdio closes, so all trailing build output has reached the harness before it starts the server or rejects the build. Exit-code and signal handling remain unchanged. ### Verification - `pnpm types` - `pnpm test-start-webpack test/production/production-build-dir/production-build-dir.test.ts` - `pnpm test-start-webpack test/production/app-dir/sync-io-blocks-root/sync-io-blocks-root.test.ts` - Repeated webpack stress campaign for the sync-IO suite: 5/5 runs, including 20/20 `--debug-prerender` route cases - Prettier and ESLint on `test/lib/next-modes/next-start.ts` <!-- NEXT_JS_LLM --> <!-- fleet 821362ed-9f96-4e53-b56e-49bf00e878b9 --> 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> |
||
|
|
3d85af96be |
Expose durableUseCacheEntries config in workStore (#97926)
https://github.com/vercel/next.js/pull/95233 needs this to decide whether to perform the manifest lookup. |
||
|
|
89d017eac4 |
Pages Router: Deprecate React 18 support (#97689)
## Summary - warn during `next dev` and `next build` when React 18 is installed, while keeping `next start` quiet - explain that React 18 remains supported in Next.js 16 but will be removed in Next.js 17 - update the React version guidance with React 19 and TypeScript type upgrade commands ## Verification - `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-dev-turbo test/e2e/react-current-version/react-current-version.test.ts` - `NEXT_TEST_REACT_VERSION=18.3.1 NEXT_TEST_PREFER_OFFLINE=1 pnpm test-dev-turbo test/e2e/react-current-version/react-current-version.test.ts` - targeted production build/start coverage passed with React 18 and React 19 - Not passing: `pnpm --filter=next types` (existing unrelated errors in `httpget.ts`, `image-optimizer.ts`, `proxy-request.ts`, telemetry typings, and compiled declarations) - Not passing: `pnpm lint` (existing unused Turbo Tasks check) <!-- NEXT_JS_LLM --> |
||
|
|
286fcc346f |
Turbopack: call loadActionManifest for app-route (#97921)
For #95233, I need to read server-reference-manifest.json even for App Routes, which previously didn't emit server action manifest entries at all (because thus far, it was unnecessary). |