From 6aae39c759362f3f1689072b5b261a7d2b2a66a5 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 14 Dec 2022 19:52:52 +0100 Subject: [PATCH 1/2] Fix app client entry key for windows (#44011) ## Bug The app client entry key was in win32 slashes like `app\blog`, and when we add the new layer checking logic in #43197, `name.startsWith('app/')` doesn't work. Fixes #43854 Fixes #43902 image - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) --- azure-pipelines.yml | 41 +++++++++++++++++-- package.json | 1 + packages/create-next-app/package.json | 1 - packages/font/package.json | 2 +- .../plugins/flight-client-entry-plugin.ts | 5 ++- packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- pnpm-lock.yaml | 9 ++-- .../app-dir-basic/app/blog/page.js | 3 ++ test/integration/app-dir-basic/app/layout.js | 8 ++++ test/integration/app-dir-basic/app/page.js | 3 ++ test/integration/app-dir-basic/next.config.js | 5 +++ .../app-dir-basic/test/index.test.js | 33 +++++++++++++++ 13 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 test/integration/app-dir-basic/app/blog/page.js create mode 100644 test/integration/app-dir-basic/app/layout.js create mode 100644 test/integration/app-dir-basic/app/page.js create mode 100644 test/integration/app-dir-basic/next.config.js create mode 100644 test/integration/app-dir-basic/test/index.test.js diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4ca6149a8a4a791..00377d09a11d1c0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,7 +35,8 @@ variables: PNPM_CACHE_FOLDER: $(Pipeline.Workspace)/.pnpm-store PNPM_VERSION: 7.3.0 NEXT_TELEMETRY_DISABLED: '1' - node_version: ^14.19.0 + node_14_version: ^14.19.0 + node_16_version: ^16.8.0 stages: - stage: Test @@ -46,7 +47,7 @@ stages: steps: - task: NodeTool@0 inputs: - versionSpec: $(node_version) + versionSpec: $(node_14_version) displayName: 'Install Node.js' - bash: | @@ -84,7 +85,7 @@ stages: - task: NodeTool@0 inputs: - versionSpec: $(node_version) + versionSpec: $(node_14_version) displayName: 'Install Node.js' - bash: | @@ -107,3 +108,37 @@ stages: - script: node run-tests.js --type unit condition: eq(variables['isDocsOnly'], 'No') displayName: 'Run tests' + + - job: test_integration_app_dir + pool: + vmImage: 'windows-2019' + steps: + - task: NodeTool@0 + inputs: + versionSpec: $(node_16_version) + displayName: 'Install Node.js' + + - bash: | + node scripts/run-for-change.js --not --type docs --exec echo "##vso[task.setvariable variable=isDocsOnly]No" + displayName: 'Check Docs Only Change' + + - script: npm i -g pnpm@$(PNPM_VERSION) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm config set store-dir $(PNPM_CACHE_FOLDER) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm store path + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm install && pnpm run build + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Install and build' + + - script: npx playwright install chromium + condition: eq(variables['isDocsOnly'], 'No') + + - script: | + node run-tests.js -c 1 test/integration/app-dir-basic/test/index.test.js + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Run tests' diff --git a/package.json b/package.json index 77b2d103f9f78b5..690163865715ff2 100644 --- a/package.json +++ b/package.json @@ -198,6 +198,7 @@ "release": "6.3.1", "request-promise-core": "1.1.2", "resolve-from": "5.0.0", + "rimraf": "3.0.2", "sass": "1.54.0", "seedrandom": "3.0.5", "selenium-webdriver": "4.0.0-beta.4", diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 1527f63482b474e..468e92677fd4386 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -45,7 +45,6 @@ "cross-spawn": "6.0.5", "got": "10.7.0", "prompts": "2.1.0", - "rimraf": "3.0.2", "tar": "4.4.10", "update-check": "1.5.4", "validate-npm-package-name": "3.0.0" diff --git a/packages/font/package.json b/packages/font/package.json index 52f2e95ec396835..ef6a0f145d84429 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -12,7 +12,7 @@ ], "license": "MIT", "scripts": { - "build": "rm -rf dist && pnpm ncc-fontkit && tsc -d -p tsconfig.json", + "build": "rimraf dist && pnpm ncc-fontkit && tsc -d -p tsconfig.json", "prepublishOnly": "cd ../../ && turbo run build", "dev": "pnpm ncc-fontkit && tsc -d -w -p tsconfig.json", "typescript": "tsec --noEmit -p tsconfig.json", diff --git a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts index 57e2ff6ee13a3dd..93a055bd1cf5dec 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -509,7 +509,10 @@ export class FlightClientEntryPlugin { // Add for the client compilation // Inject the entry to the client compiler. if (this.dev) { - const pageKey = COMPILER_NAMES.client + bundlePath + const pageKey = (COMPILER_NAMES.client + bundlePath).replace( + /\\/g, + path.posix.sep + ) if (!entries[pageKey]) { entries[pageKey] = { type: EntryTypes.CHILD_ENTRY, diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 8a39adb18544e6b..917eb33e11974ed 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -12,7 +12,7 @@ "author": "Joe Haddad ", "license": "MIT", "scripts": { - "build": "rm -rf dist && tsc -d -p tsconfig.json", + "build": "rimraf dist && tsc -d -p tsconfig.json", "prepublishOnly": "cd ../../ && turbo run build", "dev": "tsc -d -w -p tsconfig.json", "typescript": "tsec --noEmit -p tsconfig.json" diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 3778d55c68b357b..4c4329d018df971 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -12,7 +12,7 @@ "author": "Joe Haddad ", "license": "MIT", "scripts": { - "build": "rm -rf dist && tsc -d -p tsconfig.json", + "build": "rimraf dist && tsc -d -p tsconfig.json", "prepublishOnly": "cd ../../ && turbo run build", "dev": "tsc -d -w -p tsconfig.json" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2101ea16dbc7a3d..e9d12e0058f4992 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,6 +163,7 @@ importers: release: 6.3.1 request-promise-core: 1.1.2 resolve-from: 5.0.0 + rimraf: 3.0.2 sass: 1.54.0 seedrandom: 3.0.5 selenium-webdriver: 4.0.0-beta.4 @@ -337,6 +338,7 @@ importers: release: 6.3.1 request-promise-core: 1.1.2_request@2.88.2 resolve-from: 5.0.0 + rimraf: 3.0.2 sass: 1.54.0 seedrandom: 3.0.5 selenium-webdriver: 4.0.0-beta.4 @@ -403,7 +405,6 @@ importers: cross-spawn: 6.0.5 got: 10.7.0 prompts: 2.1.0 - rimraf: 3.0.2 tar: 4.4.10 update-check: 1.5.4 validate-npm-package-name: 3.0.0 @@ -425,7 +426,6 @@ importers: cross-spawn: 6.0.5 got: 10.7.0 prompts: 2.1.0 - rimraf: 3.0.2 tar: 4.4.10 update-check: 1.5.4 validate-npm-package-name: 3.0.0 @@ -9805,7 +9805,7 @@ packages: dev: true /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} /concat-stream/1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -14291,6 +14291,7 @@ packages: /is-docker/2.0.0: resolution: {integrity: sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==} engines: {node: '>=8'} + dev: true /is-docker/2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -14642,7 +14643,7 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} dependencies: - is-docker: 2.0.0 + is-docker: 2.2.1 /is-yarn-global/0.3.0: resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} diff --git a/test/integration/app-dir-basic/app/blog/page.js b/test/integration/app-dir-basic/app/blog/page.js new file mode 100644 index 000000000000000..f8a7b0ab425271f --- /dev/null +++ b/test/integration/app-dir-basic/app/blog/page.js @@ -0,0 +1,3 @@ +export default function page() { + return <>blog +} diff --git a/test/integration/app-dir-basic/app/layout.js b/test/integration/app-dir-basic/app/layout.js new file mode 100644 index 000000000000000..747270b45987a2b --- /dev/null +++ b/test/integration/app-dir-basic/app/layout.js @@ -0,0 +1,8 @@ +export default function RootLayout({ children }) { + return ( + + + {children} + + ) +} diff --git a/test/integration/app-dir-basic/app/page.js b/test/integration/app-dir-basic/app/page.js new file mode 100644 index 000000000000000..8c790ee258cfb0c --- /dev/null +++ b/test/integration/app-dir-basic/app/page.js @@ -0,0 +1,3 @@ +export default function page() { + return <>page +} diff --git a/test/integration/app-dir-basic/next.config.js b/test/integration/app-dir-basic/next.config.js new file mode 100644 index 000000000000000..cfa3ac3d7aa94b3 --- /dev/null +++ b/test/integration/app-dir-basic/next.config.js @@ -0,0 +1,5 @@ +module.exports = { + experimental: { + appDir: true, + }, +} diff --git a/test/integration/app-dir-basic/test/index.test.js b/test/integration/app-dir-basic/test/index.test.js new file mode 100644 index 000000000000000..4dd430ba144cfd6 --- /dev/null +++ b/test/integration/app-dir-basic/test/index.test.js @@ -0,0 +1,33 @@ +/* eslint-env jest */ + +import { join } from 'path' +import { runDevSuite, runProdSuite, renderViaHTTP } from 'next-test-utils' + +import webdriver from 'next-webdriver' +const appDir = join(__dirname, '..') + +function runTests(context, env) { + describe('App Dir Basic', () => { + it('should render html properly', async () => { + const indexHtml = await renderViaHTTP(context.appPort, '/') + const blogHtml = await renderViaHTTP(context.appPort, '/blog') + + expect(indexHtml).toContain('page') + expect(blogHtml).toContain('blog') + }) + + it('should hydrate pages properly', async () => { + const browser = await webdriver(context.appPort, '/') + const indexHtml = await browser.waitForElementByCss('body').text() + const url = await browser.url() + await browser.loadPage(url + 'blog') + const blogHtml = await browser.waitForElementByCss('body').text() + + expect(indexHtml).toContain('page') + expect(blogHtml).toContain('blog') + }) + }) +} + +runDevSuite('App Dir Basic', appDir, { runTests }) +runProdSuite('App Dir Basic', appDir, { runTests }) From 35f1a3501a8d61adb00f2048a37da8f3546ba48c Mon Sep 17 00:00:00 2001 From: Damien Simonin Feugas Date: Thu, 15 Dec 2022 10:08:03 +0100 Subject: [PATCH 2/2] feat: enables 'edge' as a possible runtime for API routes (#44045) --- .../react-18/switchable-runtime.md | 42 ++---- docs/api-reference/edge-runtime.md | 2 +- docs/api-routes/edge-api-routes.md | 14 +- .../data-fetching/get-server-side-props.md | 12 ++ .../incremental-static-regeneration.md | 2 +- docs/manifest.json | 2 +- errors/edge-dynamic-code-evaluation.md | 2 +- errors/middleware-upgrade-guide.md | 2 +- .../returning-response-body-in-middleware.md | 2 + examples/with-webassembly/pages/api/edge.ts | 2 +- .../build/analysis/extract-const-value.ts | 2 +- .../build/analysis/get-page-static-info.ts | 52 ++++--- packages/next/build/entries.ts | 12 +- packages/next/build/index.ts | 8 +- packages/next/build/utils.ts | 6 +- packages/next/build/webpack-config.ts | 4 +- .../loaders/next-edge-ssr-loader/render.ts | 2 +- .../loaders/next-serverless-loader/index.ts | 4 +- .../webpack/plugins/flight-types-plugin.ts | 6 +- packages/next/export/index.ts | 7 +- packages/next/lib/constants.ts | 6 +- packages/next/lib/is-api-route.ts | 3 + packages/next/lib/is-edge-runtime.ts | 8 ++ packages/next/server/base-server.ts | 5 +- packages/next/server/config-schema.ts | 3 +- packages/next/server/config-shared.ts | 6 - packages/next/server/next-server.ts | 5 +- packages/next/server/next-typescript.ts | 1 + packages/next/server/router.ts | 3 +- packages/next/server/web-server.ts | 6 +- packages/next/shared/lib/router/router.ts | 3 +- packages/next/types/index.d.ts | 2 +- .../pages/api/dump-headers-edge.js | 2 +- test/e2e/app-dir/app/pages/api/hello.js | 2 +- .../app/pages/api/edge.js | 2 +- .../app/pages/api/index.js | 2 +- .../edge-async-local-storage/index.test.ts | 4 +- .../e2e/edge-can-use-wasm-files/index.test.ts | 2 +- .../app/pages/api/edge.js | 2 +- .../app/pages/api/edge.js | 2 + .../app/pages/index.jsx | 1 + .../edge-configurable-runtime/index.test.ts | 132 ++++++++++++++++++ .../app/pages/api/edge-search-params.js | 2 +- .../app/pages/api/dump-headers-edge.js | 2 +- test/e2e/og-api/app/pages/api/og.js | 2 +- .../app/pages/api/test-cookie-edge.js | 2 +- .../streaming-ssr/pages/api/user/[id].js | 2 +- test/e2e/switchable-runtime/index.test.ts | 21 ++- test/e2e/switchable-runtime/pages/api/edge.js | 2 +- .../e2e/switchable-runtime/pages/api/hello.js | 2 +- .../pages/api/syntax-error-in-dev.js | 2 +- .../pages/api/route.js | 2 +- .../test/index.test.js | 14 +- .../pages/api/route.js | 2 +- .../pages/api/route.js | 2 +- .../test/index.test.js | 18 +-- .../pages/api/route.js | 2 +- .../pages/api/test.js | 3 +- .../pages/api/route.js | 2 +- .../image-generation/app/pages/api/image.jsx | 2 +- test/integration/telemetry/pages/api/og.jsx | 2 +- .../index.test.ts | 2 +- 62 files changed, 318 insertions(+), 157 deletions(-) create mode 100644 packages/next/lib/is-api-route.ts create mode 100644 packages/next/lib/is-edge-runtime.ts create mode 100644 test/e2e/edge-configurable-runtime/app/pages/api/edge.js create mode 100644 test/e2e/edge-configurable-runtime/app/pages/index.jsx create mode 100644 test/e2e/edge-configurable-runtime/index.test.ts diff --git a/docs/advanced-features/react-18/switchable-runtime.md b/docs/advanced-features/react-18/switchable-runtime.md index cdd5268e825ddd7..db5a74e6acb61bb 100644 --- a/docs/advanced-features/react-18/switchable-runtime.md +++ b/docs/advanced-features/react-18/switchable-runtime.md @@ -8,21 +8,6 @@ Next.js has two **server runtimes** where you can render parts of your applicati By default, Next.js uses the Node.js runtime. [Middleware](https://nextjs.org/docs/advanced-features/middleware) and [Edge API Routes](https://nextjs.org/docs/api-routes/edge-api-routes) use the Edge runtime. -## Global Runtime Option - -To configure the runtime for your whole application, you can set the experimental option `runtime` in your `next.config.js` file: - -```js -// next.config.js -module.exports = { - experimental: { - runtime: 'experimental-edge', // 'node.js' (default) | experimental-edge - }, -} -``` - -You can detect which runtime you're using by looking at the `process.env.NEXT_RUNTIME` Environment Variable during runtime, and examining the `options.nextRuntime` variable during compilation. - ## Page Runtime Option On each page, you can optionally export a `runtime` config set to either `'nodejs'` or `'experimental-edge'`: @@ -38,22 +23,21 @@ export const config = { } ``` -When both the per-page runtime and global runtime are set, the per-page runtime overrides the global runtime. If the per-page runtime is _not_ set, the global runtime option will be used. - ## Runtime Differences -|   | Node (Server) | Node (Serverless) | Edge | -| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ----------------- | ---------------- | -| [Cold Boot](https://vercel.com/docs/concepts/get-started/compute#cold-and-hot-boots?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) | / | ~250ms | Instant | -| HTTP Streaming | Yes | Yes | Yes | -| IO | All | All | `fetch` | -| Scalability | / | High | Highest | -| Security | Normal | High | High | -| Latency | Normal | Low | Lowest | -| Code Size | / | 50MB | 1MB | -| NPM Packages | All | All | A smaller subset | +|   | Node (Server) | Node (Serverless) | Edge | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ----------------- | -------------------------------------------------------- | +| Name | `nodejs` | `nodejs` | `edge` or `experimental-edge` if using Next.js Rendering | +| [Cold Boot](https://vercel.com/docs/concepts/get-started/compute#cold-and-hot-boots?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) | / | ~250ms | Instant | +| HTTP Streaming | Yes | Yes | Yes | +| IO | All | All | `fetch` | +| Scalability | / | High | Highest | +| Security | Normal | High | High | +| Latency | Normal | Low | Lowest | +| Code Size | / | 50 MB | 4 MB | +| NPM Packages | All | All | A smaller subset | -Next.js' default runtime configuration is good for most use cases, but there’re still many reasons to change to one runtime over the other one. +Next.js' default runtime configuration is good for most use cases, but there are still many reasons to change to one runtime over the other one. For example, for API routes that rely on native Node.js APIs, they need to run with the Node.js Runtime. However, if an API only uses something like cookie-based authentication, using Middleware and the Edge Runtime will be a better choice due to its lower latency as well as better scalability. @@ -63,7 +47,7 @@ For example, for API routes that rely on native Node.js APIs, they need to run w ```typescript export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default (req) => new Response('Hello world!') diff --git a/docs/api-reference/edge-runtime.md b/docs/api-reference/edge-runtime.md index 3471b5c04167283..0a2704132b84e4d 100644 --- a/docs/api-reference/edge-runtime.md +++ b/docs/api-reference/edge-runtime.md @@ -144,7 +144,7 @@ You can relax the check to allow specific files with your Middleware or Edge API ```javascript export const config = { - runtime: 'experimental-edge', // for Edge API Routes only + runtime: 'edge', // for Edge API Routes only unstable_allowDynamic: [ '/lib/utilities.js', // allows a single file '/node_modules/function-bind/**', // use a glob to allow anything in the function-bind 3rd party module diff --git a/docs/api-routes/edge-api-routes.md b/docs/api-routes/edge-api-routes.md index ae9378b6f506066..6546c26351d98cb 100644 --- a/docs/api-routes/edge-api-routes.md +++ b/docs/api-routes/edge-api-routes.md @@ -2,7 +2,7 @@ description: Edge API Routes enable you to build high performance APIs directly inside your Next.js application. --- -# Edge API Routes (Beta) +# Edge API Routes Edge API Routes enable you to build high performance APIs with Next.js. Using the [Edge Runtime](/docs/api-reference/edge-runtime.md), they are often faster than Node.js-based API Routes. This performance improvement does come with [constraints](/docs/api-reference/edge-runtime.md#unsupported-apis), like not having access to native Node.js APIs. Instead, Edge API Routes are built on standard Web APIs. @@ -14,7 +14,7 @@ Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated ```typescript export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default (req) => new Response('Hello world!') @@ -26,7 +26,7 @@ export default (req) => new Response('Hello world!') import type { NextRequest } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default async function handler(req: NextRequest) { @@ -50,7 +50,7 @@ export default async function handler(req: NextRequest) { import type { NextRequest } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default async function handler(req: NextRequest) { @@ -75,7 +75,7 @@ export default async function handler(req: NextRequest) { import type { NextRequest } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default async function handler(req: NextRequest) { @@ -91,7 +91,7 @@ export default async function handler(req: NextRequest) { import { type NextRequest } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default async function handler(req: NextRequest) { @@ -131,4 +131,6 @@ Edge API Routes use the [Edge Runtime](/docs/api-reference/edge-runtime.md), whe Edge API Routes can [stream responses](/docs/api-reference/edge-runtime.md#web-stream-apis) from the server and run _after_ cached files (e.g. HTML, CSS, JavaScript) have been accessed. Server-side streaming can help improve performance with faster [Time To First Byte (TTFB)](https://web.dev/ttfb/). +> Note: Using [Edge Runtime](/docs/api-reference/edge-runtime.md) with `getServerSideProps` does not give you access to the response object. If you need access to `res`, you should use the [Node.js runtime](/docs/advanced-features/react-18/switchable-runtime.md) by setting `runtime: 'nodejs'`. + View the [supported APIs](/docs/api-reference/edge-runtime.md) and [unsupported APIs](/docs/api-reference/edge-runtime.md#unsupported-apis) for the Edge Runtime. diff --git a/docs/basic-features/data-fetching/get-server-side-props.md b/docs/basic-features/data-fetching/get-server-side-props.md index 3d789e4939445b6..65a0e8dbf5fa4ef 100644 --- a/docs/basic-features/data-fetching/get-server-side-props.md +++ b/docs/basic-features/data-fetching/get-server-side-props.md @@ -45,6 +45,18 @@ It can be tempting to reach for an [API Route](/docs/api-routes/introduction.md) Take the following example. An API route is used to fetch some data from a CMS. That API route is then called directly from `getServerSideProps`. This produces an additional call, reducing performance. Instead, directly import the logic used inside your API Route into `getServerSideProps`. This could mean calling a CMS, database, or other API directly from inside `getServerSideProps`. +### getServerSideProps with Edge API Routes + +`getServerSideProps` can be used with both Serverless and Edge Runtimes, and you can set props in both. However, currently in Edge Runtime, you do not have access to the response object. This means that you cannot — for example — add cookies in `getServerSideProps`. To have access to the response object, you should **continue to use the Node.js runtime**, which is the default runtime. + +You can explicitly set the runtime on a [per-page basis](https://nextjs.org/docs/advanced-features/react-18/switchable-runtime#page-runtime-option) by modifying the `config`, for example: + +```js +export const config = { + runtime: 'nodejs', +} +``` + ## Fetching data on the client side If your page contains frequently updating data, and you don’t need to pre-render the data, you can fetch the data on the [client side](/docs/basic-features/data-fetching/client-side.md). An example of this is user-specific data: diff --git a/docs/basic-features/data-fetching/incremental-static-regeneration.md b/docs/basic-features/data-fetching/incremental-static-regeneration.md index a4bc788533d324e..b7fb3ebc63eca61 100644 --- a/docs/basic-features/data-fetching/incremental-static-regeneration.md +++ b/docs/basic-features/data-fetching/incremental-static-regeneration.md @@ -27,7 +27,7 @@ description: 'Learn how to create or update static pages at runtime with Increme Next.js allows you to create or update static pages _after_ you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, **without needing to rebuild the entire site**. With ISR, you can retain the benefits of static while scaling to millions of pages. -> Note: the `experimental-edge` runtime (https://nextjs.org/docs/api-reference/edge-runtime) is currently not compatible with ISR although can leverage `stale-while-revalidate` by setting the `cache-control` header manually. +> Note: The [`experimental-edge` runtime](https://nextjs.org/docs/api-reference/edge-runtime) is currently not compatible with ISR, although can leverage `stale-while-revalidate` by setting the `cache-control` header manually. To use ISR, add the `revalidate` prop to `getStaticProps`: diff --git a/docs/manifest.json b/docs/manifest.json index 15178b9b8f14687..1a956ba9bbdc200 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -151,7 +151,7 @@ "path": "/docs/api-routes/response-helpers.md" }, { - "title": "Edge API Routes (Beta)", + "title": "Edge API Routes", "path": "/docs/api-routes/edge-api-routes.md" } ] diff --git a/errors/edge-dynamic-code-evaluation.md b/errors/edge-dynamic-code-evaluation.md index 26a2de05ef172af..5003e10e4453347 100644 --- a/errors/edge-dynamic-code-evaluation.md +++ b/errors/edge-dynamic-code-evaluation.md @@ -33,7 +33,7 @@ You can relax the check to allow specific files with your Middleware or Edge API ```typescript export const config = { - runtime: 'experimental-edge', // for Edge API Routes only + runtime: 'edge', // for Edge API Routes only unstable_allowDynamic: [ '/lib/utilities.js', // allows a single file '/node_modules/function-bind/**', // use a glob to allow anything in the function-bind 3rd party module diff --git a/errors/middleware-upgrade-guide.md b/errors/middleware-upgrade-guide.md index 6d10bf8472ee325..ca1508b0bb8a4de 100644 --- a/errors/middleware-upgrade-guide.md +++ b/errors/middleware-upgrade-guide.md @@ -175,7 +175,7 @@ If you were previously using Middleware to forward headers to an external API, y import { type NextRequest } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default async function handler(req: NextRequest) { diff --git a/errors/returning-response-body-in-middleware.md b/errors/returning-response-body-in-middleware.md index 344ce1b278f95c0..dd3ea9a34c6ca4f 100644 --- a/errors/returning-response-body-in-middleware.md +++ b/errors/returning-response-body-in-middleware.md @@ -1,5 +1,7 @@ # Returning response body in Middleware +> Note: In Next.js v13.0.0 you can now respond to Middleware directly by returning a `NextResponse`. For more information, see [Producing a Response](https://nextjs.org/docs/advanced-features/middleware#producing-a-response). + #### Why This Error Occurred [Middleware](https://nextjs.org/docs/advanced-features/middleware) can no longer produce a response body as of `v12.2+`. diff --git a/examples/with-webassembly/pages/api/edge.ts b/examples/with-webassembly/pages/api/edge.ts index 0e9a30908ae409a..21867d663e03603 100644 --- a/examples/with-webassembly/pages/api/edge.ts +++ b/examples/with-webassembly/pages/api/edge.ts @@ -13,4 +13,4 @@ export default async function handler() { return new Response(`got: ${number}`) } -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } diff --git a/packages/next/build/analysis/extract-const-value.ts b/packages/next/build/analysis/extract-const-value.ts index 209f3e813ae1e2d..0ae1d5fc7993ac6 100644 --- a/packages/next/build/analysis/extract-const-value.ts +++ b/packages/next/build/analysis/extract-const-value.ts @@ -204,7 +204,7 @@ function extractValue(node: Node, path?: string[]): any { /** * Extracts the value of an exported const variable named `exportedName` - * (e.g. "export const config = { runtime: 'experimental-edge' }") from swc's AST. + * (e.g. "export const config = { runtime: 'edge' }") from swc's AST. * The value must be one of (or throws UnsupportedValueError): * - string * - boolean diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index 8e153b7fcbacf61..63fc708c2c89c70 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -1,18 +1,20 @@ -import { isServerRuntime } from '../../server/config-shared' import type { NextConfig } from '../../server/config-shared' import type { Middleware, RouteHas } from '../../lib/load-custom-routes' + +import { promises as fs } from 'fs' +import { matcher } from 'next/dist/compiled/micromatch' +import { ServerRuntime } from 'next/types' import { extractExportedConstValue, UnsupportedValueError, } from './extract-const-value' import { parseModule } from './parse-module' -import { promises as fs } from 'fs' -import { tryToParsePath } from '../../lib/try-to-parse-path' import * as Log from '../output/log' import { SERVER_RUNTIME } from '../../lib/constants' -import { ServerRuntime } from 'next/types' import { checkCustomRoutes } from '../../lib/load-custom-routes' -import { matcher } from 'next/dist/compiled/micromatch' +import { tryToParsePath } from '../../lib/try-to-parse-path' +import { isAPIRoute } from '../../lib/is-api-route' +import { isEdgeRuntime } from '../../lib/is-edge-runtime' import { RSC_MODULE_TYPES } from '../../shared/lib/constants' export interface MiddlewareConfig { @@ -229,13 +231,13 @@ function getMiddlewareConfig( return result } -let warnedAboutExperimentalEdgeApiFunctions = false -function warnAboutExperimentalEdgeApiFunctions() { - if (warnedAboutExperimentalEdgeApiFunctions) { +let warnedAboutExperimentalEdge = false +function warnAboutExperimentalEdge() { + if (warnedAboutExperimentalEdge) { return } Log.warn(`You are using an experimental edge runtime, the API might change.`) - warnedAboutExperimentalEdgeApiFunctions = true + warnedAboutExperimentalEdge = true } const warnedUnsupportedValueMap = new Map() @@ -307,7 +309,8 @@ export async function getPageStaticInfo(params: { if ( typeof resolvedRuntime !== 'undefined' && - !isServerRuntime(resolvedRuntime) + resolvedRuntime !== SERVER_RUNTIME.nodejs && + !isEdgeRuntime(resolvedRuntime) ) { const options = Object.values(SERVER_RUNTIME).join(', ') if (typeof resolvedRuntime !== 'string') { @@ -326,15 +329,28 @@ export async function getPageStaticInfo(params: { const requiresServerRuntime = ssr || ssg || pageType === 'app' - resolvedRuntime = - SERVER_RUNTIME.edge === resolvedRuntime - ? SERVER_RUNTIME.edge - : requiresServerRuntime - ? resolvedRuntime || nextConfig.experimental?.runtime - : undefined + resolvedRuntime = isEdgeRuntime(resolvedRuntime) + ? resolvedRuntime + : requiresServerRuntime + ? resolvedRuntime || nextConfig.experimental?.runtime + : undefined - if (resolvedRuntime === SERVER_RUNTIME.edge) { - warnAboutExperimentalEdgeApiFunctions() + if (resolvedRuntime === SERVER_RUNTIME.experimentalEdge) { + warnAboutExperimentalEdge() + } + + if ( + resolvedRuntime === SERVER_RUNTIME.edge && + pageType === 'pages' && + page && + !isAPIRoute(page.replace(/^\/pages\//, '/')) + ) { + const message = `Page ${page} provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.` + Log.error(message) + + if (!isDev) { + process.exit(1) + } } const middlewareConfig = getMiddlewareConfig( diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index ae00a6095ff4feb..5321c624b3f0ff5 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -12,13 +12,13 @@ import chalk from 'next/dist/compiled/chalk' import { posix, join } from 'path' import { stringify } from 'querystring' import { - API_ROUTE, PAGES_DIR_ALIAS, ROOT_DIR_ALIAS, APP_DIR_ALIAS, - SERVER_RUNTIME, WEBPACK_LAYERS, } from '../lib/constants' +import { isAPIRoute } from '../lib/is-api-route' +import { isEdgeRuntime } from '../lib/is-edge-runtime' import { APP_CLIENT_INTERNALS, RSC_MODULE_TYPES } from '../shared/lib/constants' import { CLIENT_STATIC_FILES_RUNTIME_AMP, @@ -175,7 +175,7 @@ export function getEdgeServerEntry(opts: { return `next-middleware-loader?${stringify(loaderParams)}!` } - if (opts.page.startsWith('/api/') || opts.page === '/api') { + if (isAPIRoute(opts.page)) { const loaderParams: EdgeFunctionLoaderOptions = { absolutePagePath: opts.absolutePagePath, page: opts.page, @@ -257,8 +257,8 @@ export async function runDependingOnPageType(params: { await params.onEdgeServer() return } - if (params.page.match(API_ROUTE)) { - if (params.pageRuntime === SERVER_RUNTIME.edge) { + if (isAPIRoute(params.page)) { + if (isEdgeRuntime(params.pageRuntime)) { await params.onEdgeServer() return } @@ -279,7 +279,7 @@ export async function runDependingOnPageType(params: { await Promise.all([params.onClient(), params.onServer()]) return } - if (params.pageRuntime === SERVER_RUNTIME.edge) { + if (isEdgeRuntime(params.pageRuntime)) { await Promise.all([params.onClient(), params.onEdgeServer()]) return } diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index a29110bdc0966be..b9d09a5a44f9089 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -18,7 +18,6 @@ import { PUBLIC_DIR_MIDDLEWARE_CONFLICT, MIDDLEWARE_FILENAME, PAGES_DIR_ALIAS, - SERVER_RUNTIME, } from '../lib/constants' import { fileExists } from '../lib/file-exists' import { findPagesDir } from '../lib/find-pages-dir' @@ -109,6 +108,7 @@ import { writeBuildId } from './write-build-id' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' import { NextConfigComplete } from '../server/config-shared' import isError, { NextError } from '../lib/is-error' +import { isEdgeRuntime } from '../lib/is-edge-runtime' import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin' import { MiddlewareManifest } from './webpack/plugins/middleware-plugin' import { recursiveCopy } from '../lib/recursive-copy' @@ -1428,7 +1428,7 @@ export default async function build( try { let edgeInfo: any - if (pageRuntime === SERVER_RUNTIME.edge) { + if (isEdgeRuntime(pageRuntime)) { if (pageType === 'app') { edgeRuntimeAppCount++ } else { @@ -1467,7 +1467,7 @@ export default async function build( if (pageType === 'app' && originalAppPath) { appNormalizedPaths.set(originalAppPath, page) // TODO-APP: handle prerendering with edge - if (pageRuntime === 'experimental-edge') { + if (isEdgeRuntime(pageRuntime)) { isStatic = false isSsg = false } else { @@ -1505,7 +1505,7 @@ export default async function build( ) } } else { - if (pageRuntime === SERVER_RUNTIME.edge) { + if (isEdgeRuntime(pageRuntime)) { if (workerResult.hasStaticProps) { console.warn( `"getStaticProps" is not yet supported fully with "experimental-edge", detected on ${page}` diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 614270f40c31195..11aed2d96bd32dc 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -20,7 +20,6 @@ import { SERVER_PROPS_GET_INIT_PROPS_CONFLICT, SERVER_PROPS_SSG_CONFLICT, MIDDLEWARE_FILENAME, - SERVER_RUNTIME, } from '../lib/constants' import { MODERN_BROWSERSLIST_TARGET } from '../shared/lib/constants' import prettyBytes from '../lib/pretty-bytes' @@ -33,6 +32,7 @@ import { GetStaticPaths, PageConfig, ServerRuntime } from 'next/types' import { BuildManifest } from '../server/get-page-files' import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash' import { UnwrapPromise } from '../lib/coalesced-function' +import { isEdgeRuntime } from '../lib/is-edge-runtime' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' import * as Log from './output/log' import { @@ -423,7 +423,7 @@ export async function printTreeView( ? '○' : pageInfo?.isSsg ? '●' - : pageInfo?.runtime === SERVER_RUNTIME.edge + : isEdgeRuntime(pageInfo?.runtime) ? 'ℇ' : 'λ' @@ -1267,7 +1267,7 @@ export async function isPageStatic({ let prerenderFallback: boolean | 'blocking' | undefined let appConfig: AppConfig = {} - if (pageRuntime === SERVER_RUNTIME.edge) { + if (isEdgeRuntime(pageRuntime)) { const runtime = await getRuntimeContext({ paths: edgeInfo.files.map((file: string) => path.join(distDir, file)), env: edgeInfo.env, diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 21209d17a4c18c4..18bc7e98ebf55d8 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -10,13 +10,13 @@ import { PAGES_DIR_ALIAS, ROOT_DIR_ALIAS, APP_DIR_ALIAS, - SERVER_RUNTIME, WEBPACK_LAYERS, RSC_MOD_REF_PROXY_ALIAS, } from '../lib/constants' import { EXTERNAL_PACKAGES } from '../lib/server-external-packages' import { fileExists } from '../lib/file-exists' import { CustomRoutes } from '../lib/load-custom-routes.js' +import { isEdgeRuntime } from '../lib/is-edge-runtime' import { CLIENT_STATIC_FILES_RUNTIME_AMP, CLIENT_STATIC_FILES_RUNTIME_MAIN, @@ -628,7 +628,7 @@ export default async function getBaseWebpackConfig( const disableOptimizedLoading = true if (isClient) { - if (config.experimental.runtime === SERVER_RUNTIME.edge) { + if (isEdgeRuntime(config.experimental.runtime)) { Log.warn( 'You are using the experimental Edge Runtime with `experimental.runtime`.' ) diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts index 6eb8ffa3f09b6ae..ff616c2f06af5bb 100644 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts +++ b/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts @@ -72,7 +72,7 @@ export function getRender({ pagesType, extendRenderOpts: { buildId, - runtime: SERVER_RUNTIME.edge, + runtime: SERVER_RUNTIME.experimentalEdge, supportsDynamicHTML: true, disableOptimizedLoading: true, serverComponentManifest, diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts index f11541a03a2666b..fa5026aa622a8d6 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts @@ -2,7 +2,7 @@ import devalue from 'next/dist/compiled/devalue' import { join } from 'path' import { parse } from 'querystring' import { webpack } from 'next/dist/compiled/webpack/webpack' -import { API_ROUTE } from '../../../../lib/constants' +import { isAPIRoute } from '../../../../lib/is-api-route' import { isDynamicRoute } from '../../../../shared/lib/router/utils' import { escapeStringRegexp } from '../../../../shared/lib/escape-regexp' import { __ApiPreviewProps } from '../../../../server/api-utils' @@ -88,7 +88,7 @@ const nextServerlessLoader: webpack.LoaderDefinitionFunction = function () { ` : 'const runtimeConfig = {}' - if (page.match(API_ROUTE)) { + if (isAPIRoute(page)) { return ` ${envLoading} ${runtimeConfigImports} diff --git a/packages/next/build/webpack/plugins/flight-types-plugin.ts b/packages/next/build/webpack/plugins/flight-types-plugin.ts index 0213ea62b1fd350..aff833983aa69a0 100644 --- a/packages/next/build/webpack/plugins/flight-types-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-types-plugin.ts @@ -58,7 +58,11 @@ interface IEntry { dynamicParams?: boolean fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache' preferredRegion?: 'auto' | 'home' | 'edge' - ${options.type === 'page' ? "runtime?: 'nodejs' | 'experimental-edge'" : ''} + ${ + options.type === 'page' + ? "runtime?: 'nodejs' | 'experimental-edge' | 'edge'" + : '' + } } // ============= diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index fe1fcb1809544d5..a785731a7c48e69 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -13,7 +13,7 @@ import { promisify } from 'util' import { AmpPageStatus, formatAmpMessages } from '../build/output/index' import * as Log from '../build/output/log' import createSpinner from '../build/spinner' -import { API_ROUTE, SSG_FALLBACK_EXPORT_ERROR } from '../lib/constants' +import { SSG_FALLBACK_EXPORT_ERROR } from '../lib/constants' import { recursiveCopy } from '../lib/recursive-copy' import { recursiveDelete } from '../lib/recursive-delete' import { @@ -40,6 +40,7 @@ import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-pa import { loadEnvConfig } from '@next/env' import { PrerenderManifest } from '../build' import { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin' +import { isAPIRoute } from '../lib/is-api-route' import { getPagePath } from '../server/require' import { Span } from '../trace' import { FontConfig } from '../server/font-utils' @@ -243,7 +244,7 @@ export default async function exportApp( // _error is exported as 404.html later on // API Routes are Node.js functions - if (page.match(API_ROUTE)) { + if (isAPIRoute(page)) { hasApiRoutes = true continue } @@ -475,7 +476,7 @@ export default async function exportApp( const filteredPaths = exportPaths.filter( // Remove API routes - (route) => !exportPathMap[route].page.match(API_ROUTE) + (route) => !isAPIRoute(exportPathMap[route].page) ) if (filteredPaths.length !== exportPaths.length) { diff --git a/packages/next/lib/constants.ts b/packages/next/lib/constants.ts index ef1d10b1033945b..7e0127bc1a7f6fa 100644 --- a/packages/next/lib/constants.ts +++ b/packages/next/lib/constants.ts @@ -1,8 +1,5 @@ import type { ServerRuntime } from '../types' -// Regex for API routes -export const API_ROUTE = /^\/api(?:\/|$)/ - // Patterns to detect middleware files export const MIDDLEWARE_FILENAME = 'middleware' export const MIDDLEWARE_LOCATION_REGEXP = `(?:src/)?${MIDDLEWARE_FILENAME}` @@ -65,7 +62,8 @@ export const ESLINT_PROMPT_VALUES = [ ] export const SERVER_RUNTIME: Record = { - edge: 'experimental-edge', + edge: 'edge', + experimentalEdge: 'experimental-edge', nodejs: 'nodejs', } diff --git a/packages/next/lib/is-api-route.ts b/packages/next/lib/is-api-route.ts new file mode 100644 index 000000000000000..0e079c8018cc9da --- /dev/null +++ b/packages/next/lib/is-api-route.ts @@ -0,0 +1,3 @@ +export function isAPIRoute(value?: string) { + return value === '/api' || Boolean(value?.startsWith('/api/')) +} diff --git a/packages/next/lib/is-edge-runtime.ts b/packages/next/lib/is-edge-runtime.ts new file mode 100644 index 000000000000000..7d297fb32fb7844 --- /dev/null +++ b/packages/next/lib/is-edge-runtime.ts @@ -0,0 +1,8 @@ +import { ServerRuntime } from '../types' +import { SERVER_RUNTIME } from './constants' + +export function isEdgeRuntime(value?: string): value is ServerRuntime { + return ( + value === SERVER_RUNTIME.experimentalEdge || value === SERVER_RUNTIME.edge + ) +} diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 3cf525acb8e5541..c31269c652af67f 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -33,6 +33,7 @@ import type { FontLoaderManifest } from '../build/webpack/plugins/font-loader-ma import { parse as parseQs } from 'querystring' import { format as formatUrl, parse as parseUrl } from 'url' import { getRedirectStatus } from '../lib/redirect-status' +import { isEdgeRuntime } from '../lib/is-edge-runtime' import { NEXT_BUILTIN_DOCUMENT, STATIC_STATUS_PAGES, @@ -1080,7 +1081,7 @@ export default abstract class Server { } // strip header so we generate HTML still if ( - opts.runtime !== 'experimental-edge' || + !isEdgeRuntime(opts.runtime) || (this.serverOptions as any).webServerConfig ) { for (const param of FLIGHT_PARAMETERS) { @@ -1293,7 +1294,7 @@ export default abstract class Server { }) if ( this.nextConfig.experimental.fetchCache && - (opts.runtime !== 'experimental-edge' || + (!isEdgeRuntime(opts.runtime) || (this.serverOptions as any).webServerConfig) ) { delete req.headers[FETCH_CACHE_HEADER] diff --git a/packages/next/server/config-schema.ts b/packages/next/server/config-schema.ts index 14624f761d3e578..2090cd6e46a0c9d 100644 --- a/packages/next/server/config-schema.ts +++ b/packages/next/server/config-schema.ts @@ -1,6 +1,7 @@ import { NextConfig } from './config' import type { JSONSchemaType } from 'ajv' import { VALID_LOADERS } from '../shared/lib/image-config' +import { SERVER_RUNTIME } from '../lib/constants' const configSchema = { type: 'object', @@ -353,7 +354,7 @@ const configSchema = { }, runtime: { // automatic typing doesn't like enum - enum: ['experimental-edge', 'nodejs'] as any, + enum: Object.values(SERVER_RUNTIME) as any, type: 'string', }, serverComponentsExternalPackages: { diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index 0a56331d28ceb4f..858eed14c5261c7 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -623,12 +623,6 @@ export async function normalizeConfig(phase: string, config: any) { return await config } -export function isServerRuntime(value?: string): value is ServerRuntime { - return ( - value === undefined || value === 'nodejs' || value === 'experimental-edge' - ) -} - export function validateConfig(userConfig: NextConfig): { errors?: Array | null } { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 058f23239ac2ebe..4052b3d5d9db007 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -31,6 +31,7 @@ import fs from 'fs' import { join, relative, resolve, sep } from 'path' import { IncomingMessage, ServerResponse } from 'http' import { addRequestMeta, getRequestMeta } from './request-meta' +import { isAPIRoute } from '../lib/is-api-route' import { isDynamicRoute } from '../shared/lib/router/utils' import { PAGES_MANIFEST, @@ -1199,7 +1200,7 @@ export default class NextNodeServer extends BaseServer { } const bubbleNoFallback = !!query._nextBubbleNoFallback - if (pathname === '/api' || pathname.startsWith('/api/')) { + if (isAPIRoute(pathname)) { delete query._nextBubbleNoFallback const handled = await this.handleApiRequest(req, res, pathname, query) @@ -1268,7 +1269,7 @@ export default class NextNodeServer extends BaseServer { if (!pageFound && this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes) { params = dynamicRoute.match(pathname) || undefined - if (dynamicRoute.page.startsWith('/api') && params) { + if (isAPIRoute(dynamicRoute.page) && params) { page = dynamicRoute.page pageFound = true break diff --git a/packages/next/server/next-typescript.ts b/packages/next/server/next-typescript.ts index 665b7069b4ba47d..2835fd0b3c912b4 100644 --- a/packages/next/server/next-typescript.ts +++ b/packages/next/server/next-typescript.ts @@ -132,6 +132,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.', '"experimental-edge"': 'Prefer the experimental Edge runtime.', }, link: 'https://beta.nextjs.org/docs/api-reference/segment-config#runtime', diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 8a5bd6c4fe2b605..ad72b4ada272e13 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -12,6 +12,7 @@ import { getNextInternalQuery, NextUrlWithParsedQuery, } from './request-meta' +import { isAPIRoute } from '../lib/is-api-route' import { getPathMatch } from '../shared/lib/router/utils/path-match' import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' @@ -374,7 +375,7 @@ export default class Router { if ( pathnameInfo.locale && !route.matchesLocaleAPIRoutes && - pathnameInfo.pathname.match(/^\/api(?:\/|$)/) + isAPIRoute(pathnameInfo.pathname) ) { continue } diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index e236f6b881f75bd..727f01763e1c77f 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -5,17 +5,17 @@ import type { NextParsedUrlQuery, NextUrlWithParsedQuery } from './request-meta' import type { Params } from '../shared/lib/router/utils/route-matcher' import type { PayloadOptions } from './send-payload' import type { LoadComponentsReturnType } from './load-components' -import { NoFallbackError, Options } from './base-server' import type { DynamicRoutes, PageChecker, Route } from './router' import type { NextConfig } from './config-shared' import type { BaseNextRequest, BaseNextResponse } from './base-http' import type { UrlWithParsedQuery } from 'url' -import BaseServer from './base-server' import { byteLength } from './api-utils/web' +import BaseServer, { NoFallbackError, Options } from './base-server' import { generateETag } from './lib/etag' import { addRequestMeta } from './request-meta' import WebResponseCache from './response-cache/web' +import { isAPIRoute } from '../lib/is-api-route' import { getPathMatch } from '../shared/lib/router/utils/path-match' import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-asset-path' import { detectDomainLocale } from '../shared/lib/i18n/detect-domain-locale' @@ -307,7 +307,7 @@ export default class NextWebServer extends BaseServer { } const bubbleNoFallback = !!query._nextBubbleNoFallback - if (pathname === '/api' || pathname.startsWith('/api/')) { + if (isAPIRoute(pathname)) { delete query._nextBubbleNoFallback } diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index cc9e43dd9301b51..7fc6446ed4fc43f 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -44,6 +44,7 @@ import { removeLocale } from '../../../client/remove-locale' import { removeBasePath } from '../../../client/remove-base-path' import { addBasePath } from '../../../client/add-base-path' import { hasBasePath } from '../../../client/has-base-path' +import { isAPIRoute } from '../../../lib/is-api-route' import { getNextPathnameInfo } from './utils/get-next-pathname-info' import { formatNextPathnameInfo } from './utils/format-next-pathname-info' import { compareRouterStates } from './utils/compare-states' @@ -2135,7 +2136,7 @@ export default class Router implements BaseRouter { } } - if (route === '/api' || route.startsWith('/api/')) { + if (isAPIRoute(route)) { handleHardNavigation({ url: as, router: this }) return new Promise(() => {}) } diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index f1186464bb04d9d..6d1f9d4411a45bc 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -23,7 +23,7 @@ import { // @ts-ignore This path is generated at build time and conflicts otherwise import next from '../dist/server/next' -export type ServerRuntime = 'nodejs' | 'experimental-edge' | undefined +export type ServerRuntime = 'nodejs' | 'experimental-edge' | 'edge' | undefined // @ts-ignore This path is generated at build time and conflicts otherwise export { NextConfig } from '../dist/server/config' diff --git a/test/e2e/app-dir/app-middleware/pages/api/dump-headers-edge.js b/test/e2e/app-dir/app-middleware/pages/api/dump-headers-edge.js index 0ece8ea2c75187b..05396dccbb6a964 100644 --- a/test/e2e/app-dir/app-middleware/pages/api/dump-headers-edge.js +++ b/test/e2e/app-dir/app-middleware/pages/api/dump-headers-edge.js @@ -1,5 +1,5 @@ export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default (req) => { diff --git a/test/e2e/app-dir/app/pages/api/hello.js b/test/e2e/app-dir/app/pages/api/hello.js index 47779ec1fa09fca..81adbb05f102452 100644 --- a/test/e2e/app-dir/app/pages/api/hello.js +++ b/test/e2e/app-dir/app/pages/api/hello.js @@ -3,5 +3,5 @@ export default function api(req) { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/edge.js b/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/edge.js index 26bcb1402fe01d8..306d20a7e28250f 100644 --- a/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/edge.js +++ b/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/edge.js @@ -7,5 +7,5 @@ export default async (req) => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/index.js b/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/index.js index 26bcb1402fe01d8..306d20a7e28250f 100644 --- a/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/index.js +++ b/test/e2e/edge-api-endpoints-can-receive-body/app/pages/api/index.js @@ -7,5 +7,5 @@ export default async (req) => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/e2e/edge-async-local-storage/index.test.ts b/test/e2e/edge-async-local-storage/index.test.ts index 39c2798f182246e..cb38bfa3c72e150 100644 --- a/test/e2e/edge-async-local-storage/index.test.ts +++ b/test/e2e/edge-async-local-storage/index.test.ts @@ -10,7 +10,7 @@ describe('edge api can use async local storage', () => { { title: 'a single instance', code: ` - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } const storage = new AsyncLocalStorage() export default async function handler(request) { @@ -36,7 +36,7 @@ describe('edge api can use async local storage', () => { { title: 'multiple instances', code: ` - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } const topStorage = new AsyncLocalStorage() export default async function handler(request) { diff --git a/test/e2e/edge-can-use-wasm-files/index.test.ts b/test/e2e/edge-can-use-wasm-files/index.test.ts index 48050bc0c584b54..42f8232552c2323 100644 --- a/test/e2e/edge-can-use-wasm-files/index.test.ts +++ b/test/e2e/edge-can-use-wasm-files/index.test.ts @@ -49,7 +49,7 @@ describe('edge api endpoints can use wasm files', () => { const value = await increment(input); return new Response(null, { headers: { data: JSON.stringify({ input, value }) } }); } - export const config = { runtime: 'experimental-edge' }; + export const config = { runtime: 'edge' }; `, 'src/add.wasm': new FileRef(path.join(__dirname, './add.wasm')), 'src/add.js': ` diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js index 49da52df583bda7..7d3dc54e193a0de 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js @@ -1,4 +1,4 @@ -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } /** * @param {import('next/server').NextRequest} req diff --git a/test/e2e/edge-configurable-runtime/app/pages/api/edge.js b/test/e2e/edge-configurable-runtime/app/pages/api/edge.js new file mode 100644 index 000000000000000..beac5edf5a72d37 --- /dev/null +++ b/test/e2e/edge-configurable-runtime/app/pages/api/edge.js @@ -0,0 +1,2 @@ +export default () => new Response('ok') +export const config = { runtime: 'edge' } diff --git a/test/e2e/edge-configurable-runtime/app/pages/index.jsx b/test/e2e/edge-configurable-runtime/app/pages/index.jsx new file mode 100644 index 000000000000000..dc344c3effb9f29 --- /dev/null +++ b/test/e2e/edge-configurable-runtime/app/pages/index.jsx @@ -0,0 +1 @@ +export default () =>

hello world

diff --git a/test/e2e/edge-configurable-runtime/index.test.ts b/test/e2e/edge-configurable-runtime/index.test.ts new file mode 100644 index 000000000000000..d96cddd836b60e0 --- /dev/null +++ b/test/e2e/edge-configurable-runtime/index.test.ts @@ -0,0 +1,132 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { fetchViaHTTP, File, nextBuild } from 'next-test-utils' +import { join } from 'path' + +const appDir = join(__dirname, './app') +const pagePath = 'pages/index.jsx' +const apiPath = 'pages/api/edge.js' +const page = new File(join(appDir, pagePath)) +const api = new File(join(appDir, apiPath)) + +describe('Configurable runtime for pages and API routes', () => { + let next: NextInstance + + if ((global as any).isNextDev) { + describe('In dev mode', () => { + beforeAll(async () => { + next = await createNext({ + files: new FileRef(appDir), + dependencies: {}, + skipStart: true, + }) + }) + + afterEach(async () => { + await next.stop() + await next.patchFile(pagePath, page.originalContent) + await next.patchFile(apiPath, api.originalContent) + }) + + afterAll(() => next.destroy()) + + it('runs with no warning API route on the edge runtime', async () => { + await next.start() + const res = await fetchViaHTTP(next.url, `/api/edge`) + expect(res.status).toEqual(200) + expect(next.cliOutput).not.toInclude('error') + expect(next.cliOutput).not.toInclude('warn') + }) + + it('warns about API route using experimental-edge runtime', async () => { + await next.patchFile( + apiPath, + ` + export default () => new Response('ok'); + export const config = { runtime: 'experimental-edge' }; + ` + ) + await next.start() + const res = await fetchViaHTTP(next.url, `/api/edge`) + expect(res.status).toEqual(200) + expect(next.cliOutput).not.toInclude('error') + expect(next.cliOutput).toInclude( + 'warn - You are using an experimental edge runtime, the API might change.' + ) + }) + it('warns about page using edge runtime', async () => { + await next.patchFile( + pagePath, + ` + export default () => (

hello world

); + export const runtime = 'experimental-edge'; + ` + ) + await next.start() + const res = await fetchViaHTTP(next.url, `/`) + expect(res.status).toEqual(200) + expect(next.cliOutput).not.toInclude('error') + expect(next.cliOutput).toInclude( + 'warn - You are using an experimental edge runtime, the API might change.' + ) + }) + + it('errors about page using edge runtime', async () => { + await next.patchFile( + pagePath, + ` + export default () => (

hello world

); + export const runtime = 'edge'; + ` + ) + await next.start() + const res = await fetchViaHTTP(next.url, `/`) + expect(res.status).toEqual(200) + expect(next.cliOutput).toInclude( + `error - Page /pages provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.` + ) + expect(next.cliOutput).not.toInclude('warn') + }) + }) + } else if ((global as any).isNextStart) { + describe('In start mode', () => { + // TODO because createNext runs process.exit() without any log info on build failure, rely on good old nextBuild() + afterEach(async () => { + page.restore() + api.restore() + }) + + it('builds with API route on the edge runtime and page on the experimental edge runtime', async () => { + page.write(` + export default () => (

hello world

); + export const runtime = 'experimental-edge'; + `) + const output = await nextBuild(appDir, undefined, { + stdout: true, + stderr: true, + }) + expect(output.code).toBe(0) + expect(output.stderr).not.toContain(`error`) + expect(output.stdout).not.toContain(`warn`) + }) + + it('does not build with page on the edge runtime', async () => { + page.write(` + export default () => (

hello world

); + export const runtime = 'edge'; + `) + const output = await nextBuild(appDir, undefined, { + stdout: true, + stderr: true, + }) + expect(output.code).toBe(1) + expect(output.stderr).not.toContain(`Build failed`) + expect(output.stderr).toContain( + `error - Page / provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.` + ) + }) + }) + } else { + it.skip('no deploy tests', () => {}) + } +}) diff --git a/test/e2e/middleware-general/app/pages/api/edge-search-params.js b/test/e2e/middleware-general/app/pages/api/edge-search-params.js index 01a968ee9d80626..9b8cf7cd547ef24 100644 --- a/test/e2e/middleware-general/app/pages/api/edge-search-params.js +++ b/test/e2e/middleware-general/app/pages/api/edge-search-params.js @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server' -export const config = { runtime: 'experimental-edge', regions: 'default' } +export const config = { runtime: 'edge', regions: 'default' } /** * @param {import('next/server').NextRequest} diff --git a/test/e2e/middleware-request-header-overrides/app/pages/api/dump-headers-edge.js b/test/e2e/middleware-request-header-overrides/app/pages/api/dump-headers-edge.js index 0ece8ea2c75187b..05396dccbb6a964 100644 --- a/test/e2e/middleware-request-header-overrides/app/pages/api/dump-headers-edge.js +++ b/test/e2e/middleware-request-header-overrides/app/pages/api/dump-headers-edge.js @@ -1,5 +1,5 @@ export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default (req) => { diff --git a/test/e2e/og-api/app/pages/api/og.js b/test/e2e/og-api/app/pages/api/og.js index 1afb24a11292095..7d744957fc307d7 100644 --- a/test/e2e/og-api/app/pages/api/og.js +++ b/test/e2e/og-api/app/pages/api/og.js @@ -2,7 +2,7 @@ import { ImageResponse } from '@vercel/og' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default function () { diff --git a/test/e2e/skip-trailing-slash-redirect/app/pages/api/test-cookie-edge.js b/test/e2e/skip-trailing-slash-redirect/app/pages/api/test-cookie-edge.js index 6018a223708fd1d..212da9b3231a1e1 100644 --- a/test/e2e/skip-trailing-slash-redirect/app/pages/api/test-cookie-edge.js +++ b/test/e2e/skip-trailing-slash-redirect/app/pages/api/test-cookie-edge.js @@ -1,7 +1,7 @@ import { NextResponse } from 'next/server' export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default function handler(req) { diff --git a/test/e2e/streaming-ssr/streaming-ssr/pages/api/user/[id].js b/test/e2e/streaming-ssr/streaming-ssr/pages/api/user/[id].js index c5649d2074e4999..67d2491a96f9fc8 100644 --- a/test/e2e/streaming-ssr/streaming-ssr/pages/api/user/[id].js +++ b/test/e2e/streaming-ssr/streaming-ssr/pages/api/user/[id].js @@ -3,5 +3,5 @@ export default async function handler() { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/e2e/switchable-runtime/index.test.ts b/test/e2e/switchable-runtime/index.test.ts index 6c5c1e44bf2c5c8..480f735dd67e7b9 100644 --- a/test/e2e/switchable-runtime/index.test.ts +++ b/test/e2e/switchable-runtime/index.test.ts @@ -45,15 +45,10 @@ describe('Switchable runtime', () => { beforeAll(async () => { next = await createNext({ - files: { - app: new FileRef(join(__dirname, './app')), - pages: new FileRef(join(__dirname, './pages')), - utils: new FileRef(join(__dirname, './utils')), - 'next.config.js': new FileRef(join(__dirname, './next.config.js')), - }, + files: new FileRef(__dirname), dependencies: { - react: 'experimental', - 'react-dom': 'experimental', + react: 'latest', + 'react-dom': 'latest', }, }) context = { @@ -229,7 +224,7 @@ describe('Switchable runtime', () => { 'pages/api/switch-in-dev.js', ` export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default () => new Response('edge response') @@ -259,7 +254,7 @@ describe('Switchable runtime', () => { 'pages/api/switch-in-dev.js', ` export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default () => new Response('edge response again') @@ -340,7 +335,7 @@ describe('Switchable runtime', () => { 'pages/api/switch-in-dev-same-content.js', ` export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default () => new Response('edge response') @@ -373,7 +368,7 @@ describe('Switchable runtime', () => { 'pages/api/syntax-error-in-dev.js', ` export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } export default => new Response('edge response') @@ -391,7 +386,7 @@ describe('Switchable runtime', () => { export default () => new Response('edge response again') export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } ` diff --git a/test/e2e/switchable-runtime/pages/api/edge.js b/test/e2e/switchable-runtime/pages/api/edge.js index d375a24aaab7480..806920e75dfeb16 100644 --- a/test/e2e/switchable-runtime/pages/api/edge.js +++ b/test/e2e/switchable-runtime/pages/api/edge.js @@ -3,5 +3,5 @@ export default (req) => { } export const config = { - runtime: `experimental-edge`, + runtime: `edge`, } diff --git a/test/e2e/switchable-runtime/pages/api/hello.js b/test/e2e/switchable-runtime/pages/api/hello.js index 7f27052a3fbc202..c8e368a7530a0fd 100644 --- a/test/e2e/switchable-runtime/pages/api/hello.js +++ b/test/e2e/switchable-runtime/pages/api/hello.js @@ -3,5 +3,5 @@ export default (req) => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js b/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js index 3b7243a1205ef17..9e13e2a85e39516 100644 --- a/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js +++ b/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js @@ -1,5 +1,5 @@ export default () => new Response('edge response') export const config = { - runtime: `experimental-edge`, + runtime: `edge`, } diff --git a/test/integration/edge-runtime-configurable-guards/pages/api/route.js b/test/integration/edge-runtime-configurable-guards/pages/api/route.js index 9d808b1a2bb6800..8bb7f51f3b5009a 100644 --- a/test/integration/edge-runtime-configurable-guards/pages/api/route.js +++ b/test/integration/edge-runtime-configurable-guards/pages/api/route.js @@ -4,5 +4,5 @@ export default () => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/integration/edge-runtime-configurable-guards/test/index.test.js b/test/integration/edge-runtime-configurable-guards/test/index.test.js index c55c813b4a8f0f7..ae0144346d42efe 100644 --- a/test/integration/edge-runtime-configurable-guards/test/index.test.js +++ b/test/integration/edge-runtime-configurable-guards/test/index.test.js @@ -72,7 +72,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '/lib/**' } `) @@ -125,7 +125,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '**' } `) @@ -159,7 +159,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '/lib/**' } `) @@ -221,7 +221,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '**' } `) @@ -257,7 +257,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '/lib/**' } `) @@ -328,7 +328,7 @@ describe('Edge runtime configurable guards', () => { return Response.json({ result: true }) } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', unstable_allowDynamic: '/pages/**' } `) @@ -397,7 +397,7 @@ describe('Edge runtime configurable guards', () => { export default async function handler(request) { return Response.json({ result: (() => {}) instanceof Function }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, diff --git a/test/integration/edge-runtime-dynamic-code/pages/api/route.js b/test/integration/edge-runtime-dynamic-code/pages/api/route.js index 1d73702aec6030f..f0d136912e57988 100644 --- a/test/integration/edge-runtime-dynamic-code/pages/api/route.js +++ b/test/integration/edge-runtime-dynamic-code/pages/api/route.js @@ -23,4 +23,4 @@ export default async function handler(request) { ) } -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } diff --git a/test/integration/edge-runtime-module-errors/pages/api/route.js b/test/integration/edge-runtime-module-errors/pages/api/route.js index d93a0ca4a9badfa..62ba98a1ceb49eb 100644 --- a/test/integration/edge-runtime-module-errors/pages/api/route.js +++ b/test/integration/edge-runtime-module-errors/pages/api/route.js @@ -2,4 +2,4 @@ export default async function handler(request) { return Response.json({ ok: true }) } -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } diff --git a/test/integration/edge-runtime-module-errors/test/index.test.js b/test/integration/edge-runtime-module-errors/test/index.test.js index 2906fe6ce0a3de7..242e9dc934fa7ae 100644 --- a/test/integration/edge-runtime-module-errors/test/index.test.js +++ b/test/integration/edge-runtime-module-errors/test/index.test.js @@ -68,7 +68,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: basename() }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -129,7 +129,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: writeFile() }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -188,7 +188,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: await throwAsync() }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -267,7 +267,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: true }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -325,7 +325,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: true }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -384,7 +384,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: true }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -445,7 +445,7 @@ describe('Edge runtime code with imports', () => { return Response.json({ ok: true }) } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -503,7 +503,7 @@ describe('Edge runtime code with imports', () => { return response } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, @@ -564,7 +564,7 @@ describe('Edge runtime code with imports', () => { return response } - export const config = { runtime: 'experimental-edge' } + export const config = { runtime: 'edge' } `) }, }, diff --git a/test/integration/edge-runtime-response-error/pages/api/route.js b/test/integration/edge-runtime-response-error/pages/api/route.js index a1ff06c9a8fc603..6ecf1ee385d91fc 100644 --- a/test/integration/edge-runtime-response-error/pages/api/route.js +++ b/test/integration/edge-runtime-response-error/pages/api/route.js @@ -2,4 +2,4 @@ export default async function handler(request) { return 'Boom' } -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } diff --git a/test/integration/edge-runtime-streaming-error/pages/api/test.js b/test/integration/edge-runtime-streaming-error/pages/api/test.js index b5d485fcd223ad1..027f27fe080c93e 100644 --- a/test/integration/edge-runtime-streaming-error/pages/api/test.js +++ b/test/integration/edge-runtime-streaming-error/pages/api/test.js @@ -1,6 +1,7 @@ export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } + export default function () { return new Response( new ReadableStream({ diff --git a/test/integration/edge-runtime-with-node.js-apis/pages/api/route.js b/test/integration/edge-runtime-with-node.js-apis/pages/api/route.js index 8c38026256157c7..910521218d8f69e 100644 --- a/test/integration/edge-runtime-with-node.js-apis/pages/api/route.js +++ b/test/integration/edge-runtime-with-node.js-apis/pages/api/route.js @@ -5,4 +5,4 @@ export default async function handler(request) { return Response.json({ ok: true }) } -export const config = { runtime: 'experimental-edge' } +export const config = { runtime: 'edge' } diff --git a/test/integration/image-generation/app/pages/api/image.jsx b/test/integration/image-generation/app/pages/api/image.jsx index 518c3d654e00dc4..a15b0cc50f16da5 100644 --- a/test/integration/image-generation/app/pages/api/image.jsx +++ b/test/integration/image-generation/app/pages/api/image.jsx @@ -5,5 +5,5 @@ export default async () => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/integration/telemetry/pages/api/og.jsx b/test/integration/telemetry/pages/api/og.jsx index 518c3d654e00dc4..a15b0cc50f16da5 100644 --- a/test/integration/telemetry/pages/api/og.jsx +++ b/test/integration/telemetry/pages/api/og.jsx @@ -5,5 +5,5 @@ export default async () => { } export const config = { - runtime: 'experimental-edge', + runtime: 'edge', } diff --git a/test/production/generate-middleware-source-maps/index.test.ts b/test/production/generate-middleware-source-maps/index.test.ts index 9e8ac97a80a6921..b869e80e17bd3e2 100644 --- a/test/production/generate-middleware-source-maps/index.test.ts +++ b/test/production/generate-middleware-source-maps/index.test.ts @@ -13,7 +13,7 @@ describe('Middleware source maps', () => { export default function () { return
Hello, world!
} `, 'pages/api/edge.js': ` - export const config = { runtime: 'experimental-edge' }; + export const config = { runtime: 'edge' }; export default function (req) { return new Response("Hello from " + req.url); }