diff --git a/crates/next-error-code-swc-plugin/src/lib.rs b/crates/next-error-code-swc-plugin/src/lib.rs index 71f349acb349..cef91ca8e243 100644 --- a/crates/next-error-code-swc-plugin/src/lib.rs +++ b/crates/next-error-code-swc-plugin/src/lib.rs @@ -56,7 +56,6 @@ fn is_error_class_name(name: &str) -> bool { || name == "DecodeError" || name == "DynamicServerError" || name == "ExportError" - || name == "FatalError" || name == "ImageError" || name == "InstantValidationError" || name == "InvariantError" diff --git a/docs/01-app/01-getting-started/05-server-and-client-components.mdx b/docs/01-app/01-getting-started/05-server-and-client-components.mdx index d1615570d3aa..4cf024887573 100644 --- a/docs/01-app/01-getting-started/05-server-and-client-components.mdx +++ b/docs/01-app/01-getting-started/05-server-and-client-components.mdx @@ -173,7 +173,11 @@ export default function Counter() { `"use client"` is used to declare a **boundary** between the Server and Client module graphs (trees). -Once a file is marked with `"use client"`, **all its imports and child components are considered part of the client bundle**. This means you don't need to add the directive to every component that is intended for the client. +Once a file is marked with `"use client"`, **all of its imports and the components it directly renders are included in the client bundle**. This means you don’t need to add the directive to every component that is intended for the client. + +This behavior applies to components that are part of the Client Component’s [module graph](/docs/app/glossary#module-graph), which includes the modules it imports and the components it renders directly. It does not apply to Server Components passed as children or other props. Those components are not imported into the Client Component’s module graph. They are rendered on the server and passed to the Client Component as rendered output. + +See [Interleaving Server and Client Components](/docs/app/getting-started/server-and-client-components#interleaving-server-and-client-components) for how Server and Client Components can be combined. ### Reducing JS bundle size @@ -338,7 +342,7 @@ export default function Page() { } ``` -In this pattern, all Server Components will be rendered on the server ahead of time, including those as props. The resulting RSC payload will contain references of where Client Components should be rendered within the component tree. +In this pattern, Server Components are rendered on the server ahead of time, even when passed as props to Client Components. The React Server Component Payload contains the rendered result of those Server Components, plus placeholders for where Client Components should be rendered and references to their JavaScript files. ### Context providers diff --git a/docs/01-app/02-guides/authentication.mdx b/docs/01-app/02-guides/authentication.mdx index 1fbc5b14c6e8..7afb2c792e77 100644 --- a/docs/01-app/02-guides/authentication.mdx +++ b/docs/01-app/02-guides/authentication.mdx @@ -561,7 +561,7 @@ const secretKey = process.env.SESSION_SECRET #### 2. Encrypting and decrypting sessions -Next, you can use your preferred [session management library](#session-management-libraries) to encrypt and decrypt sessions. Continuing from the previous example, we'll use [Jose](https://www.npmjs.com/package/jose) (compatible with the [Edge Runtime](/docs/app/api-reference/edge)) and React's [`server-only`](https://www.npmjs.com/package/server-only) package to ensure that your session management logic is only executed on the server. +Next, you can use your preferred [session management library](#session-management-libraries) to encrypt and decrypt sessions. Continuing from the previous example, we'll use [Jose](https://www.npmjs.com/package/jose) and React's [`server-only`](https://www.npmjs.com/package/server-only) package to ensure that your session management logic is only executed on the server. ```tsx filename="app/lib/session.ts" switcher import 'server-only' @@ -1121,7 +1121,7 @@ While Proxy can be useful for initial checks, it should not be your only line of > **Tips**: > > - In Proxy, you can also read cookies using `req.cookies.get('session').value`. -> - Proxy uses the Node.js runtime, check if your Auth library and session management library are compatible. You may need to use [Middleware](https://github.com/vercel/next.js/blob/v15.5.6/docs/01-app/03-api-reference/03-file-conventions/middleware.mdx) if your Auth library only supports [Edge Runtime](/docs/app/api-reference/edge) +> - Proxy uses the Node.js runtime, check if your Auth library and session management library are compatible. > - You can use the `matcher` property in the Proxy to specify which routes Proxy should run on. Although, for auth, it's recommended Proxy runs on all routes. diff --git a/docs/01-app/02-guides/caching-without-cache-components.mdx b/docs/01-app/02-guides/caching-without-cache-components.mdx index c3fc250f32be..062018400ea1 100644 --- a/docs/01-app/02-guides/caching-without-cache-components.mdx +++ b/docs/01-app/02-guides/caching-without-cache-components.mdx @@ -183,7 +183,7 @@ export const revalidate = false > **Good to know**: > > - The revalidate value needs to be statically analyzable. For example `revalidate = 600` is valid, but `revalidate = 60 * 10` is not. -> - The revalidate value is not available when using `runtime = 'edge'`. +> - The revalidate value is not available when using the deprecated `runtime = 'edge'`. > - In Development, Pages are _always_ rendered on-demand and are never cached. This allows you to see changes immediately without waiting for a revalidation period to pass. #### Revalidation frequency diff --git a/docs/01-app/02-guides/migrating-to-cache-components.mdx b/docs/01-app/02-guides/migrating-to-cache-components.mdx index dd7f7db9b091..dff0f4398034 100644 --- a/docs/01-app/02-guides/migrating-to-cache-components.mdx +++ b/docs/01-app/02-guides/migrating-to-cache-components.mdx @@ -178,7 +178,7 @@ export default async function Page() { ## `runtime = 'edge'` -**Not supported.** Cache Components requires the Node.js runtime. Switch to the Node.js runtime (the default) by removing the `runtime = 'edge'` export. If you need edge behavior for specific routes, use [Proxy](/docs/app/api-reference/file-conventions/proxy) instead. +**Not supported.** Cache Components requires the Node.js runtime. Switch to the Node.js runtime (the default) by removing the [deprecated](/docs/messages/edge-runtime-deprecated) `runtime = 'edge'` export. If you need edge behavior for specific routes, use [Proxy](/docs/app/api-reference/file-conventions/proxy) instead. ## UI state preservation diff --git a/docs/01-app/02-guides/self-hosting.mdx b/docs/01-app/02-guides/self-hosting.mdx index d0273b919fa9..2917c783472e 100644 --- a/docs/01-app/02-guides/self-hosting.mdx +++ b/docs/01-app/02-guides/self-hosting.mdx @@ -30,8 +30,6 @@ Image Optimization can be used with a [static export](/docs/app/guides/static-ex [Proxy](/docs/app/api-reference/file-conventions/proxy) works self-hosted with zero configuration when deploying using `next start`. Since it requires access to the incoming request, it is not supported when using a [static export](/docs/app/guides/static-exports). -Proxy uses the [Edge runtime](/docs/app/api-reference/edge), a subset of all available Node.js APIs to help ensure low latency, since it may run in front of every route or asset in your application. If you do not want this, you can use the [full Node.js runtime](/blog/next-15-2#nodejs-middleware-experimental) to run Proxy. - If you are looking to add logic (or use an external package) that requires all Node.js APIs, you might be able to move this logic to a [layout](/docs/app/api-reference/file-conventions/layout) as a [Server Component](/docs/app/getting-started/server-and-client-components). For example, checking [headers](/docs/app/api-reference/functions/headers) and [redirecting](/docs/app/api-reference/functions/redirect). You can also use headers, cookies, or query parameters to [redirect](/docs/app/api-reference/config/next-config-js/redirects#header-cookie-and-query-matching) or [rewrite](/docs/app/api-reference/config/next-config-js/rewrites#header-cookie-and-query-matching) through `next.config.js`. If that does not work, you can also use a [custom server](/docs/pages/guides/custom-server). ## Environment Variables diff --git a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/index.mdx b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/index.mdx index d9c308413df0..b537ed830866 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/index.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/index.mdx @@ -5,12 +5,12 @@ description: Learn about how to configure options for Next.js route segments. The Route Segment Config options allow you to configure the behavior of a [Page](/docs/app/api-reference/file-conventions/page), [Layout](/docs/app/api-reference/file-conventions/layout), or [Route Handler](/docs/app/api-reference/file-conventions/route) by directly exporting the following variables: -| Option | Type | Default | -| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------- | -| [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config/dynamicParams) | `boolean` | `true` | -| [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config/runtime) | `'nodejs' \| 'edge'` | `'nodejs'` | -| [`preferredRegion`](/docs/app/api-reference/file-conventions/route-segment-config/preferredRegion) | `'auto' \| 'global' \| 'home' \| string \| string[]` | `'auto'` | -| [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config/maxDuration) | `number` | Set by deployment platform | +| Option | Type | Default | +| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------- | +| [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config/dynamicParams) | `boolean` | `true` | +| [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config/runtime) | `'nodejs' \| 'edge' (deprecated)` | `'nodejs'` | +| [`preferredRegion`](/docs/app/api-reference/file-conventions/route-segment-config/preferredRegion) | `'auto' \| 'global' \| 'home' \| string \| string[] (deprecated)` | `'auto'` | +| [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config/maxDuration) | `number` | Set by deployment platform | ## Version History diff --git a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/preferredRegion.mdx b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/preferredRegion.mdx index e307a6558b74..6998aba9a77a 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/preferredRegion.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/preferredRegion.mdx @@ -1,8 +1,10 @@ --- -title: preferredRegion +title: preferredRegion (deprecated) description: API reference for the preferredRegion route segment config option. --- +> **Deprecated:** The `preferredRegion` route segment config is deprecated. Remove the `preferredRegion` export from your route files. See the [deprecation message](/docs/messages/preferred-region-deprecated) for details. + The `preferredRegion` option allows you to specify the preferred deployment region for a route segment. This value is passed to your deployment platform. ```tsx filename="layout.tsx | page.tsx | route.ts" switcher @@ -24,7 +26,7 @@ export const preferredRegion = // string || string[] ## Vercel -If deploying Next.js on Vercel, regions are only supported if `export const runtime = 'edge'` is set. The following options can be passed: +If deploying Next.js on Vercel, regions were previously only supported with `export const runtime = 'edge'`, which is now [deprecated](/docs/messages/edge-runtime-deprecated). The following options can be passed: - **`'auto'`** (default): Uses the default region. - **`'global'`**: Prefer deploying the route to all availableregions. diff --git a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/runtime.mdx b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/runtime.mdx index 7634c9240d69..8244d7be193a 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/runtime.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/runtime.mdx @@ -7,18 +7,18 @@ The `runtime` option allows you to select the JavaScript runtime used for render ```tsx filename="layout.tsx | page.tsx | route.ts" switcher export const runtime = 'nodejs' -// 'nodejs' | 'edge' +// 'nodejs' ``` ```js filename="layout.js | page.js | route.js" switcher export const runtime = 'nodejs' -// 'nodejs' | 'edge' +// 'nodejs' ``` - **`'nodejs'`** (default) -- **`'edge'`** +- **`'edge'`** (deprecated) > **Good to know**: > -> - Using `runtime: 'edge'` is **not supported** for Cache Components. +> - The Edge Runtime is deprecated. Remove the `runtime` export from your route files. See [Edge Runtime Deprecated](/docs/messages/edge-runtime-deprecated). > - This option cannot be used in [Proxy](/docs/app/api-reference/file-conventions/proxy). diff --git a/docs/01-app/03-api-reference/03-file-conventions/route.mdx b/docs/01-app/03-api-reference/03-file-conventions/route.mdx index 5e80618c6b09..5a95a2500dfe 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/route.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/route.mdx @@ -647,7 +647,7 @@ export const dynamicParams = true export const revalidate = false export const fetchCache = 'auto' export const runtime = 'nodejs' -export const preferredRegion = 'auto' +export const preferredRegion = 'auto' // deprecated ``` ```js filename="app/items/route.js" switcher @@ -656,7 +656,7 @@ export const dynamicParams = true export const revalidate = false export const fetchCache = 'auto' export const runtime = 'nodejs' -export const preferredRegion = 'auto' +export const preferredRegion = 'auto' // deprecated ``` See the [API reference](/docs/app/api-reference/file-conventions/route-segment-config) for more details. diff --git a/docs/01-app/03-api-reference/05-config/01-next-config-js/turbopack.mdx b/docs/01-app/03-api-reference/05-config/01-next-config-js/turbopack.mdx index 3e85c30e21c8..af10aed5741a 100644 --- a/docs/01-app/03-api-reference/05-config/01-next-config-js/turbopack.mdx +++ b/docs/01-app/03-api-reference/05-config/01-next-config-js/turbopack.mdx @@ -229,7 +229,7 @@ In addition, a number of built-in conditions are supported: - `development`: Matches when using `next dev`. - `production`: Matches when using `next build`. - `node`: Matches code that will run on the default Node.js runtime. -- `edge-light`: Matches code that will run on the [Edge runtime](/docs/app/api-reference/edge). +- `edge-light`: Matches code that will run on the [Edge runtime](/docs/app/api-reference/edge) (deprecated). Rules can be an object or an array of objects. An array is often useful for modeling disjoint conditions: diff --git a/docs/01-app/03-api-reference/07-adapters/08-invoking-entrypoints.mdx b/docs/01-app/03-api-reference/07-adapters/08-invoking-entrypoints.mdx index 5fd30e9f44e9..0e258831178f 100644 --- a/docs/01-app/03-api-reference/07-adapters/08-invoking-entrypoints.mdx +++ b/docs/01-app/03-api-reference/07-adapters/08-invoking-entrypoints.mdx @@ -48,7 +48,9 @@ Relevant files in the Next.js core: - [`packages/next/src/build/templates/app-route.ts`](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/app-route.ts) - and [`packages/next/src/build/templates/pages-api.ts`](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/pages-api.ts) -## Edge runtime (`runtime: 'edge'`) +## Edge runtime (`runtime: 'edge'`) (deprecated) + +> The Edge Runtime is [deprecated](/docs/messages/edge-runtime-deprecated). New routes should use the Node.js runtime. Edge entrypoints use the following interface: diff --git a/docs/01-app/03-api-reference/07-adapters/09-output-types.mdx b/docs/01-app/03-api-reference/07-adapters/09-output-types.mdx index 7923f07e1fd6..cc9c8dc47a90 100644 --- a/docs/01-app/03-api-reference/07-adapters/09-output-types.mdx +++ b/docs/01-app/03-api-reference/07-adapters/09-output-types.mdx @@ -15,7 +15,7 @@ The `outputs` object contains arrays of build output types: > **Note:** When `config.output` is set to `'export'`, only `outputs.staticFiles` is populated. All other arrays (`pages`, `appPages`, `pagesApi`, `appRoutes`, `prerenders`) will be empty since the entire application is exported as static files. -For any route output with `runtime: 'edge'`, `edgeRuntime` is included and contains the canonical entry metadata for invoking that output in your edge runtime. +For any route output with `runtime: 'edge'`, `edgeRuntime` is included and contains the canonical entry metadata for invoking that output in your edge runtime. Note that the Edge Runtime is [deprecated](/docs/messages/edge-runtime-deprecated). ## Pages (`outputs.pages`) @@ -38,7 +38,7 @@ React pages from the `pages/` directory: } config: { maxDuration?: number // Maximum duration of the route in seconds - preferredRegion?: string | string[] // Preferred deployment region + preferredRegion?: string | string[] // Preferred deployment region (deprecated) env?: Record // Environment variables (edge runtime only) } } @@ -65,7 +65,7 @@ API routes from `pages/api/`: } config: { maxDuration?: number // Maximum duration of the route in seconds - preferredRegion?: string | string[] // Preferred deployment region + preferredRegion?: string | string[] // Preferred deployment region (deprecated) env?: Record // Environment variables (edge runtime only) } } @@ -92,7 +92,7 @@ React pages from the `app/` directory: } config: { maxDuration?: number // Maximum duration of the route in seconds - preferredRegion?: string | string[] // Preferred deployment region + preferredRegion?: string | string[] // Preferred deployment region (deprecated) env?: Record // Environment variables (edge runtime only) } } @@ -119,7 +119,7 @@ API and metadata routes from the `app/` directory: } config: { maxDuration?: number // Maximum duration of the route in seconds - preferredRegion?: string | string[] // Preferred deployment region + preferredRegion?: string | string[] // Preferred deployment region (deprecated) env?: Record // Environment variables (edge runtime only) } } @@ -194,7 +194,7 @@ Static assets and auto-statically optimized pages: } config: { maxDuration?: number // Maximum duration of the route in seconds - preferredRegion?: string | string[] // Preferred deployment region + preferredRegion?: string | string[] // Preferred deployment region (deprecated) env?: Record // Environment variables (edge runtime only) matchers?: Array<{ source: string // Source pattern diff --git a/errors/edge-runtime-deprecated.mdx b/errors/edge-runtime-deprecated.mdx new file mode 100644 index 000000000000..cbac80507b4e --- /dev/null +++ b/errors/edge-runtime-deprecated.mdx @@ -0,0 +1,23 @@ +--- +title: Edge Runtime Deprecated +--- + +## Why This Warning Occurred + +One or more routes in your application use `export const runtime = 'edge'`, which is deprecated. + +## How to Migrate + +Remove the `runtime` export from your route files: + +```diff +- export const runtime = 'edge' +``` + +The Node.js runtime is the default, so no replacement is needed. + +This applies to all route files that support the `runtime` segment config: `page.ts`, `layout.ts`, `route.ts`, and API routes. + +## Useful Links + +- [Route Segment Config](/docs/app/api-reference/file-conventions/route-segment-config/runtime) diff --git a/errors/preferred-region-deprecated.mdx b/errors/preferred-region-deprecated.mdx new file mode 100644 index 000000000000..b79aed8c5e71 --- /dev/null +++ b/errors/preferred-region-deprecated.mdx @@ -0,0 +1,21 @@ +--- +title: preferredRegion Deprecated +--- + +## Why This Warning Occurred + +One or more routes in your application use `export const preferredRegion`, which is deprecated. + +## How to Migrate + +Remove the `preferredRegion` export from your route files: + +```diff +- export const preferredRegion = 'home' +``` + +This applies to all route files that support the `preferredRegion` segment config: `page.ts`, `layout.ts`, and `route.ts`. + +## Useful Links + +- [Route Segment Config](/docs/app/api-reference/file-conventions/route-segment-config/preferredRegion) diff --git a/packages/next/src/build/analysis/get-page-static-info.ts b/packages/next/src/build/analysis/get-page-static-info.ts index 7e0e7dc92fd6..0862916edc14 100644 --- a/packages/next/src/build/analysis/get-page-static-info.ts +++ b/packages/next/src/build/analysis/get-page-static-info.ts @@ -15,6 +15,10 @@ import { import { tryToParsePath } from '../../lib/try-to-parse-path' import { isAPIRoute } from '../../lib/is-api-route' import { isEdgeRuntime } from '../../lib/is-edge-runtime' +import { + warnAboutEdgeRuntime, + warnAboutPreferredRegion, +} from '../warn-about-edge-runtime' import { RSC_MODULE_TYPES } from '../../shared/lib/constants' import type { RSCMeta } from '../webpack/loaders/get-module-build-info' import { PAGE_TYPES } from '../../lib/page-types' @@ -721,6 +725,14 @@ export async function getAppPageStaticInfo({ ) } + if (isEdgeRuntime(config.runtime)) { + warnAboutEdgeRuntime() + } + + if (config.preferredRegion !== undefined) { + warnAboutPreferredRegion() + } + return { type: PAGE_TYPES.APP, rsc, @@ -831,6 +843,14 @@ export async function getPagesPageStaticInfo({ } } + if (isEdgeRuntime(resolvedRuntime)) { + warnAboutEdgeRuntime() + } + + if (config.config?.regions !== undefined) { + warnAboutPreferredRegion() + } + return { type: PAGE_TYPES.PAGES, getStaticProps, diff --git a/packages/next/src/build/warn-about-edge-runtime.ts b/packages/next/src/build/warn-about-edge-runtime.ts new file mode 100644 index 000000000000..e23a1c3efc1f --- /dev/null +++ b/packages/next/src/build/warn-about-edge-runtime.ts @@ -0,0 +1,19 @@ +import * as Log from './output/log' + +export function warnAboutEdgeRuntime() { + // Webpack build workers each run in a separate process with their own + // warnOnce cache, so the same warning would be emitted once per worker. + // Suppress in workers; the main build process emits the warning once during + // the "Collecting page data" phase. + if (process.env.NEXT_PRIVATE_BUILD_WORKER) return + Log.warnOnce( + `The Edge Runtime is deprecated. You can use the "nodejs" runtime instead. Learn more: https://nextjs.org/docs/messages/edge-runtime-deprecated` + ) +} + +export function warnAboutPreferredRegion() { + if (process.env.NEXT_PRIVATE_BUILD_WORKER) return + Log.warnOnce( + `The "preferredRegion" route segment config is deprecated. Learn more: https://nextjs.org/docs/messages/preferred-region-deprecated` + ) +} diff --git a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts index 0d559d0faf80..c679d218940a 100644 --- a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts +++ b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts @@ -78,6 +78,7 @@ checkFields { - try { - const partytownDeps: NecessaryDependencies = hasNecessaryDependencies(dir, [ - { - file: '@builder.io/partytown', - pkg: '@builder.io/partytown', - exportsRestrict: false, - }, - ]) + const partytownDeps: NecessaryDependencies = hasNecessaryDependencies(dir, [ + { + file: '@builder.io/partytown', + pkg: '@builder.io/partytown', + exportsRestrict: false, + }, + ]) - if (partytownDeps.missing?.length > 0) { - await missingDependencyError(dir) - } else { - try { - await copyPartytownStaticFiles(partytownDeps, targetDir) - } catch (err) { - Log.warn( - `Partytown library files could not be copied to the static directory. Please ensure that ${bold( - cyan('@builder.io/partytown') - )} is installed as a dependency.` - ) - } - } - } catch (err) { - // Don't show a stack trace when there is an error due to missing dependencies - if (err instanceof FatalError) { - console.error(err.message) - // Throw to allow finally blocks to run (e.g., telemetry flush) - throw err + if (partytownDeps.missing?.length > 0) { + await missingDependencyError(dir) + } else { + try { + await copyPartytownStaticFiles(partytownDeps, targetDir) + } catch (err) { + Log.warn( + `Partytown library files could not be copied to the static directory. Please ensure that ${bold( + cyan('@builder.io/partytown') + )} is installed as a dependency.` + ) } - throw err } } diff --git a/packages/next/src/server/dev/turbopack-utils.ts b/packages/next/src/server/dev/turbopack-utils.ts index 0b7962b23264..f195d6761ccf 100644 --- a/packages/next/src/server/dev/turbopack-utils.ts +++ b/packages/next/src/server/dev/turbopack-utils.ts @@ -15,6 +15,7 @@ import { HMR_MESSAGE_SENT_TO_BROWSER, } from './hot-reloader-types' import * as Log from '../../build/output/log' +import { warnAboutEdgeRuntime } from '../../build/warn-about-edge-runtime' import type { PropagateToWorkersField } from '../lib/router-utils/types' import type { TurbopackManifestLoader } from '../../shared/lib/turbopack/manifest-loader' import type { AppRoute, Entrypoints, PageRoute } from '../../build/swc/types' @@ -227,6 +228,7 @@ export async function handleRouteType({ await manifestLoader.loadBuildManifest(page) await manifestLoader.loadPagesManifest(page) if (type === 'edge') { + warnAboutEdgeRuntime() await manifestLoader.loadMiddlewareManifest(page, 'pages') } else { manifestLoader.deleteMiddlewareManifest(serverKey) @@ -320,6 +322,7 @@ export async function handleRouteType({ await manifestLoader.loadPagesManifest(page) if (type === 'edge') { + warnAboutEdgeRuntime() await manifestLoader.loadMiddlewareManifest(page, 'pages') } else { manifestLoader.deleteMiddlewareManifest(key) @@ -373,6 +376,7 @@ export async function handleRouteType({ const type = writtenEndpoint.type if (type === 'edge') { + warnAboutEdgeRuntime() manifestLoader.loadMiddlewareManifest(page, 'app') } else { manifestLoader.deleteMiddlewareManifest(key) @@ -404,6 +408,7 @@ export async function handleRouteType({ manifestLoader.loadAppPathsManifest(page) if (type === 'edge') { + warnAboutEdgeRuntime() manifestLoader.loadMiddlewareManifest(page, 'app') } else { manifestLoader.deleteMiddlewareManifest(key) diff --git a/packages/next/src/server/typescript/rules/config.ts b/packages/next/src/server/typescript/rules/config.ts index 8801c9ddf361..5c2d37c180ed 100644 --- a/packages/next/src/server/typescript/rules/config.ts +++ b/packages/next/src/server/typescript/rules/config.ts @@ -61,12 +61,12 @@ const API_DOCS: Record< }, preferredRegion: { description: - 'Specify the perferred region that this layout or page should be deployed to. If the region option is not specified, it inherits the option from the nearest parent layout. The root defaults to `"auto"`.\n\nYou can also specify a region, such as "iad1", or an array of regions, such as `["iad1", "sfo1"]`.', + '@deprecated\\n\\nThe `preferredRegion` route segment config is deprecated. Remove this export.', options: { '"auto"': - 'Next.js will first deploy to the `"home"` region. Then if it doesn\'t detect any waterfall requests after a few requests, it can upgrade that route, to be deployed globally. If it detects any waterfall requests after that, it can eventually downgrade back to `"home`".', - '"global"': 'Prefer deploying globally.', - '"home"': 'Prefer deploying to the Home region.', + '@deprecated\\n\\nNext.js will first deploy to the `"home"` region. Then if it doesn\'t detect any waterfall requests after a few requests, it can upgrade that route, to be deployed globally. If it detects any waterfall requests after that, it can eventually downgrade back to `"home`".', + '"global"': '@deprecated\\n\\nPrefer deploying globally.', + '"home"': '@deprecated\\n\\nPrefer deploying to the Home region.', }, link: 'https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#preferredregion', isValid: (value: string) => { @@ -124,7 +124,7 @@ const API_DOCS: Record< 'The `runtime` option controls the preferred runtime to render this route.', options: { '"nodejs"': 'Prefer the Node.js runtime.', - '"edge"': 'Prefer the Edge runtime.', + '"edge"': `@deprecated\n\nThe Edge Runtime is deprecated. Use \`"nodejs"\` instead.`, '"experimental-edge"': `@deprecated\n\nThis option is no longer experimental. Use \`edge\` instead.`, } satisfies DocsOptionsObject< FullAppSegmentConfig['runtime'] | 'experimental-edge' diff --git a/test/cache-components-tests-manifest.json b/test/cache-components-tests-manifest.json index 0470ac161e51..049df5cbfcd3 100644 --- a/test/cache-components-tests-manifest.json +++ b/test/cache-components-tests-manifest.json @@ -311,6 +311,7 @@ "test/e2e/edge-configurable-runtime/index.test.ts", "test/e2e/edge-pages-support/edge-document.test.ts", "test/e2e/edge-pages-support/index.test.ts", + "test/e2e/edge-runtime-deprecated/edge-runtime-deprecated.test.ts", "test/e2e/edge-runtime-uses-edge-light-import-specifier-for-packages/edge-runtime-uses-edge-light-import-specifier-for-packages.test.ts", "test/e2e/import-conditions/import-conditions.test.ts", "test/e2e/manual-client-base-path/index.test.ts", diff --git a/test/e2e/edge-runtime-deprecated/app/edge-route/route.ts b/test/e2e/edge-runtime-deprecated/app/edge-route/route.ts new file mode 100644 index 000000000000..1a9d280ae635 --- /dev/null +++ b/test/e2e/edge-runtime-deprecated/app/edge-route/route.ts @@ -0,0 +1,5 @@ +export const runtime = 'edge' + +export function GET() { + return new Response('edge route') +} diff --git a/test/e2e/edge-runtime-deprecated/app/layout.tsx b/test/e2e/edge-runtime-deprecated/app/layout.tsx new file mode 100644 index 000000000000..dbce4ea8e3ae --- /dev/null +++ b/test/e2e/edge-runtime-deprecated/app/layout.tsx @@ -0,0 +1,11 @@ +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/test/e2e/edge-runtime-deprecated/app/page.tsx b/test/e2e/edge-runtime-deprecated/app/page.tsx new file mode 100644 index 000000000000..966ce65038a8 --- /dev/null +++ b/test/e2e/edge-runtime-deprecated/app/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

Hello

+} diff --git a/test/e2e/edge-runtime-deprecated/edge-runtime-deprecated.test.ts b/test/e2e/edge-runtime-deprecated/edge-runtime-deprecated.test.ts new file mode 100644 index 000000000000..f3f18de19fe3 --- /dev/null +++ b/test/e2e/edge-runtime-deprecated/edge-runtime-deprecated.test.ts @@ -0,0 +1,37 @@ +import { nextTestSetup } from 'e2e-utils' +import { retry } from 'next-test-utils' + +const EXPECTED_WARNING = + 'The Edge Runtime is deprecated. You can use the "nodejs" runtime instead.' + +describe('edge-runtime-deprecated', () => { + const { next, isNextDev } = nextTestSetup({ + files: __dirname, + }) + + it('should warn about deprecated edge runtime', async () => { + if (isNextDev) { + // In dev mode, the warning fires when the edge route is first compiled. + await next.fetch('/edge-route') + } + + await retry(async () => { + expect(next.cliOutput).toContain(EXPECTED_WARNING) + }) + }) + + it('should only warn once', async () => { + if (isNextDev) { + // Trigger compilation of the edge route again (already compiled, but + // another request ensures no duplicate warnings). + await next.fetch('/edge-route') + } + + await retry(async () => { + expect(next.cliOutput).toContain(EXPECTED_WARNING) + }) + + const occurrences = next.cliOutput.split(EXPECTED_WARNING).length - 1 + expect(occurrences).toBe(1) + }) +}) diff --git a/test/production/next-server-nft/next-server-nft.test.ts b/test/production/next-server-nft/next-server-nft.test.ts index 329534b693e0..8b01ec4303fb 100644 --- a/test/production/next-server-nft/next-server-nft.test.ts +++ b/test/production/next-server-nft/next-server-nft.test.ts @@ -243,7 +243,6 @@ async function readNormalizedNFT(next, name) { "/node_modules/next/dist/lib/download-swc.js", "/node_modules/next/dist/lib/error-telemetry-utils.js", "/node_modules/next/dist/lib/fallback.js", - "/node_modules/next/dist/lib/fatal-error.js", "/node_modules/next/dist/lib/file-exists.js", "/node_modules/next/dist/lib/find-config.js", "/node_modules/next/dist/lib/find-pages-dir.js", diff --git a/test/production/preferred-region-deprecated/app/layout.tsx b/test/production/preferred-region-deprecated/app/layout.tsx new file mode 100644 index 000000000000..dbce4ea8e3ae --- /dev/null +++ b/test/production/preferred-region-deprecated/app/layout.tsx @@ -0,0 +1,11 @@ +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/test/production/preferred-region-deprecated/app/page.tsx b/test/production/preferred-region-deprecated/app/page.tsx new file mode 100644 index 000000000000..966ce65038a8 --- /dev/null +++ b/test/production/preferred-region-deprecated/app/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

Hello

+} diff --git a/test/production/preferred-region-deprecated/app/region-route/route.ts b/test/production/preferred-region-deprecated/app/region-route/route.ts new file mode 100644 index 000000000000..f95d82ba4020 --- /dev/null +++ b/test/production/preferred-region-deprecated/app/region-route/route.ts @@ -0,0 +1,5 @@ +export const preferredRegion = 'home' + +export function GET() { + return new Response('region route') +} diff --git a/test/production/preferred-region-deprecated/preferred-region-deprecated.test.ts b/test/production/preferred-region-deprecated/preferred-region-deprecated.test.ts new file mode 100644 index 000000000000..b2bda0c8cd05 --- /dev/null +++ b/test/production/preferred-region-deprecated/preferred-region-deprecated.test.ts @@ -0,0 +1,26 @@ +import { nextTestSetup } from 'e2e-utils' +import { retry } from 'next-test-utils' + +const EXPECTED_WARNING = + 'The "preferredRegion" route segment config is deprecated.' + +describe('preferred-region-deprecated', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should warn about deprecated preferredRegion', async () => { + await retry(async () => { + expect(next.cliOutput).toContain(EXPECTED_WARNING) + }) + }) + + it('should only warn once', async () => { + await retry(async () => { + expect(next.cliOutput).toContain(EXPECTED_WARNING) + }) + + const occurrences = next.cliOutput.split(EXPECTED_WARNING).length - 1 + expect(occurrences).toBe(1) + }) +}) diff --git a/turbo.json b/turbo.json index d430ed75ad1a..2ee6357f932c 100644 --- a/turbo.json +++ b/turbo.json @@ -20,7 +20,7 @@ "dependsOn": ["^test-storybook"] }, "pack-for-isolated-tests": { - "dependsOn": ["build"], + "inputs": ["$TURBO_DEFAULT$", "dist/**"], "outputs": ["packed.tgz"] }, "typescript": {},