From ff7eeb9860f587b069540592e4d83fcfe96d846c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:35:33 +0200 Subject: [PATCH 01/11] add test --- .../index.test.ts | 33 +++++++++++++++++++ test/lib/next-test-utils.js | 1 + 2 files changed, 34 insertions(+) create mode 100644 test/e2e/edge-vs.-non-edge-api-route-priority/index.test.ts diff --git a/test/e2e/edge-vs.-non-edge-api-route-priority/index.test.ts b/test/e2e/edge-vs.-non-edge-api-route-priority/index.test.ts new file mode 100644 index 000000000000..48fe0a3e92ba --- /dev/null +++ b/test/e2e/edge-vs.-non-edge-api-route-priority/index.test.ts @@ -0,0 +1,33 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { fetchViaHTTP } from 'next-test-utils' + +describe('Edge vs. non-Edge API route priority', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/api/user/login.js': ` + export default async function handler(_, res) { + res.send('from login.js') + } + `, + 'pages/api/user/[id].js': ` + export const config = { + runtime: 'experimental-edge', + } + export default async function handler() { + return new Response('from [id].js') + }`, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('more specific route should match', async () => { + const res = await fetchViaHTTP(next.url, '/api/user/login') + expect(await res.text()).toBe('from login.js') + }) +}) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index 1bcb877dac75..6f8849100e60 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -114,6 +114,7 @@ export function renderViaHTTP(appPort, pathname, query, opts) { return fetchViaHTTP(appPort, pathname, query, opts).then((res) => res.text()) } +/** @return {Response} */ export function fetchViaHTTP(appPort, pathname, query, opts) { const url = `${pathname}${ typeof query === 'string' ? query : query ? `?${qs.stringify(query)}` : '' From 19b3851de9f72bf9ecdb4f79d01bf16a974c5519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:36:37 +0200 Subject: [PATCH 02/11] cleanup --- packages/next/server/next-server.ts | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 209e93d35890..d32245dc398a 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1049,20 +1049,16 @@ export default class NextNodeServer extends BaseServer { return manifest } - /** - * Return a list of middleware routing items. This method exists to be later - * overridden by the development server in order to use a different source - * to get the list. - */ + /** Returns a middleware routing item. */ protected getMiddleware(): RoutingItem | undefined { const manifest = this.getMiddlewareManifest() - const rootMiddleware = manifest?.middleware?.['/'] - if (!rootMiddleware) { + const middleware = manifest?.middleware?.['/'] + if (!middleware) { return } return { - match: getMiddlewareMatcher(rootMiddleware), + match: getMiddlewareMatcher(middleware), page: '/', } } @@ -1079,13 +1075,6 @@ export default class NextNodeServer extends BaseServer { })) } - protected getEdgeRoutes(): RoutingItem[] { - const edgeFunctions = this.getEdgeFunctions() - const middleware = this.getMiddleware() - - return edgeFunctions.concat(middleware ? [middleware] : []) - } - /** * Get information for the edge function located in the provided page * folder. If the edge function info can't be found it will throw From b23a2684dcba4a41c93914edbe330dea256a9ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:36:52 +0200 Subject: [PATCH 03/11] remove duplicate from `allRoutes` --- packages/next/server/router.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 1159d08e21d6..ae89e5cda6ba 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -242,7 +242,6 @@ export default class Router { // disabled ...(this.useFileSystemPublicRoutes ? [ - ...(edgeSSRCatchAllRoute ? [edgeSSRCatchAllRoute] : []), { type: 'route', name: 'page checker', From 3c5d74224e34b391b18f888390b234d5aa7f0370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:43:23 +0200 Subject: [PATCH 04/11] fix test util type --- test/lib/next-test-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index 6f8849100e60..64c17c24938f 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -114,7 +114,7 @@ export function renderViaHTTP(appPort, pathname, query, opts) { return fetchViaHTTP(appPort, pathname, query, opts).then((res) => res.text()) } -/** @return {Response} */ +/** @return {Promise} */ export function fetchViaHTTP(appPort, pathname, query, opts) { const url = `${pathname}${ typeof query === 'string' ? query : query ? `?${qs.stringify(query)}` : '' From 9c6f8057621eafa02c281deace9b1e63ab57d3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:47:31 +0200 Subject: [PATCH 05/11] extend test util type with `buffer` and `headers.raw` --- test/lib/next-test-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index 64c17c24938f..a038c5a93bec 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -114,7 +114,7 @@ export function renderViaHTTP(appPort, pathname, query, opts) { return fetchViaHTTP(appPort, pathname, query, opts).then((res) => res.text()) } -/** @return {Promise} */ +/** @return {Promise} */ export function fetchViaHTTP(appPort, pathname, query, opts) { const url = `${pathname}${ typeof query === 'string' ? query : query ? `?${qs.stringify(query)}` : '' From ca6bfc9b07a9afd1b55a95c3a369a80aa18389f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:50:01 +0200 Subject: [PATCH 06/11] rephrase --- packages/next/server/next-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index d32245dc398a..9557223b8cb9 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1049,7 +1049,7 @@ export default class NextNodeServer extends BaseServer { return manifest } - /** Returns a middleware routing item. */ + /** Returns the middleware routing item if there is one. */ protected getMiddleware(): RoutingItem | undefined { const manifest = this.getMiddlewareManifest() const middleware = manifest?.middleware?.['/'] From 33d54da45655cd300a01aca5823a7a68355f20b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 10 Aug 2022 16:56:28 +0200 Subject: [PATCH 07/11] satisfy `any` TS god --- test/lib/next-test-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index a038c5a93bec..d46441e156f3 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -114,7 +114,7 @@ export function renderViaHTTP(appPort, pathname, query, opts) { return fetchViaHTTP(appPort, pathname, query, opts).then((res) => res.text()) } -/** @return {Promise} */ +/** @return {Promise} */ export function fetchViaHTTP(appPort, pathname, query, opts) { const url = `${pathname}${ typeof query === 'string' ? query : query ? `?${qs.stringify(query)}` : '' From ee8a46f49aa03a9637df4a8c24f8303d123f5f70 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 16 Aug 2022 14:18:15 +0100 Subject: [PATCH 08/11] remove edgeSSRCatchAllRoute to fix priority --- packages/next/server/base-server.ts | 2 +- packages/next/server/next-server.ts | 62 +++++++++++------------------ packages/next/server/router.ts | 10 +---- 3 files changed, 26 insertions(+), 48 deletions(-) diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index f2d03427c6dd..3ce9c6f177aa 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -129,7 +129,7 @@ export interface BaseRequestHandler { ): Promise } -type RequestContext = { +export type RequestContext = { req: BaseNextRequest res: BaseNextResponse pathname: string diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index ec5beca34983..3a1db1bb5717 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -67,6 +67,7 @@ import BaseServer, { prepareServerlessUrl, stringifyQuery, RoutingItem, + RequestContext, } from './base-server' import { getPagePath, requireFontManifest } from './require' import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' @@ -767,6 +768,28 @@ export default class NextNodeServer extends BaseServer { ) } + protected async renderPageComponent( + ctx: RequestContext, + bubbleNoFallback: boolean + ) { + const edgeFunctions = this.getEdgeFunctions() + + for (const item of edgeFunctions) { + if (item.match(ctx.pathname)) { + await this.runEdgeFunction({ + req: ctx.req, + res: ctx.res, + query: ctx.query, + params: ctx.renderOpts.params, + page: ctx.pathname, + }) + return null + } + } + + return super.renderPageComponent(ctx, bubbleNoFallback) + } + protected async findPageComponents( pathname: string, query: NextParsedUrlQuery = {}, @@ -1551,45 +1574,6 @@ export default class NextNodeServer extends BaseServer { routes.push(middlewareCatchAllRoute) } - if (this.getEdgeFunctions().length) { - const edgeCatchAllRoute: Route = { - match: getPathMatch('/:path*'), - type: 'route', - name: 'edge functions catchall', - fn: async (req, res, _params, parsed) => { - const edgeFunctions = this.getEdgeFunctions() - if (!edgeFunctions.length) return { finished: false } - - const { query, pathname } = parsed - const normalizedPathname = removeTrailingSlash(pathname || '') - let page = normalizedPathname - let params: Params | undefined = undefined - - for (const edgeFunction of edgeFunctions) { - const matched = edgeFunction.match(page) - if (matched) { - params = matched - page = edgeFunction.page - break - } - } - - const edgeSSRResult = await this.runEdgeFunction({ - req, - res, - query, - params, - page, - }) - - return { - finished: !!edgeSSRResult, - } - }, - } - - routes.push(edgeCatchAllRoute) - } } return routes diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index bf0ece09684d..0cb309f823af 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -220,8 +220,7 @@ export default class Router { - User rewrites (checking filesystem and pages each match) */ - const [middlewareCatchAllRoute, edgeSSRCatchAllRoute] = - this.catchAllMiddleware + const [middlewareCatchAllRoute] = this.catchAllMiddleware const allRoutes = [ ...(middlewareCatchAllRoute ? this.fsRoutes @@ -298,12 +297,7 @@ export default class Router { // We only check the catch-all route if public page routes hasn't been // disabled - ...(this.useFileSystemPublicRoutes - ? [ - ...(edgeSSRCatchAllRoute ? [edgeSSRCatchAllRoute] : []), - this.catchAllRoute, - ] - : []), + ...(this.useFileSystemPublicRoutes ? [this.catchAllRoute] : []), ] for (const testRoute of allRoutes) { From 51f6ca22080ba800db27aa8e46c1fb9c7666b91f Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 16 Aug 2022 14:27:28 +0100 Subject: [PATCH 09/11] lint-fix --- packages/next/server/next-server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 3a1db1bb5717..6784007f15ef 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -90,7 +90,6 @@ import { checkIsManualRevalidate } from './api-utils' import { shouldUseReactRoot, isTargetLikeServerless } from './utils' import ResponseCache from './response-cache' import { IncrementalCache } from './lib/incremental-cache' -import { getSortedRoutes } from '../shared/lib/router/utils/sorted-routes' if (shouldUseReactRoot) { ;(process.env as any).__NEXT_REACT_ROOT = 'true' From 2d9e4308d6c7785def1d84fd52d2d7a9a6f1c0f5 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 16 Aug 2022 18:06:33 +0100 Subject: [PATCH 10/11] handle failing test cases --- packages/next/server/dev/next-dev-server.ts | 1 + .../next/server/dev/on-demand-entry-handler.ts | 9 +++++++++ packages/next/server/next-server.ts | 16 ++++++---------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index a43f65c1ed3f..5f538ced0a2e 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -386,6 +386,7 @@ export default class DevServer extends Server { routedPages.push(pageName) }, onEdgeServer: () => { + routedPages.push(pageName) edgeRoutesSet.add(pageName) }, }) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index da43d221be0a..f3c820da209d 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -1,4 +1,5 @@ import type ws from 'ws' +import * as Log from '../../build/output/log' import type { webpack } from 'next/dist/compiled/webpack/webpack' import type { NextConfigComplete } from '../config-shared' import { EventEmitter } from 'events' @@ -514,6 +515,13 @@ export function onDemandEntryHandler({ return { async ensurePage(page: string, clientOnly: boolean): Promise { + const stalledTime = 60 + const stalledEnsureTimeout = setTimeout(() => { + Log.warn( + `Ensuring ${page} has taken longer than ${stalledTime}s, if this continues to stall this may be a bug` + ) + }, stalledTime * 1000) + const pagePathData = await findPagePathData( rootDir, pagesDir, @@ -630,6 +638,7 @@ export function onDemandEntryHandler({ invalidator.invalidate([...added.keys()]) await Promise.all(invalidatePromises) } + clearTimeout(stalledEnsureTimeout) }, onHMR(client: ws) { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 6784007f15ef..ac37e669e717 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -774,7 +774,7 @@ export default class NextNodeServer extends BaseServer { const edgeFunctions = this.getEdgeFunctions() for (const item of edgeFunctions) { - if (item.match(ctx.pathname)) { + if (item.page === ctx.pathname) { await this.runEdgeFunction({ req: ctx.req, res: ctx.res, @@ -1623,15 +1623,11 @@ export default class NextNodeServer extends BaseServer { }): Promise { let middlewareInfo: ReturnType | undefined - try { - await this.ensureEdgeFunction(params.page) - middlewareInfo = this.getEdgeFunctionInfo({ - page: params.page, - middleware: false, - }) - } catch { - return null - } + await this.ensureEdgeFunction(params.page) + middlewareInfo = this.getEdgeFunctionInfo({ + page: params.page, + middleware: false, + }) if (!middlewareInfo) { return null From c134e3dd7e8a0da405e8969c4132d0db0772c181 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 16 Aug 2022 18:20:42 +0100 Subject: [PATCH 11/11] fix check --- packages/next/server/next-server.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 704d9bd6c2c4..f0e869773d0c 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -673,16 +673,22 @@ export default class NextNodeServer extends BaseServer { page: string, builtPagePath: string ): Promise { - const handledAsEdgeFunction = await this.runEdgeFunction({ - req, - res, - query, - params, - page, - }) + const edgeFunctions = this.getEdgeFunctions() - if (handledAsEdgeFunction) { - return true + for (const item of edgeFunctions) { + if (item.page === page) { + const handledAsEdgeFunction = await this.runEdgeFunction({ + req, + res, + query, + params, + page, + }) + + if (handledAsEdgeFunction) { + return true + } + } } const pageModule = await require(builtPagePath)