diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index bf743e8d03db2..01a3f9e5c2268 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -164,15 +164,6 @@ export function getEdgeServerEntry(opts: { return `next-middleware-loader?${stringify(loaderParams)}!` } - if (opts.page.startsWith('/api/')) { - const loaderParams: MiddlewareLoaderOptions = { - absolutePagePath: opts.absolutePagePath, - page: opts.page, - } - - return `next-edge-function-loader?${stringify(loaderParams)}!` - } - const loaderParams: MiddlewareSSRLoaderQuery = { absolute500Path: opts.pages['/500'] || '', absoluteAppPath: opts.pages['/_app'], @@ -418,9 +409,7 @@ export function runDependingOnPageType(params: { if (params.page === MIDDLEWARE_FILE) { return [params.onEdgeServer()] } else if (params.page.match(API_ROUTE)) { - return params.pageRuntime === 'edge' - ? [params.onEdgeServer()] - : [params.onServer()] + return [params.onServer()] } else if (params.page === '/_document') { return [params.onServer()] } else if ( diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 2ef17d8528015..2a2179e519b09 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -94,6 +94,7 @@ import { getUnresolvedModuleFromError, copyTracedFiles, isReservedPage, + isCustomErrorPage, isServerComponentPage, } from './utils' import getBaseWebpackConfig from './webpack-config' @@ -1254,7 +1255,10 @@ export default async function build( isHybridAmp, ssgPageRoutes, initialRevalidateSeconds: false, - runtime: pageRuntime, + runtime: + !isReservedPage(page) && !isCustomErrorPage(page) + ? pageRuntime + : undefined, pageDuration: undefined, ssgPageDurations: undefined, }) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index bbae4c5040099..be2aa7ee50e8f 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1232,7 +1232,6 @@ export default async function getBaseWebpackConfig( 'next-flight-client-entry-loader', 'noop-loader', 'next-middleware-loader', - 'next-edge-function-loader', 'next-middleware-ssr-loader', 'next-middleware-wasm-loader', 'next-app-loader', diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 4f8a1b45ca5a9..36e4380778c87 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -7,7 +7,6 @@ import { webpack5 } from 'next/dist/compiled/webpack/webpack' export function getModuleBuildInfo(webpackModule: webpack5.Module) { return webpackModule.buildInfo as { nextEdgeMiddleware?: EdgeMiddlewareMeta - nextEdgeApiFunction?: EdgeMiddlewareMeta nextEdgeSSR?: EdgeSSRMeta nextUsedEnvVars?: Set nextWasmMiddlewareBinding?: WasmBinding diff --git a/packages/next/build/webpack/loaders/next-edge-function-loader.ts b/packages/next/build/webpack/loaders/next-edge-function-loader.ts deleted file mode 100644 index e66d06f270da2..0000000000000 --- a/packages/next/build/webpack/loaders/next-edge-function-loader.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { getModuleBuildInfo } from './get-module-build-info' -import { stringifyRequest } from '../stringify-request' - -export type EdgeFunctionLoaderOptions = { - absolutePagePath: string - page: string -} - -export default function middlewareLoader(this: any) { - const { absolutePagePath, page }: EdgeFunctionLoaderOptions = - this.getOptions() - const stringifiedPagePath = stringifyRequest(this, absolutePagePath) - const buildInfo = getModuleBuildInfo(this._module) - buildInfo.nextEdgeApiFunction = { - page: page || '/', - } - - return ` - import { adapter } from 'next/dist/server/web/adapter' - - // The condition is true when the "process" module is provided - if (process !== global.process) { - // prefer local process but global.process has correct "env" - process.env = global.process.env; - global.process = process; - } - - var mod = require(${stringifiedPagePath}) - var handler = mod.middleware || mod.default; - - if (typeof handler !== 'function') { - throw new Error('The Edge Function "pages${page}" must export a \`default\` function'); - } - - export default function (opts) { - return adapter({ - ...opts, - page: ${JSON.stringify(page)}, - handler, - }) - } - ` -} diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 8f837d2ea6053..1b3d58ab74e5f 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -14,26 +14,24 @@ import { NEXT_CLIENT_SSR_ENTRY_SUFFIX, } from '../../../shared/lib/constants' -interface EdgeFunctionDefinition { - env: string[] - files: string[] - name: string - page: string - regexp: string - wasm?: WasmBinding[] -} - export interface MiddlewareManifest { version: 1 sortedMiddleware: string[] clientInfo: [location: string, isSSR: boolean][] - middleware: { [page: string]: EdgeFunctionDefinition } - functions: { [page: string]: EdgeFunctionDefinition } + middleware: { + [page: string]: { + env: string[] + files: string[] + name: string + page: string + regexp: string + wasm?: WasmBinding[] + } + } } interface EntryMetadata { edgeMiddleware?: EdgeMiddlewareMeta - edgeApiFunction?: EdgeMiddlewareMeta edgeSSR?: EdgeSSRMeta env: Set wasmBindings: Set @@ -44,7 +42,6 @@ const middlewareManifest: MiddlewareManifest = { sortedMiddleware: [], clientInfo: [], middleware: {}, - functions: {}, version: 1, } @@ -313,8 +310,6 @@ function getExtractMetadata(params: { entryMetadata.edgeSSR = buildInfo.nextEdgeSSR } else if (buildInfo?.nextEdgeMiddleware) { entryMetadata.edgeMiddleware = buildInfo.nextEdgeMiddleware - } else if (buildInfo?.nextEdgeApiFunction) { - entryMetadata.edgeApiFunction = buildInfo.nextEdgeApiFunction } /** @@ -391,19 +386,16 @@ function getCreateAssets(params: { // There should always be metadata for the entrypoint. const metadata = metadataByEntry.get(entrypoint.name) - const page = - metadata?.edgeMiddleware?.page || - metadata?.edgeSSR?.page || - metadata?.edgeApiFunction?.page + const page = metadata?.edgeMiddleware?.page || metadata?.edgeSSR?.page if (!page) { continue } const { namedRegex } = getNamedMiddlewareRegex(page, { - catchAll: !metadata.edgeSSR && !metadata.edgeApiFunction, + catchAll: !metadata.edgeSSR, }) - const edgeFunctionDefinition: EdgeFunctionDefinition = { + middlewareManifest.middleware[page] = { env: Array.from(metadata.env), files: getEntryFiles(entrypoint.getFiles(), metadata), name: entrypoint.name, @@ -411,12 +403,6 @@ function getCreateAssets(params: { regexp: namedRegex, wasm: Array.from(metadata.wasmBindings), } - - if (metadata.edgeApiFunction /* || metadata.edgeSSR */) { - middlewareManifest.functions[page] = edgeFunctionDefinition - } else { - middlewareManifest.middleware[page] = edgeFunctionDefinition - } } middlewareManifest.sortedMiddleware = getSortedRoutes( diff --git a/packages/next/server/body-streams.ts b/packages/next/server/body-streams.ts index 19a3c20c004b9..38b14e54e7d19 100644 --- a/packages/next/server/body-streams.ts +++ b/packages/next/server/body-streams.ts @@ -19,7 +19,7 @@ function requestToBodyStream(request: IncomingMessage): BodyStream { return transform.readable as unknown as ReadableStream } -export function bodyStreamToNodeStream(bodyStream: BodyStream): Readable { +function bodyStreamToNodeStream(bodyStream: BodyStream): Readable { const reader = bodyStream.getReader() return Readable.from( (async function* () { diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 290f481edf50c..0b733bd28fb3c 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -354,9 +354,7 @@ export default class DevServer extends Server { onClient: () => {}, onServer: () => {}, onEdgeServer: () => { - if (!pageName.startsWith('/api/')) { - routedMiddleware.push(pageName) - } + routedMiddleware.push(pageName) ssrMiddleware.add(pageName) }, }) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 6b72fb474cfe8..969bf533211c1 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -72,9 +72,9 @@ import { getCustomRoute } from './server-route-utils' import { urlQueryToSearchParams } from '../shared/lib/router/utils/querystring' import ResponseCache from '../server/response-cache' import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash' -import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info' +import { clonableBodyForRequest } from './body-streams' import { getMiddlewareRegex } from '../shared/lib/router/utils/route-regex' -import { bodyStreamToNodeStream, clonableBodyForRequest } from './body-streams' +import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info' export * from './base-server' @@ -540,19 +540,6 @@ export default class NextNodeServer extends BaseServer { page: string, builtPagePath: string ): Promise { - const handledAsEdgeFunction = await this.runEdgeFunctionApiEndpoint({ - req, - res, - query, - params, - page, - builtPagePath, - }) - - if (handledAsEdgeFunction) { - return true - } - const pageModule = await require(builtPagePath) query = { ...query, ...params } @@ -1044,15 +1031,11 @@ export default class NextNodeServer extends BaseServer { } /** - * Get information for the edge function located in the provided page - * folder. If the edge function info can't be found it will throw + * Get information for the middleware located in the provided page + * folder. If the middleware info can't be found it will throw * an error. */ - protected getEdgeFunctionInfo(params: { - page: string - /** Whether we should look for a middleware or not */ - middleware: boolean - }) { + protected getMiddlewareInfo(page: string) { const manifest: MiddlewareManifest = require(join( this.serverDistDir, MIDDLEWARE_MANIFEST @@ -1061,14 +1044,12 @@ export default class NextNodeServer extends BaseServer { let foundPage: string try { - foundPage = denormalizePagePath(normalizePagePath(params.page)) + foundPage = denormalizePagePath(normalizePagePath(page)) } catch (err) { - throw pageNotFoundError(params.page) + throw pageNotFoundError(page) } - let pageInfo = params.middleware - ? manifest.middleware[foundPage] - : manifest.functions[foundPage] + let pageInfo = manifest.middleware[foundPage] if (!pageInfo) { throw pageNotFoundError(foundPage) } @@ -1094,10 +1075,7 @@ export default class NextNodeServer extends BaseServer { _isSSR?: boolean ): Promise { try { - return ( - this.getEdgeFunctionInfo({ page: pathname, middleware: true }).paths - .length > 0 - ) + return this.getMiddlewareInfo(pathname).paths.length > 0 } catch (_) {} return false @@ -1164,10 +1142,7 @@ export default class NextNodeServer extends BaseServer { } await this.ensureMiddleware(middleware.page, middleware.ssr) - const middlewareInfo = this.getEdgeFunctionInfo({ - page: middleware.page, - middleware: true, - }) + const middlewareInfo = this.getMiddlewareInfo(middleware.page) result = await run({ name: middlewareInfo.name, @@ -1436,80 +1411,4 @@ export default class NextNodeServer extends BaseServer { this.warnIfQueryParametersWereDeleted = () => {} } } - - private async runEdgeFunctionApiEndpoint(params: { - req: NodeNextRequest - res: NodeNextResponse - query: ParsedUrlQuery - params: Params | false - page: string - builtPagePath: string - }): Promise { - let middlewareInfo: ReturnType | undefined - - try { - middlewareInfo = this.getEdgeFunctionInfo({ - page: params.page, - middleware: false, - }) - } catch { - return false - } - - // For middleware to "fetch" we must always provide an absolute URL - const url = getRequestMeta(params.req, '__NEXT_INIT_URL')! - if (!url.startsWith('http')) { - throw new Error( - 'To use middleware you must provide a `hostname` and `port` to the Next.js Server' - ) - } - - const result = await run({ - name: middlewareInfo.name, - paths: middlewareInfo.paths, - env: middlewareInfo.env, - wasm: middlewareInfo.wasm, - request: { - headers: params.req.headers, - method: params.req.method, - nextConfig: { - basePath: this.nextConfig.basePath, - i18n: this.nextConfig.i18n, - trailingSlash: this.nextConfig.trailingSlash, - }, - url, - page: { - name: params.page, - ...(params.params && { params: params.params }), - }, - // TODO(gal): complete body - // body: originalBody?.cloneBodyStream(), - }, - useCache: !this.nextConfig.experimental.runtime, - onWarning: (_warning: Error) => { - // if (params.onWarning) { - // warning.message += ` "./${middlewareInfo.name}"` - // params.onWarning(warning) - // } - }, - }) - - params.res.statusCode = result.response.status - params.res.statusMessage = result.response.statusText - - result.response.headers.forEach((value, key) => { - params.res.appendHeader(key, value) - }) - - if (result.response.body) { - // TODO(gal): not sure that we always need to stream - bodyStreamToNodeStream(result.response.body).pipe( - params.res.originalResponse - ) - } else { - params.res.originalResponse.end() - } - - return true - } } diff --git a/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/hello.js b/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/hello.js deleted file mode 100644 index c8e368a7530a0..0000000000000 --- a/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/hello.js +++ /dev/null @@ -1,7 +0,0 @@ -export default (req) => { - return new Response(`Hello from ${req.url}`) -} - -export const config = { - runtime: 'edge', -} diff --git a/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/node.js b/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/node.js deleted file mode 100644 index 5587ef8457afc..0000000000000 --- a/test/integration/react-streaming-and-server-components/switchable-runtime/pages/api/node.js +++ /dev/null @@ -1,3 +0,0 @@ -export default (req, res) => { - res.send('Hello, world') -} diff --git a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js index bb5ad381e84d4..0a6cbade04325 100644 --- a/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js +++ b/test/integration/react-streaming-and-server-components/test/switchable-runtime.test.js @@ -12,7 +12,6 @@ import { renderViaHTTP, waitFor, } from 'next-test-utils' -import { readJson } from 'fs-extra' const appDir = join(__dirname, '../switchable-runtime') @@ -175,31 +174,6 @@ describe('Switchable runtime (prod)', () => { }) }) - it('should build /api/hello as an api route with edge runtime', async () => { - const response = await fetchViaHTTP(context.appPort, '/api/hello') - const text = await response.text() - expect(text).toMatch(/Hello from .+\/api\/hello/) - - const manifest = await readJson( - join(context.appDir, '.next/server/middleware-manifest.json') - ) - expect(manifest).toMatchObject({ - functions: { - '/api/hello': { - env: [], - files: [ - 'server/edge-runtime-webpack.js', - 'server/pages/api/hello.js', - ], - name: 'pages/api/hello', - page: '/api/hello', - regexp: '^/api/hello$', - wasm: [], - }, - }, - }) - }) - it('should display correct tree view with page types in terminal', async () => { const stdoutLines = splitLines(context.stdout).filter((line) => /^[┌├└/]/.test(line) @@ -207,8 +181,6 @@ describe('Switchable runtime (prod)', () => { const expectedOutputLines = splitLines(` ┌ /_app ├ ○ /404 - ├ ℇ /api/hello - ├ λ /api/node ├ ℇ /edge ├ ℇ /edge-rsc ├ ○ /node @@ -220,16 +192,12 @@ describe('Switchable runtime (prod)', () => { ├ λ /node-ssr └ ○ /static `) - - const mappedOutputLines = expectedOutputLines.map((_line, index) => { - /** @type {string} */ - const str = stdoutLines[index] - const beginningOfPath = str.indexOf('/') - const endOfPath = str.indexOf(' ', beginningOfPath) - return str.slice(0, endOfPath) + const isMatched = expectedOutputLines.every((line, index) => { + const matched = stdoutLines[index].startsWith(line) + return matched }) - expect(mappedOutputLines).toEqual(expectedOutputLines) + expect(isMatched).toBe(true) }) it('should prefetch data for static pages', async () => { @@ -371,29 +339,4 @@ describe('Switchable runtime (dev)', () => { 'This is a static RSC page.' ) }) - - it('should build /api/hello as an api route with edge runtime', async () => { - const response = await fetchViaHTTP(context.appPort, '/api/hello') - const text = await response.text() - expect(text).toMatch(/Hello from .+\/api\/hello/) - - const manifest = await readJson( - join(context.appDir, '.next/server/middleware-manifest.json') - ) - expect(manifest).toMatchObject({ - functions: { - '/api/hello': { - env: [], - files: [ - 'server/edge-runtime-webpack.js', - 'server/pages/api/hello.js', - ], - name: 'pages/api/hello', - page: '/api/hello', - regexp: '^/api/hello$', - wasm: [], - }, - }, - }) - }) })