diff --git a/packages/next/src/build/app-segments/app-segment-config.ts b/packages/next/src/build/app-segments/app-segment-config.ts index b82623a9a100d..aaed6dbfd0494 100644 --- a/packages/next/src/build/app-segments/app-segment-config.ts +++ b/packages/next/src/build/app-segments/app-segment-config.ts @@ -55,7 +55,7 @@ export const AppSegmentConfigSchema = z.object({ /** * The runtime to use for the page. */ - runtime: z.enum(['edge', 'nodejs', 'experimental-edge']).optional(), + runtime: z.enum(['edge', 'nodejs']).optional(), /** * The maximum duration for the page in seconds. @@ -109,7 +109,7 @@ export type AppSegmentConfig = { /** * The runtime to use for the page. */ - runtime?: 'edge' | 'nodejs' | 'experimental-edge' + runtime?: 'edge' | 'nodejs' /** * The maximum duration for the page in seconds. diff --git a/packages/next/src/build/app-segments/collect-app-segments.ts b/packages/next/src/build/app-segments/collect-app-segments.ts index 3ba166a53189b..b907c4b409027 100644 --- a/packages/next/src/build/app-segments/collect-app-segments.ts +++ b/packages/next/src/build/app-segments/collect-app-segments.ts @@ -24,20 +24,6 @@ import { getLayoutOrPageModule } from '../../server/lib/app-dir-module' type GenerateStaticParams = (options: { params?: Params }) => Promise -/** - * Filters out segments that don't contribute to static generation. - * - * @param segments the segments to filter - * @returns the filtered segments - */ -function filterSegments(segments: AppSegment[]) { - return segments.filter((result) => { - return ( - result.config || result.generateStaticParams || result.isDynamicSegment - ) - }) -} - /** * Parses the app config and attaches it to the segment. */ @@ -60,6 +46,13 @@ function attach(segment: AppSegment, userland: unknown) { ) { segment.generateStaticParams = userland.generateStaticParams as GenerateStaticParams + + // Validate that `generateStaticParams` makes sense in this context. + if (segment.config?.runtime === 'edge') { + throw new Error( + 'Edge runtime is not supported with `generateStaticParams`.' + ) + } } } @@ -112,7 +105,7 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) { current = parallelRoutes.children } - return filterSegments(segments) + return segments } /** @@ -154,7 +147,7 @@ function collectAppRouteSegments( // Extract the segment config from the userland module. attach(segment, routeModule.userland) - return filterSegments(segments) + return segments } /** diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 1114833899d7f..bde8a1ca3fbd3 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -76,6 +76,7 @@ import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { getRuntimeContext } from '../server/web/sandbox' import { isClientReference } from '../lib/client-reference' import { withStaticGenerationStore } from '../server/async-storage/with-static-generation-store' +import type { CacheHandler } from '../server/lib/incremental-cache' import { IncrementalCache } from '../server/lib/incremental-cache' import { nodeFs } from '../server/lib/node-fs-methods' import * as ciEnvironment from '../server/ci-info' @@ -1240,12 +1241,20 @@ export async function buildAppStaticPaths({ isAppPPRFallbacksEnabled: boolean | undefined buildId: string }): Promise { - ComponentMod.patchFetch() + if ( + segments.some((generate) => generate.config?.dynamicParams === true) && + nextConfigOutput === 'export' + ) { + throw new Error( + '"dynamicParams: true" cannot be used with "output: export". See more info here: https://nextjs.org/docs/app/building-your-application/deploying/static-exports' + ) + } - let CacheHandler: any + ComponentMod.patchFetch() + let CurCacheHandler: typeof CacheHandler | undefined if (cacheHandler) { - CacheHandler = interopDefault( + CurCacheHandler = interopDefault( await import(formatDynamicImportPath(dir, cacheHandler)).then( (mod) => mod.default || mod ) @@ -1267,7 +1276,7 @@ export async function buildAppStaticPaths({ notFoundRoutes: [], preview: null as any, // `preview` is special case read in next-dev-server }), - CurCacheHandler: CacheHandler, + CurCacheHandler, requestHeaders, minimalMode: ciEnvironment.hasNextSupport, }) @@ -1361,15 +1370,6 @@ export async function buildAppStaticPaths({ } ) - if ( - segments.some((generate) => generate.config?.dynamicParams === true) && - nextConfigOutput === 'export' - ) { - throw new Error( - '"dynamicParams: true" cannot be used with "output: export". See more info here: https://nextjs.org/docs/app/building-your-application/deploying/static-exports' - ) - } - for (const segment of segments) { // Check to see if there are any missing params for segments that have // dynamicParams set to false. diff --git a/packages/next/src/server/dev/static-paths-worker.ts b/packages/next/src/server/dev/static-paths-worker.ts index 82cede435e819..cde0bdd14d44c 100644 --- a/packages/next/src/server/dev/static-paths-worker.ts +++ b/packages/next/src/server/dev/static-paths-worker.ts @@ -18,6 +18,7 @@ import { checkIsRoutePPREnabled, type ExperimentalPPRConfig, } from '../lib/experimental/ppr' +import { InvariantError } from '../../shared/lib/invariant-error' type RuntimeConfig = { pprConfig: ExperimentalPPRConfig | undefined @@ -80,14 +81,6 @@ export async function loadStaticPaths({ isAppPath, }) - if (!components.getStaticPaths && !isAppPath) { - // we shouldn't get to this point since the worker should - // only be called for SSG pages with getStaticPaths - throw new Error( - `Invariant: failed to load page with getStaticPaths for ${pathname}` - ) - } - if (isAppPath) { const segments = await collectSegments(components) @@ -95,7 +88,7 @@ export async function loadStaticPaths({ isAppPageRouteModule(components.routeModule) && checkIsRoutePPREnabled(config.pprConfig, reduceAppConfig(segments)) - return await buildAppStaticPaths({ + return buildAppStaticPaths({ dir, page: pathname, dynamicIO: config.dynamicIO, @@ -113,9 +106,15 @@ export async function loadStaticPaths({ isAppPPRFallbacksEnabled, buildId, }) + } else if (!components.getStaticPaths) { + // We shouldn't get to this point since the worker should only be called for + // SSG pages with getStaticPaths. + throw new InvariantError( + `Failed to load page with getStaticPaths for ${pathname}` + ) } - return await buildStaticPaths({ + return buildStaticPaths({ page: pathname, getStaticPaths: components.getStaticPaths, configFileName: config.configFileName, diff --git a/test/e2e/app-dir/app-css/app/dashboard/page.js b/test/e2e/app-dir/app-css/app/dashboard/page.js index 5a3e15c25c935..588a0bf594643 100644 --- a/test/e2e/app-dir/app-css/app/dashboard/page.js +++ b/test/e2e/app-dir/app-css/app/dashboard/page.js @@ -13,4 +13,4 @@ export default function DashboardPage(props) { ) } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/app-edge-root-layout/app/layout.js b/test/e2e/app-dir/app-edge-root-layout/app/layout.js index 76ddc3fd4fa08..6f828ac76e275 100644 --- a/test/e2e/app-dir/app-edge-root-layout/app/layout.js +++ b/test/e2e/app-dir/app-edge-root-layout/app/layout.js @@ -6,4 +6,4 @@ export default function layout({ children }) { ) } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/app-routes/app/edge/advanced/body/json/route.ts b/test/e2e/app-dir/app-routes/app/edge/advanced/body/json/route.ts index e55869eeba3d9..a563f930bd365 100644 --- a/test/e2e/app-dir/app-routes/app/edge/advanced/body/json/route.ts +++ b/test/e2e/app-dir/app-routes/app/edge/advanced/body/json/route.ts @@ -1,7 +1,7 @@ import type { NextRequest } from 'next/server' import { withRequestMeta } from '../../../../../helpers' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export async function POST(request: NextRequest) { const body = await request.json() diff --git a/test/e2e/app-dir/app-routes/app/edge/advanced/body/streaming/route.ts b/test/e2e/app-dir/app-routes/app/edge/advanced/body/streaming/route.ts index af98c4c380166..72e338aeeaa94 100644 --- a/test/e2e/app-dir/app-routes/app/edge/advanced/body/streaming/route.ts +++ b/test/e2e/app-dir/app-routes/app/edge/advanced/body/streaming/route.ts @@ -1,6 +1,6 @@ import type { NextRequest } from 'next/server' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export async function POST(request: NextRequest) { const reader = request.body?.getReader() diff --git a/test/e2e/app-dir/app-routes/app/edge/advanced/body/text/route.ts b/test/e2e/app-dir/app-routes/app/edge/advanced/body/text/route.ts index aa39c6f22529a..7552b0ab2439c 100644 --- a/test/e2e/app-dir/app-routes/app/edge/advanced/body/text/route.ts +++ b/test/e2e/app-dir/app-routes/app/edge/advanced/body/text/route.ts @@ -1,7 +1,7 @@ import type { NextRequest } from 'next/server' import { withRequestMeta } from '../../../../../helpers' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export async function POST(request: NextRequest) { const body = await request.text() diff --git a/test/e2e/app-dir/app-routes/app/edge/advanced/query/route.ts b/test/e2e/app-dir/app-routes/app/edge/advanced/query/route.ts index 15ed371f6848e..95c25743f35d8 100644 --- a/test/e2e/app-dir/app-routes/app/edge/advanced/query/route.ts +++ b/test/e2e/app-dir/app-routes/app/edge/advanced/query/route.ts @@ -1,7 +1,7 @@ import { withRequestMeta } from '../../../../helpers' import { NextRequest } from 'next/server' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export async function GET(request: NextRequest): Promise { const { searchParams } = request.nextUrl diff --git a/test/e2e/app-dir/app-routes/app/edge/headers/route.ts b/test/e2e/app-dir/app-routes/app/edge/headers/route.ts index 7a6833be3d22a..6647fb4c12915 100644 --- a/test/e2e/app-dir/app-routes/app/edge/headers/route.ts +++ b/test/e2e/app-dir/app-routes/app/edge/headers/route.ts @@ -2,7 +2,7 @@ import { headers } from 'next/headers' import { NextResponse } from 'next/server' import { getRequestMeta } from '../../../helpers' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export async function GET() { const meta = getRequestMeta(await headers()) diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/body/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/body/page.js index faaa6e61e5478..7dbf025136058 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/body/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/body/page.js @@ -1,4 +1,4 @@ -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default async function Page() { const data = await fetch( diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/encoding/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/encoding/page.js index 06e180abd09f4..e0e4e00e0e22a 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/encoding/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/encoding/page.js @@ -1,4 +1,4 @@ -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default async function Page() { const data = await fetch( diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/no-store/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/no-store/page.js index bf54997d27663..22ec8c03235e3 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/no-store/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/no-store/page.js @@ -1,6 +1,6 @@ import { cache, use } from 'react' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default function Page() { const getData = cache(() => diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method-request/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method-request/page.js index 3361a86b1b3d1..e441a7e474f73 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method-request/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method-request/page.js @@ -1,4 +1,4 @@ -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default async function Page() { const data = await fetch( diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method/page.js index 0ca937144c965..219fe314cabc9 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/post-method/page.js @@ -1,6 +1,6 @@ import { fetchRetry } from '../../../lib/fetch-retry' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default async function Page() { const data = await fetchRetry( diff --git a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/revalidate-3/page.js b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/revalidate-3/page.js index e78de6628c86e..1f015f816e89e 100644 --- a/test/e2e/app-dir/app-static/app/variable-revalidate-edge/revalidate-3/page.js +++ b/test/e2e/app-dir/app-static/app/variable-revalidate-edge/revalidate-3/page.js @@ -1,6 +1,6 @@ import { cache, use } from 'react' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export const dynamic = 'force-static' export default function Page() { diff --git a/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js b/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js index 8ffa7eec6cb8c..475a4b3c25d6e 100644 --- a/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js +++ b/test/e2e/app-dir/app/app/(rootonly)/dashboard/hello/page.js @@ -6,5 +6,5 @@ export default function HelloPage(props) { ) } -export const runtime = 'experimental-edge' +export const runtime = 'edge' export const preferredRegion = ['iad1', 'sfo1'] diff --git a/test/e2e/app-dir/app/app/slow-page-no-loading/page.js b/test/e2e/app-dir/app/app/slow-page-no-loading/page.js index 965ac1f019edc..4ceba1aeb381e 100644 --- a/test/e2e/app-dir/app/app/slow-page-no-loading/page.js +++ b/test/e2e/app-dir/app/app/slow-page-no-loading/page.js @@ -13,5 +13,5 @@ export default function SlowPage(props) { return

{data.message}

} -export const runtime = 'experimental-edge' +export const runtime = 'edge' export const preferredRegion = 'global' diff --git a/test/e2e/app-dir/app/app/slow-page-with-loading/page.js b/test/e2e/app-dir/app/app/slow-page-with-loading/page.js index 71854491d1280..69d1928970433 100644 --- a/test/e2e/app-dir/app/app/slow-page-with-loading/page.js +++ b/test/e2e/app-dir/app/app/slow-page-with-loading/page.js @@ -13,4 +13,4 @@ export default function SlowPage(props) { return

{data.message}

} -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/app/app/test-page/page.js b/test/e2e/app-dir/app/app/test-page/page.js index ff78b5e67c14c..a707e77382849 100644 --- a/test/e2e/app-dir/app/app/test-page/page.js +++ b/test/e2e/app-dir/app/app/test-page/page.js @@ -2,5 +2,5 @@ export default function Page() { return

Page

} -export const runtime = 'experimental-edge' +export const runtime = 'edge' export const preferredRegion = 'home' diff --git a/test/e2e/app-dir/metadata/app/basic-edge/page.tsx b/test/e2e/app-dir/metadata/app/basic-edge/page.tsx index a54fd7bd4e72f..6aa3fd5913282 100644 --- a/test/e2e/app-dir/metadata/app/basic-edge/page.tsx +++ b/test/e2e/app-dir/metadata/app/basic-edge/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from 'next' import Link from 'next/link' import Client from './client' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default function Page() { return ( diff --git a/test/e2e/app-dir/next-font/app/(preload)/page.js b/test/e2e/app-dir/next-font/app/(preload)/page.js index 64e1768d9e36c..fab157e5961ae 100644 --- a/test/e2e/app-dir/next-font/app/(preload)/page.js +++ b/test/e2e/app-dir/next-font/app/(preload)/page.js @@ -13,4 +13,4 @@ export default function HomePage() { ) } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/next-image/app/legacy-edge-runtime/layout.js b/test/e2e/app-dir/next-image/app/legacy-edge-runtime/layout.js index 7fdd2e1fcfe19..a98ab29e02ea0 100644 --- a/test/e2e/app-dir/next-image/app/legacy-edge-runtime/layout.js +++ b/test/e2e/app-dir/next-image/app/legacy-edge-runtime/layout.js @@ -1,7 +1,7 @@ import Image from 'next/legacy/image' import testPng from '../../images/test.png' -export const runtime = 'experimental-edge' +export const runtime = 'edge' export default function LegacyEdgeLayout({ children }) { return ( diff --git a/test/e2e/app-dir/next-image/app/page.js b/test/e2e/app-dir/next-image/app/page.js index 5402e817dd4c0..1caffc0bf5db6 100644 --- a/test/e2e/app-dir/next-image/app/page.js +++ b/test/e2e/app-dir/next-image/app/page.js @@ -19,4 +19,4 @@ export default function Page() { ) } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js index 6f2b162798eb9..91f7bd0199df1 100644 --- a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js +++ b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/[id]/page.js @@ -2,4 +2,4 @@ export default function page() { return 'dynamic route [id] page' } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js index 356b786c602f3..17cf8f0f419e6 100644 --- a/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js +++ b/test/e2e/app-dir/rsc-basic/app/edge/dynamic/page.js @@ -2,4 +2,4 @@ export default function page() { return 'dynamic route index page' } -export const runtime = 'experimental-edge' +export const runtime = 'edge' diff --git a/test/e2e/app-dir/set-cookies/app/api/app/experimental-edge/route.js b/test/e2e/app-dir/set-cookies/app/api/app/experimental-edge/route.js deleted file mode 100644 index 6ba240c8e899c..0000000000000 --- a/test/e2e/app-dir/set-cookies/app/api/app/experimental-edge/route.js +++ /dev/null @@ -1,12 +0,0 @@ -export const runtime = 'experimental-edge' - -import cookies from '../../../../cookies.mjs' - -export async function GET() { - const headers = new Headers() - for (const cookie of cookies) { - headers.append('set-cookie', cookie) - } - - return new Response(null, { headers }) -} diff --git a/test/e2e/app-dir/set-cookies/set-cookies.test.ts b/test/e2e/app-dir/set-cookies/set-cookies.test.ts index 12f5ba8851c24..ab4f3d886d5f3 100644 --- a/test/e2e/app-dir/set-cookies/set-cookies.test.ts +++ b/test/e2e/app-dir/set-cookies/set-cookies.test.ts @@ -21,28 +21,28 @@ describe('set-cookies', () => { return } - describe.each(['edge', 'experimental-edge', 'node'])( - 'for %s runtime', - (runtime) => { - describe.each(['pages', 'app'])('for /%s', (dir) => { - it('should set two set-cookie headers', async () => { - let res = await next.fetch(`/api/${dir}/${runtime}`) + describe.each([ + { dir: 'pages', runtimes: ['edge', 'experimental-edge', 'node'] }, + { dir: 'app', runtimes: ['edge', 'node'] }, + ])('for /$dir', ({ dir, runtimes }) => { + describe.each(runtimes)('for %s runtime', (runtime) => { + it('should set two set-cookie headers', async () => { + let res = await next.fetch(`/api/${dir}/${runtime}`) - let headers = getSetCookieHeaders(res) + let headers = getSetCookieHeaders(res) - expect(headers).toHaveLength(2) - expect(headers).toEqual(cookies) + expect(headers).toHaveLength(2) + expect(headers).toEqual(cookies) - res = await next.fetch( - `/api/${dir}/${runtime}?next-config-headers=true` - ) + res = await next.fetch( + `/api/${dir}/${runtime}?next-config-headers=true` + ) - headers = getSetCookieHeaders(res) + headers = getSetCookieHeaders(res) - expect(headers).toHaveLength(4) - expect(headers).toEqual([...nextConfigHeaders, ...cookies]) - }) + expect(headers).toHaveLength(4) + expect(headers).toEqual([...nextConfigHeaders, ...cookies]) }) - } - ) + }) + }) })