Skip to content

[pull] canary from vercel:canary #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "15.4.2-canary.24"
"version": "15.4.2-canary.25"
}
30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,16 @@
"pretty-ms": "7.0.0",
"random-seed": "0.3.0",
"react": "19.0.0",
"react-builtin": "npm:[email protected]9784cb37-20250730",
"react-builtin": "npm:[email protected]c260b38d-20250731",
"react-dom": "19.0.0",
"react-dom-builtin": "npm:[email protected]9784cb37-20250730",
"react-dom-experimental-builtin": "npm:[email protected]9784cb37-20250730",
"react-experimental-builtin": "npm:[email protected]9784cb37-20250730",
"react-is-builtin": "npm:[email protected]9784cb37-20250730",
"react-server-dom-turbopack": "19.2.0-canary-9784cb37-20250730",
"react-server-dom-turbopack-experimental": "npm:[email protected]9784cb37-20250730",
"react-server-dom-webpack": "19.2.0-canary-9784cb37-20250730",
"react-server-dom-webpack-experimental": "npm:[email protected]9784cb37-20250730",
"react-dom-builtin": "npm:[email protected]c260b38d-20250731",
"react-dom-experimental-builtin": "npm:[email protected]c260b38d-20250731",
"react-experimental-builtin": "npm:[email protected]c260b38d-20250731",
"react-is-builtin": "npm:[email protected]c260b38d-20250731",
"react-server-dom-turbopack": "19.2.0-canary-c260b38d-20250731",
"react-server-dom-turbopack-experimental": "npm:[email protected]c260b38d-20250731",
"react-server-dom-webpack": "19.2.0-canary-c260b38d-20250731",
"react-server-dom-webpack-experimental": "npm:[email protected]c260b38d-20250731",
"react-ssr-prepass": "1.0.8",
"react-virtualized": "9.22.3",
"relay-compiler": "13.0.2",
Expand All @@ -252,8 +252,8 @@
"resolve-from": "5.0.0",
"sass": "1.54.0",
"satori": "0.15.2",
"scheduler-builtin": "npm:[email protected]9784cb37-20250730",
"scheduler-experimental-builtin": "npm:[email protected]9784cb37-20250730",
"scheduler-builtin": "npm:[email protected]c260b38d-20250731",
"scheduler-experimental-builtin": "npm:[email protected]c260b38d-20250731",
"seedrandom": "3.0.5",
"semver": "7.3.7",
"serve-handler": "6.1.6",
Expand Down Expand Up @@ -297,10 +297,10 @@
"@types/react-dom": "19.1.6",
"@types/retry": "0.12.0",
"jest-snapshot": "30.0.0-alpha.6",
"react": "19.2.0-canary-9784cb37-20250730",
"react-dom": "19.2.0-canary-9784cb37-20250730",
"react-is": "19.2.0-canary-9784cb37-20250730",
"scheduler": "0.27.0-canary-9784cb37-20250730"
"react": "19.2.0-canary-c260b38d-20250731",
"react-dom": "19.2.0-canary-c260b38d-20250731",
"react-is": "19.2.0-canary-c260b38d-20250731",
"scheduler": "0.27.0-canary-c260b38d-20250731"
},
"patchedDependencies": {
"[email protected]": "patches/[email protected]",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"keywords": [
"react",
"next",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "ESLint configuration used by Next.js.",
"main": "index.js",
"license": "MIT",
Expand All @@ -10,7 +10,7 @@
},
"homepage": "https://nextjs.org/docs/app/api-reference/config/eslint",
"dependencies": {
"@next/eslint-plugin-next": "15.4.2-canary.24",
"@next/eslint-plugin-next": "15.4.2-canary.25",
"@rushstack/eslint-patch": "^1.10.3",
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-internal/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/eslint-plugin-internal",
"private": true,
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "ESLint plugin for working on Next.js.",
"exports": {
".": "./src/eslint-plugin-internal.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/font/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/font",
"private": true,
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-mdx/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-plugin-storybook/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-module/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-nomodule/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-rspack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next-rspack",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-rspack"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-swc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/swc",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"private": true,
"files": [
"native/"
Expand Down
8 changes: 7 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -764,5 +764,11 @@
"763": "\\`unstable_rootParams\\` must not be used within a client component. Next.js should be preventing it from being included in client components statically, but did not in this case.",
"764": "Missing workStore in %s",
"765": "Route %s used %s inside a Route Handler. Support for this API in Route Handlers is planned for a future version of Next.js.",
"766": "%s was used inside a Server Action. This is not supported. Functions from 'next/root-params' can only be called in the context of a route."
"766": "%s was used inside a Server Action. This is not supported. Functions from 'next/root-params' can only be called in the context of a route.",
"767": "A runtime prerender store should not be used for a route handler.",
"768": "createPrerenderSearchParamsForClientPage should not be called in a runtime prerender.",
"769": "createSearchParamsFromClient should not be called in a runtime prerender.",
"770": "createParamsFromClient should not be called in a runtime prerender.",
"771": "\\`%s\\` was called during a runtime prerender. Next.js should be preventing %s from being included in server components statically, but did not in this case.",
"772": "FetchStrategy.PPRRuntime should never be used when `experimental.clientSegmentCache` is disabled"
}
14 changes: 7 additions & 7 deletions packages/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next",
"version": "15.4.2-canary.24",
"version": "15.4.2-canary.25",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
Expand Down Expand Up @@ -102,7 +102,7 @@
]
},
"dependencies": {
"@next/env": "15.4.2-canary.24",
"@next/env": "15.4.2-canary.25",
"@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
Expand Down Expand Up @@ -166,11 +166,11 @@
"@jest/types": "29.5.0",
"@mswjs/interceptors": "0.23.0",
"@napi-rs/triples": "1.2.0",
"@next/font": "15.4.2-canary.24",
"@next/polyfill-module": "15.4.2-canary.24",
"@next/polyfill-nomodule": "15.4.2-canary.24",
"@next/react-refresh-utils": "15.4.2-canary.24",
"@next/swc": "15.4.2-canary.24",
"@next/font": "15.4.2-canary.25",
"@next/polyfill-module": "15.4.2-canary.25",
"@next/polyfill-nomodule": "15.4.2-canary.25",
"@next/react-refresh-utils": "15.4.2-canary.25",
"@next/swc": "15.4.2-canary.25",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "1.51.1",
"@rspack/core": "1.4.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/build/templates/app-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export async function handler(
*/
const isPrefetchRSCRequest =
getRequestMeta(req, 'isPrefetchRSCRequest') ??
Boolean(req.headers[NEXT_ROUTER_PREFETCH_HEADER])
req.headers[NEXT_ROUTER_PREFETCH_HEADER] === '1' // exclude runtime prefetches, which use '2'

// NOTE: Don't delete headers[RSC] yet, it still needs to be used in renderToHTML later

Expand Down
1 change: 1 addition & 0 deletions packages/next/src/client/app-dir/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default function Form({
}
}

// TODO(runtime-ppr): allow runtime prefetches in Form
const prefetch =
prefetchProp === false || prefetchProp === null ? prefetchProp : null

Expand Down
66 changes: 51 additions & 15 deletions packages/next/src/client/app-dir/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import {
import { isLocalURL } from '../../shared/lib/router/utils/is-local-url'
import { dispatchNavigateAction } from '../components/app-router-instance'
import { errorOnce } from '../../shared/lib/utils/error-once'
import { FetchStrategy } from '../components/segment-cache'
import {
FetchStrategy,
type PrefetchTaskFetchStrategy,
} from '../components/segment-cache'

type Url = string | UrlObject
type RequiredKeys<T> = {
Expand Down Expand Up @@ -151,10 +154,10 @@ type InternalLinkProps = {
* </Link>
* ```
*/
prefetch?: boolean | 'auto' | null
prefetch?: boolean | 'auto' | null | 'unstable_forceStale'

/**
* (unstable) Switch to a dynamic prefetch on hover. Effectively the same as
* (unstable) Switch to a full prefetch on hover. Effectively the same as
* updating the prefetch prop to `true` in a mouse event.
*/
unstable_dynamicOnHover?: boolean
Expand Down Expand Up @@ -356,18 +359,12 @@ export default function LinkComponent(
const router = React.useContext(AppRouterContext)

const prefetchEnabled = prefetchProp !== false
/**
* The possible states for prefetch are:
* - null: this is the default "auto" mode, where we will prefetch partially if the link is in the viewport
* - true: we will prefetch if the link is visible and prefetch the full page, not just partially
* - false: we will not prefetch if in the viewport at all
* - 'unstable_dynamicOnHover': this starts in "auto" mode, but switches to "full" when the link is hovered
*/

const fetchStrategy =
prefetchProp === null || prefetchProp === 'auto'
? // We default to PPR. We'll discover whether or not the route supports it with the initial prefetch.
prefetchProp !== false
? getFetchStrategyFromPrefetchProp(prefetchProp)
: // TODO: it makes no sense to assign a fetchStrategy when prefetching is disabled.
FetchStrategy.PPR
: FetchStrategy.Full

if (process.env.NODE_ENV !== 'production') {
function createPropError(args: {
Expand Down Expand Up @@ -470,11 +467,12 @@ export default function LinkComponent(
if (
props[key] != null &&
valType !== 'boolean' &&
props[key] !== 'auto'
props[key] !== 'auto' &&
props[key] !== 'unstable_forceStale'
) {
throw createPropError({
key,
expected: '`boolean | "auto"`',
expected: '`boolean | "auto" | "unstable_forceStale"`',
actual: valType,
})
}
Expand Down Expand Up @@ -745,3 +743,41 @@ const LinkStatusContext = createContext<
export const useLinkStatus = () => {
return useContext(LinkStatusContext)
}

function getFetchStrategyFromPrefetchProp(
prefetchProp: Exclude<LinkProps['prefetch'], undefined | false>
): PrefetchTaskFetchStrategy {
if (
process.env.__NEXT_CACHE_COMPONENTS &&
process.env.__NEXT_CLIENT_SEGMENT_CACHE
) {
// In the new implementation:
// - `prefetch={true}` is a runtime prefetch
// (includes cached IO + params + cookies, with dynamic holes for uncached IO).
// - `unstable_forceStale` is a "full" prefetch
// (forces inclusion of all dynamic data, i.e. the old behavior of `prefetch={true}`)
if (prefetchProp === true) {
return FetchStrategy.PPRRuntime
}
if (prefetchProp === 'unstable_forceStale') {
return FetchStrategy.Full
}

// `null` or `"auto"`: this is the default "auto" mode, where we will prefetch partially if the link is in the viewport.
// This will also include invalid prop values that don't match the types specified here.
// (although those should've been filtered out by prop validation in dev)
prefetchProp satisfies null | 'auto'
// In `clientSegmentCache`, we default to PPR, and we'll discover whether or not the route supports it with the initial prefetch.
// If we're not using `clientSegmentCache`, this will be converted into a `PrefetchKind.AUTO`.
return FetchStrategy.PPR
} else {
return prefetchProp === null || prefetchProp === 'auto'
? // In `clientSegmentCache`, we default to PPR, and we'll discover whether or not the route supports it with the initial prefetch.
// If we're not using `clientSegmentCache`, this will be converted into a `PrefetchKind.AUTO`.
FetchStrategy.PPR
: // In the old implementation without runtime prefetches, `prefetch={true}` forces all dynamic data to be prefetched.
// To preserve backwards-compatibility, anything other than `false`, `null`, or `"auto"` results in a full prefetch.
// (although invalid values should've been filtered out by prop validation in dev)
FetchStrategy.Full
}
}
2 changes: 2 additions & 0 deletions packages/next/src/client/components/app-router-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ export const publicAppRouterInstance: AppRouterInstance = {
const actionQueue = getAppRouterActionQueue()
const prefetchKind = options?.kind ?? PrefetchKind.AUTO

// We don't currently offer a way to issue a runtime prefetch via `router.prefetch()`.
// This will be possible when we update its API to not take a PrefetchKind.
let fetchStrategy: PrefetchTaskFetchStrategy
switch (prefetchKind) {
case PrefetchKind.AUTO: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function bailoutToClientRendering(reason: string): void | never {
if (workUnitStore) {
switch (workUnitStore.type) {
case 'prerender':
case 'prerender-runtime':
case 'prerender-client':
case 'prerender-ppr':
case 'prerender-legacy':
Expand Down
10 changes: 9 additions & 1 deletion packages/next/src/client/components/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from './segment-cache'
import { startTransition } from 'react'
import { PrefetchKind } from './router-reducer/router-reducer-types'
import { InvariantError } from '../../shared/lib/invariant-error'

type LinkElement = HTMLAnchorElement | SVGAElement

Expand Down Expand Up @@ -264,7 +265,7 @@ export function onNavigationIntent(
process.env.__NEXT_DYNAMIC_ON_HOVER &&
unstable_upgradeToDynamicPrefetch
) {
// Switch to a full, dynamic prefetch
// Switch to a full prefetch
instance.fetchStrategy = FetchStrategy.Full
}
rescheduleLinkPrefetch(instance, PrefetchPriority.Intent)
Expand Down Expand Up @@ -378,6 +379,13 @@ function prefetchWithOldCacheImplementation(instance: PrefetchableInstance) {
prefetchKind = PrefetchKind.FULL
break
}
case FetchStrategy.PPRRuntime: {
// We can only get here if Client Segment Cache is off, and in that case
// it shouldn't be possible for a link to request a runtime prefetch.
throw new InvariantError(
'FetchStrategy.PPRRuntime should never be used when `experimental.clientSegmentCache` is disabled'
)
}
default: {
instance.fetchStrategy satisfies never
// Unreachable, but otherwise typescript will consider the variable unassigned
Expand Down
Loading
Loading