From 9805399faa6b0ec95d3b1c2d702627680368203c Mon Sep 17 00:00:00 2001 From: Naoyuki Kanezawa Date: Wed, 6 Apr 2022 21:35:52 +0700 Subject: [PATCH] Revert "fix the dynamic routing of middleware" (#35932) Reverts vercel/next.js#32601 --- packages/next/build/index.ts | 35 +- packages/next/server/base-server.ts | 91 +++-- packages/next/server/dev/next-dev-server.ts | 61 +-- packages/next/server/next-server.ts | 357 ++++++++---------- packages/next/server/router.ts | 36 +- packages/next/server/web-server.ts | 5 +- packages/next/shared/lib/router/router.ts | 18 +- .../lib/router/utils/get-middleware-regex.ts | 20 +- .../next/shared/lib/router/utils/index.ts | 1 - .../shared/lib/router/utils/routing-items.ts | 42 --- .../shared/lib/router/utils/sorted-routes.ts | 12 - .../custom-routes/test/index.test.js | 2 +- .../pages/middleware/[slug]/_middleware.js | 5 - .../dynamic-routing/test/index.test.js | 8 +- .../middleware/dynamic-route/next.config.js | 6 - .../pages/[slug]/[id]/_middleware.js | 7 - .../dynamic-route/pages/[slug]/[id]/index.js | 3 - .../dynamic-route/pages/[slug]/_middleware.js | 7 - .../dynamic-route/pages/[slug]/index.js | 3 - .../dynamic-route/pages/[slug]/static.js | 3 - .../dynamic-route/pages/_middleware.js | 19 - .../dynamic-route/pages/api/_middleware.js | 7 - .../dynamic-route/pages/api/index.js | 3 - .../pages/catch-all/[...rest].js | 3 - .../pages/catch-all/_middleware.js | 7 - .../dynamic-route/pages/dir/_middleware.js | 7 - .../dynamic-route/pages/dir/index.js | 3 - .../middleware/dynamic-route/pages/index.js | 3 - .../pages/optional-catch-all/[[...rest]].js | 3 - .../pages/optional-catch-all/_middleware.js | 7 - .../middleware/dynamic-route/pages/static.js | 3 - .../dynamic-route/public/robots.txt | 7 - .../dynamic-route/test/index.test.js | 307 --------------- test/unit/page-route-sorter.test.ts | 82 ---- 34 files changed, 261 insertions(+), 922 deletions(-) delete mode 100644 packages/next/shared/lib/router/utils/routing-items.ts delete mode 100644 test/integration/dynamic-routing/pages/middleware/[slug]/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/next.config.js delete mode 100644 test/integration/middleware/dynamic-route/pages/[slug]/[id]/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/[slug]/[id]/index.js delete mode 100644 test/integration/middleware/dynamic-route/pages/[slug]/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/[slug]/index.js delete mode 100644 test/integration/middleware/dynamic-route/pages/[slug]/static.js delete mode 100644 test/integration/middleware/dynamic-route/pages/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/api/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/api/index.js delete mode 100644 test/integration/middleware/dynamic-route/pages/catch-all/[...rest].js delete mode 100644 test/integration/middleware/dynamic-route/pages/catch-all/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/dir/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/dir/index.js delete mode 100644 test/integration/middleware/dynamic-route/pages/index.js delete mode 100644 test/integration/middleware/dynamic-route/pages/optional-catch-all/[[...rest]].js delete mode 100644 test/integration/middleware/dynamic-route/pages/optional-catch-all/_middleware.js delete mode 100644 test/integration/middleware/dynamic-route/pages/static.js delete mode 100644 test/integration/middleware/dynamic-route/public/robots.txt delete mode 100644 test/integration/middleware/dynamic-route/test/index.test.js diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 44fd308b454c780..37d7f95338dae5a 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -450,18 +450,12 @@ export default async function build( namedRegex?: string routeKeys?: { [key: string]: string } }> - dynamicRoutes: Array< - | { - page: string - regex: string - namedRegex?: string - routeKeys?: { [key: string]: string } - } - | { - page: string - isMiddleware: true - } - > + dynamicRoutes: Array<{ + page: string + regex: string + namedRegex?: string + routeKeys?: { [key: string]: string } + }> dataRoutes: Array<{ page: string routeKeys?: { [key: string]: string } @@ -480,14 +474,14 @@ export default async function build( localeDetection?: false } } = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => ({ - version: 4, + version: 3, pages404: true, basePath: config.basePath, redirects: redirects.map((r: any) => buildCustomRoute(r, 'redirect')), headers: headers.map((r: any) => buildCustomRoute(r, 'header')), dynamicRoutes: getSortedRoutes(pageKeys) - .filter((page) => isDynamicRoute(page)) - .map(pageToRouteOrMiddleware), + .filter((page) => isDynamicRoute(page) && !page.match(MIDDLEWARE_ROUTE)) + .map(pageToRoute), staticRoutes: getSortedRoutes(pageKeys) .filter( (page) => @@ -2206,14 +2200,3 @@ function pageToRoute(page: string) { namedRegex: routeRegex.namedRegex, } } - -function pageToRouteOrMiddleware(page: string) { - if (page.match(MIDDLEWARE_ROUTE)) { - return { - page: page.replace(/\/_middleware$/, '') || '/', - isMiddleware: true as const, - } - } - - return pageToRoute(page) -} diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index eb729f66337b732..7d439aa7b61be09 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -29,9 +29,10 @@ import { TEMPORARY_REDIRECT_STATUS, } from '../shared/lib/constants' import { - getRoutingItems, + getRouteMatcher, + getRouteRegex, + getSortedRoutes, isDynamicRoute, - RoutingItem, } from '../shared/lib/router/utils' import { setLazyProp, @@ -70,6 +71,12 @@ export type FindComponentsResult = { query: NextParsedUrlQuery } +interface RoutingItem { + page: string + match: ReturnType + ssr?: boolean +} + export interface Options { /** * Object containing the configuration next.config.js @@ -170,8 +177,8 @@ export default abstract class Server { protected dynamicRoutes?: DynamicRoutes protected customRoutes: CustomRoutes protected middlewareManifest?: MiddlewareManifest + protected middleware?: RoutingItem[] protected serverComponentManifest?: any - protected allRoutes?: RoutingItem[] public readonly hostname?: string public readonly port?: number @@ -183,8 +190,7 @@ export default abstract class Server { protected abstract generateImageRoutes(): Route[] protected abstract generateStaticRoutes(): Route[] protected abstract generateFsStaticRoutes(): Route[] - protected abstract generateCatchAllStaticMiddlewareRoute(): Route | undefined - protected abstract generateCatchAllDynamicMiddlewareRoute(): Route | undefined + protected abstract generateCatchAllMiddlewareRoute(): Route | undefined protected abstract generateRewrites({ restrictedRedirectPaths, }: { @@ -195,6 +201,14 @@ export default abstract class Server { fallback: Route[] } protected abstract getFilesystemPaths(): Set + protected abstract getMiddleware(): { + match: (pathname: string | null | undefined) => + | false + | { + [paramName: string]: string | string[] + } + page: string + }[] protected abstract findPageComponents( pathname: string, query?: NextParsedUrlQuery, @@ -640,11 +654,9 @@ export default abstract class Server { fallback: Route[] } fsRoutes: Route[] - internalFsRoutes: Route[] redirects: Route[] catchAllRoute: Route - catchAllStaticMiddleware?: Route - catchAllDynamicMiddleware?: Route + catchAllMiddleware?: Route pageChecker: PageChecker useFileSystemPublicRoutes: boolean dynamicRoutes: DynamicRoutes | undefined @@ -654,7 +666,7 @@ export default abstract class Server { const imageRoutes = this.generateImageRoutes() const staticFilesRoutes = this.generateStaticRoutes() - const internalFsRoutes: Route[] = [ + const fsRoutes: Route[] = [ ...this.generateFsStaticRoutes(), { match: route('/_next/data/:path*'), @@ -744,10 +756,10 @@ export default abstract class Server { } }, }, + ...publicRoutes, + ...staticFilesRoutes, ] - const fsRoutes: Route[] = [...publicRoutes, ...staticFilesRoutes] - const restrictedRedirectPaths = this.nextConfig.basePath ? [`${this.nextConfig.basePath}/_next`] : ['/_next'] @@ -766,10 +778,7 @@ export default abstract class Server { ) const rewrites = this.generateRewrites({ restrictedRedirectPaths }) - const catchAllStaticMiddleware = - this.generateCatchAllStaticMiddlewareRoute() - const catchAllDynamicMiddleware = - this.generateCatchAllDynamicMiddlewareRoute() + const catchAllMiddleware = this.generateCatchAllMiddlewareRoute() const catchAllRoute: Route = { match: route('/:path*'), @@ -804,7 +813,7 @@ export default abstract class Server { } } - if (isApiRoute(pathname)) { + if (pathname === '/api' || pathname.startsWith('/api/')) { delete query._nextBubbleNoFallback const handled = await this.handleApiRequest(req, res, pathname, query) @@ -833,19 +842,19 @@ export default abstract class Server { const { useFileSystemPublicRoutes } = this.nextConfig if (useFileSystemPublicRoutes) { - this.allRoutes = this.getAllRoutes() this.dynamicRoutes = this.getDynamicRoutes() + if (!this.minimalMode) { + this.middleware = this.getMiddleware() + } } return { headers, fsRoutes, - internalFsRoutes, rewrites, redirects, catchAllRoute, - catchAllStaticMiddleware, - catchAllDynamicMiddleware, + catchAllMiddleware, useFileSystemPublicRoutes, dynamicRoutes: this.dynamicRoutes, basePath: this.nextConfig.basePath, @@ -922,34 +931,24 @@ export default abstract class Server { return this.runApi(req, res, query, params, page, builtPagePath) } - protected getAllRoutes(): RoutingItem[] { - const pages = Object.keys(this.pagesManifest!).map( - (page) => - normalizeLocalePath(page, this.nextConfig.i18n?.locales).pathname - ) - const middlewareMap = this.minimalMode - ? {} - : this.middlewareManifest?.middleware || {} - const middleware = Object.keys(middlewareMap).map((page) => ({ - page, - ssr: !MIDDLEWARE_ROUTE.test(middlewareMap[page].name), - })) - return getRoutingItems(pages, middleware) - } - protected getDynamicRoutes(): Array { const addedPages = new Set() - return this.allRoutes!.filter((item) => { - if ( - item.isMiddleware || - addedPages.has(item.page) || - !isDynamicRoute(item.page) + return getSortedRoutes( + Object.keys(this.pagesManifest!).map( + (page) => + normalizeLocalePath(page, this.nextConfig.i18n?.locales).pathname ) - return false - addedPages.add(item.page) - return true - }) + ) + .map((page) => { + if (addedPages.has(page) || !isDynamicRoute(page)) return null + addedPages.add(page) + return { + page, + match: getRouteMatcher(getRouteRegex(page)), + } + }) + .filter((item): item is RoutingItem => Boolean(item)) } protected async run( @@ -1941,10 +1940,6 @@ export function prepareServerlessUrl( export { stringifyQuery } from './server-route-utils' -export function isApiRoute(pathname: string) { - return pathname === '/api' || pathname.startsWith('/api/') -} - class NoFallbackError extends Error {} // Internal wrapper around build errors at development diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 1a8659acac2526a..2bbde53f9322514 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -34,9 +34,10 @@ import { DEV_MIDDLEWARE_MANIFEST, } from '../../shared/lib/constants' import { - getRoutingItems, + getRouteMatcher, + getRouteRegex, + getSortedRoutes, isDynamicRoute, - RoutingItem, } from '../../shared/lib/router/utils' import Server, { WrappedBuildError } from '../next-server' import { normalizePagePath } from '../normalize-page-path' @@ -58,6 +59,7 @@ import { } from 'next/dist/compiled/@next/react-dev-overlay/middleware' import * as Log from '../../build/output/log' import isError, { getProperError } from '../../lib/is-error' +import { getMiddlewareRegex } from '../../shared/lib/router/utils/get-middleware-regex' import { isCustomErrorPage, isReservedPage } from '../../build/utils' import { NodeNextResponse, NodeNextRequest } from '../base-http/node' import { getPageRuntime, invalidatePageRuntimeCache } from '../../build/entries' @@ -91,7 +93,6 @@ export default class DevServer extends Server { private isCustomServer: boolean protected sortedRoutes?: string[] private addedUpgradeListener = false - private middleware?: RoutingItem[] protected staticPathsWorker?: { [key: string]: any } & { loadStaticPaths: typeof import('./static-paths-worker').loadStaticPaths @@ -260,6 +261,7 @@ export default class DevServer extends Server { const routedMiddleware = [] const routedPages = [] const knownFiles = wp.getTimeInfoEntries() + const ssrMiddleware = new Set() for (const [fileName, { accuracy, safeTime }] of knownFiles) { if (accuracy === undefined || !regexPageExtension.test(fileName)) { @@ -267,13 +269,11 @@ export default class DevServer extends Server { } if (regexMiddleware.test(fileName)) { - routedMiddleware.push({ - page: - `/${relative(pagesDir!, fileName).replace(/\\+/g, '/')}` - .replace(/^\/+/g, '/') - .replace(regexMiddleware, '') || '/', - ssr: false, - }) + routedMiddleware.push( + `/${relative(pagesDir!, fileName).replace(/\\+/g, '/')}` + .replace(/^\/+/g, '/') + .replace(regexMiddleware, '/') + ) continue } @@ -293,22 +293,26 @@ export default class DevServer extends Server { isEdgeRuntime && !(isReservedPage(pageName) || isCustomErrorPage(pageName)) ) { - routedMiddleware.push({ page: pageName, ssr: true }) + routedMiddleware.push(pageName) + ssrMiddleware.add(pageName) } routedPages.push(pageName) } - this.allRoutes = getRoutingItems(routedPages, routedMiddleware) - this.middleware = this.allRoutes.filter((r) => r.isMiddleware) + this.middleware = getSortedRoutes(routedMiddleware).map((page) => ({ + match: getRouteMatcher( + getMiddlewareRegex(page, !ssrMiddleware.has(page)) + ), + page, + ssr: ssrMiddleware.has(page), + })) try { // we serve a separate manifest with all pages for the client in // dev mode so that we can match a page after a rewrite on the client // before it has been built and is populated in the _buildManifest - const sortedRoutes = this.allRoutes - .filter((r) => !r.isMiddleware) - .map((r) => r.page) + const sortedRoutes = getSortedRoutes(routedPages) if ( !this.sortedRoutes?.every((val, idx) => val === sortedRoutes[idx]) @@ -318,9 +322,12 @@ export default class DevServer extends Server { } this.sortedRoutes = sortedRoutes - this.dynamicRoutes = this.allRoutes.filter( - (r) => !r.isMiddleware && isDynamicRoute(r.page) - ) + this.dynamicRoutes = this.sortedRoutes + .filter(isDynamicRoute) + .map((page) => ({ + page, + match: getRouteMatcher(getRouteRegex(page)), + })) this.router.setDynamicRoutes(this.dynamicRoutes) @@ -528,7 +535,6 @@ export default class DevServer extends Server { response: BaseNextResponse parsedUrl: ParsedNextUrl parsed: UrlWithParsedQuery - middleware: RoutingItem[] }): Promise { try { const result = await super.runMiddleware({ @@ -743,12 +749,11 @@ export default class DevServer extends Server { } generateRoutes() { - const { fsRoutes, internalFsRoutes, ...otherRoutes } = - super.generateRoutes() + const { fsRoutes, ...otherRoutes } = super.generateRoutes() // In development we expose all compiled files for react-error-overlay's line show feature // We use unshift so that we're sure the routes is defined before Next's default routes - internalFsRoutes.unshift({ + fsRoutes.unshift({ match: route('/_next/development/:path*'), type: 'route', name: '_next/development catchall', @@ -761,7 +766,7 @@ export default class DevServer extends Server { }, }) - internalFsRoutes.unshift({ + fsRoutes.unshift({ match: route( `/_next/${CLIENT_STATIC_FILES_PATH}/${this.buildId}/${DEV_CLIENT_PAGES_MANIFEST}` ), @@ -783,7 +788,7 @@ export default class DevServer extends Server { }, }) - internalFsRoutes.unshift({ + fsRoutes.unshift({ match: route( `/_next/${CLIENT_STATIC_FILES_PATH}/${this.buildId}/${DEV_MIDDLEWARE_MANIFEST}` ), @@ -832,7 +837,7 @@ export default class DevServer extends Server { }, }) - return { fsRoutes, internalFsRoutes, ...otherRoutes } + return { fsRoutes, ...otherRoutes } } // In development public files are not added to the router but handled as a fallback instead @@ -840,10 +845,6 @@ export default class DevServer extends Server { return [] } - protected getAllRoutes(): RoutingItem[] { - return [] - } - // In development dynamic routes cannot be known ahead of time protected getDynamicRoutes(): never[] { return [] diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 0b1832862c534fb..c0e8a2b9b3cc349 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -3,7 +3,7 @@ import type { Params, Route } from './router' import type { CacheFs } from '../shared/lib/utils' import type { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin' import type RenderResult from './render-result' -import type { FetchEventResult, RequestData } from './web/types' +import type { FetchEventResult } from './web/types' import type { ParsedNextUrl } from '../shared/lib/router/utils/parse-next-url' import type { PrerenderManifest } from '../build' import type { Rewrite } from '../lib/load-custom-routes' @@ -57,7 +57,6 @@ import BaseServer, { FindComponentsResult, prepareServerlessUrl, stringifyQuery, - isApiRoute, } from './base-server' import { getMiddlewareInfo, getPagePath, requireFontManifest } from './require' import { normalizePagePath } from './normalize-page-path' @@ -69,14 +68,14 @@ import { relativizeURL } from '../shared/lib/router/utils/relativize-url' import { parseNextUrl } from '../shared/lib/router/utils/parse-next-url' import { prepareDestination } from '../shared/lib/router/utils/prepare-destination' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' -import { RoutingItem } from '../shared/lib/router/utils' +import { getMiddlewareRegex, getRouteMatcher } from '../shared/lib/router/utils' +import { MIDDLEWARE_ROUTE } from '../lib/constants' import { loadEnvConfig } from '@next/env' import { getCustomRoute } from './server-route-utils' import { urlQueryToSearchParams } from '../shared/lib/router/utils/querystring' import ResponseCache from '../server/response-cache' import { removePathTrailingSlash } from '../client/normalize-trailing-slash' import { clonableBodyForRequest } from './body-streams' -import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' export * from './base-server' @@ -1025,229 +1024,192 @@ export default class NextNodeServer extends BaseServer { } } - protected generateCatchAllStaticMiddlewareRoute(): Route | undefined { + protected generateCatchAllMiddlewareRoute(): Route | undefined { if (this.minimalMode) return undefined return { match: route('/:path*'), type: 'route', - name: 'static middleware catchall', + name: 'middleware catchall', fn: async (req, res, _params, parsed) => { - return await this.handleMiddlewareRoute({ - isDynamic: false, - req, - res, - parsed, - }) - }, - } - } - - protected generateCatchAllDynamicMiddlewareRoute(): Route | undefined { - if (this.minimalMode) return undefined + if (!this.middleware?.length) { + return { finished: false } + } - return { - match: route('/:path*'), - type: 'route', - name: 'dynamic middleware catchall', - fn: async (req, res, _params, parsed) => { - return await this.handleMiddlewareRoute({ - isDynamic: true, - req, - res, - parsed, + const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')! + const parsedUrl = parseNextUrl({ + url: initUrl, + headers: req.headers, + nextConfig: { + basePath: this.nextConfig.basePath, + i18n: this.nextConfig.i18n, + trailingSlash: this.nextConfig.trailingSlash, + }, }) - }, - } - } - private async handleMiddlewareRoute({ - isDynamic, - parsed, - req, - res, - }: { - isDynamic: boolean - parsed: NextUrlWithParsedQuery - req: BaseNextRequest - res: BaseNextResponse - }) { - if ( - !this.allRoutes?.some( - (r) => r.isMiddleware && isDynamicRoute(r.page) === isDynamic - ) - ) { - return { finished: false } - } - - const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')! - const parsedUrl = parseNextUrl({ - url: initUrl, - headers: req.headers, - nextConfig: { - basePath: this.nextConfig.basePath, - i18n: this.nextConfig.i18n, - trailingSlash: this.nextConfig.trailingSlash, - }, - }) - const normalizedPathname = removePathTrailingSlash(parsedUrl.pathname) - - const middleware: RoutingItem[] = [] - for (const item of this.allRoutes || []) { - if (item.match(normalizedPathname)) { - if (!item.isMiddleware) break - if (isDynamicRoute(item.page) !== isDynamic) continue + const normalizedPathname = removePathTrailingSlash(parsedUrl.pathname) + if (!this.middleware?.some((m) => m.match(normalizedPathname))) { + return { finished: false } + } - middleware.push(item) - } - } + let result: FetchEventResult | null = null - if (!middleware.length) { - return { finished: false } - } + try { + result = await this.runMiddleware({ + request: req, + response: res, + parsedUrl: parsedUrl, + parsed: parsed, + }) + } catch (err) { + if (isError(err) && err.code === 'ENOENT') { + await this.render404(req, res, parsed) + return { finished: true } + } - let result: FetchEventResult | null = null + const error = getProperError(err) + console.error(error) + res.statusCode = 500 + this.renderError(error, req, res, parsed.pathname || '') + return { finished: true } + } - try { - result = await this.runMiddleware({ - request: req, - response: res, - parsedUrl: parsedUrl, - parsed: parsed, - middleware, - }) - } catch (err) { - if (isError(err) && err.code === 'ENOENT') { - await this.render404(req, res, parsed) - return { finished: true } - } + if (result === null) { + return { finished: true } + } - const error = getProperError(err) - console.error(error) - res.statusCode = 500 - this.renderError(error, req, res, parsed.pathname || '') - return { finished: true } - } + if (result.response.headers.has('x-middleware-rewrite')) { + const value = result.response.headers.get('x-middleware-rewrite')! + const rel = relativizeURL(value, initUrl) + result.response.headers.set('x-middleware-rewrite', rel) + } - if (result === null) { - return { finished: true } - } + if (result.response.headers.has('Location')) { + const value = result.response.headers.get('Location')! + const rel = relativizeURL(value, initUrl) + result.response.headers.set('Location', rel) + } - if (result.response.headers.has('x-middleware-rewrite')) { - const value = result.response.headers.get('x-middleware-rewrite')! - const rel = relativizeURL(value, initUrl) - result.response.headers.set('x-middleware-rewrite', rel) - } + if ( + !result.response.headers.has('x-middleware-rewrite') && + !result.response.headers.has('x-middleware-next') && + !result.response.headers.has('Location') + ) { + result.response.headers.set('x-middleware-refresh', '1') + } - if (result.response.headers.has('Location')) { - const value = result.response.headers.get('Location')! - const rel = relativizeURL(value, initUrl) - result.response.headers.set('Location', rel) - } + result.response.headers.delete('x-middleware-next') - if ( - !result.response.headers.has('x-middleware-rewrite') && - !result.response.headers.has('x-middleware-next') && - !result.response.headers.has('Location') - ) { - result.response.headers.set('x-middleware-refresh', '1') - } + for (const [key, value] of Object.entries( + toNodeHeaders(result.response.headers) + )) { + if (key !== 'content-encoding' && value !== undefined) { + res.setHeader(key, value) + } + } - result.response.headers.delete('x-middleware-next') + const preflight = + req.method === 'HEAD' && req.headers['x-middleware-preflight'] - for (const [key, value] of Object.entries( - toNodeHeaders(result.response.headers) - )) { - if (key !== 'content-encoding' && value !== undefined) { - res.setHeader(key, value) - } - } + if (preflight) { + res.statusCode = 200 + res.send() + return { + finished: true, + } + } - const preflight = - req.method === 'HEAD' && req.headers['x-middleware-preflight'] + res.statusCode = result.response.status + res.statusMessage = result.response.statusText - if (preflight) { - res.statusCode = 200 - res.send() - return { - finished: true, - } - } + const location = result.response.headers.get('Location') + if (location) { + res.statusCode = result.response.status + if (res.statusCode === 308) { + res.setHeader('Refresh', `0;url=${location}`) + } - res.statusCode = result.response.status - res.statusMessage = result.response.statusText + res.body(location).send() + return { + finished: true, + } + } - const location = result.response.headers.get('Location') - if (location) { - res.statusCode = result.response.status - if (res.statusCode === 308) { - res.setHeader('Refresh', `0;url=${location}`) - } + if (result.response.headers.has('x-middleware-rewrite')) { + const rewritePath = result.response.headers.get( + 'x-middleware-rewrite' + )! + const parsedDestination = parseUrl(rewritePath) + const newUrl = parsedDestination.pathname - res.body(location).send() - return { - finished: true, - } - } + // TODO: remove after next minor version current `v12.0.9` + this.warnIfQueryParametersWereDeleted( + parsedUrl.query, + parsedDestination.query + ) - if (result.response.headers.has('x-middleware-rewrite')) { - const rewritePath = result.response.headers.get('x-middleware-rewrite')! - const parsedDestination = parseUrl(rewritePath) - const newUrl = parsedDestination.pathname + if ( + parsedDestination.protocol && + (parsedDestination.port + ? `${parsedDestination.hostname}:${parsedDestination.port}` + : parsedDestination.hostname) !== req.headers.host + ) { + return this.proxyRequest( + req as NodeNextRequest, + res as NodeNextResponse, + parsedDestination + ) + } - // TODO: remove after next minor version current `v12.0.9` - this.warnIfQueryParametersWereDeleted( - parsedUrl.query, - parsedDestination.query - ) + if (this.nextConfig.i18n) { + const localePathResult = normalizeLocalePath( + newUrl, + this.nextConfig.i18n.locales + ) + if (localePathResult.detectedLocale) { + parsedDestination.query.__nextLocale = + localePathResult.detectedLocale + } + } - if ( - parsedDestination.protocol && - (parsedDestination.port - ? `${parsedDestination.hostname}:${parsedDestination.port}` - : parsedDestination.hostname) !== req.headers.host - ) { - return this.proxyRequest( - req as NodeNextRequest, - res as NodeNextResponse, - parsedDestination - ) - } + addRequestMeta(req, '_nextRewroteUrl', newUrl) + addRequestMeta(req, '_nextDidRewrite', newUrl !== req.url) - if (this.nextConfig.i18n) { - const localePathResult = normalizeLocalePath( - newUrl, - this.nextConfig.i18n.locales - ) - if (localePathResult.detectedLocale) { - parsedDestination.query.__nextLocale = localePathResult.detectedLocale + return { + finished: false, + pathname: newUrl, + query: parsedDestination.query, + } } - } - addRequestMeta(req, '_nextRewroteUrl', newUrl) - addRequestMeta(req, '_nextDidRewrite', newUrl !== req.url) - - return { - finished: false, - pathname: newUrl, - query: parsedDestination.query, - } - } + if (result.response.headers.has('x-middleware-refresh')) { + res.statusCode = result.response.status + for await (const chunk of result.response.body || ([] as any)) { + this.streamResponseChunk(res as NodeNextResponse, chunk) + } + res.send() + return { + finished: true, + } + } - if (result.response.headers.has('x-middleware-refresh')) { - res.statusCode = result.response.status - for await (const chunk of result.response.body || ([] as any)) { - this.streamResponseChunk(res as NodeNextResponse, chunk) - } - res.send() - return { - finished: true, - } + return { + finished: false, + } + }, } + } - return { - finished: false, - } + protected getMiddleware() { + const middleware = this.middlewareManifest?.middleware || {} + return ( + this.middlewareManifest?.sortedMiddleware.map((page) => ({ + match: getRouteMatcher( + getMiddlewareRegex(page, MIDDLEWARE_ROUTE.test(middleware[page].name)) + ), + page, + })) || [] + ) } private middlewareBetaWarning = execOnce(() => { @@ -1261,7 +1223,6 @@ export default class NextNodeServer extends BaseServer { response: BaseNextResponse parsedUrl: ParsedNextUrl parsed: UrlWithParsedQuery - middleware: RoutingItem[] onWarning?: (warning: Error) => void }): Promise { this.middlewareBetaWarning() @@ -1277,15 +1238,11 @@ export default class NextNodeServer extends BaseServer { ) } - const page: RequestData['page'] = {} + const page: { name?: string; params?: { [key: string]: string } } = {} if (await this.hasPage(normalizedPathname)) { page.name = params.parsedUrl.pathname } else if (this.dynamicRoutes) { - const isApi = isApiRoute(normalizedPathname) for (const dynamicRoute of this.dynamicRoutes) { - // The api route should not match with others - if (isApi && !isApiRoute(dynamicRoute.page)) continue - const matchParams = dynamicRoute.match(normalizedPathname) if (matchParams) { page.name = dynamicRoute.page @@ -1303,7 +1260,7 @@ export default class NextNodeServer extends BaseServer { ? clonableBodyForRequest(params.request.body) : undefined - for (const middleware of params.middleware) { + for (const middleware of this.middleware || []) { if (middleware.match(normalizedPathname)) { if (!(await this.hasMiddleware(middleware.page, middleware.ssr))) { console.warn(`The Edge Function for ${middleware.page} was not found`) diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 32b79535082b680..075d307a14cd1dc 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -66,7 +66,6 @@ export default class Router { basePath: string headers: Route[] fsRoutes: Route[] - internalFsRoutes: Route[] redirects: Route[] rewrites: { beforeFiles: Route[] @@ -74,8 +73,7 @@ export default class Router { fallback: Route[] } catchAllRoute: Route - catchAllStaticMiddleware?: Route - catchAllDynamicMiddleware?: Route + catchAllMiddleware?: Route pageChecker: PageChecker dynamicRoutes: DynamicRoutes useFileSystemPublicRoutes: boolean @@ -86,7 +84,6 @@ export default class Router { basePath = '', headers = [], fsRoutes = [], - internalFsRoutes = [], rewrites = { beforeFiles: [], afterFiles: [], @@ -94,8 +91,7 @@ export default class Router { }, redirects = [], catchAllRoute, - catchAllStaticMiddleware, - catchAllDynamicMiddleware, + catchAllMiddleware, dynamicRoutes = [], pageChecker, useFileSystemPublicRoutes, @@ -104,7 +100,6 @@ export default class Router { basePath: string headers: Route[] fsRoutes: Route[] - internalFsRoutes: Route[] rewrites: { beforeFiles: Route[] afterFiles: Route[] @@ -112,8 +107,7 @@ export default class Router { } redirects: Route[] catchAllRoute: Route - catchAllStaticMiddleware?: Route - catchAllDynamicMiddleware?: Route + catchAllMiddleware?: Route dynamicRoutes: DynamicRoutes | undefined pageChecker: PageChecker useFileSystemPublicRoutes: boolean @@ -122,13 +116,11 @@ export default class Router { this.basePath = basePath this.headers = headers this.fsRoutes = fsRoutes - this.internalFsRoutes = internalFsRoutes this.rewrites = rewrites this.redirects = redirects this.pageChecker = pageChecker this.catchAllRoute = catchAllRoute - this.catchAllStaticMiddleware = catchAllStaticMiddleware - this.catchAllDynamicMiddleware = catchAllDynamicMiddleware + this.catchAllMiddleware = catchAllMiddleware this.dynamicRoutes = dynamicRoutes this.useFileSystemPublicRoutes = useFileSystemPublicRoutes this.locales = locales @@ -174,7 +166,7 @@ export default class Router { const originalFsPathname = checkParsedUrl.pathname const fsPathname = replaceBasePath(originalFsPathname!, this.basePath) - for (const fsRoute of [...this.internalFsRoutes, ...this.fsRoutes]) { + for (const fsRoute of this.fsRoutes) { const fsParams = fsRoute.match(fsPathname) if (fsParams) { @@ -233,14 +225,10 @@ export default class Router { ...this.headers, ...this.redirects, ...this.rewrites.beforeFiles, - ...this.internalFsRoutes, - ...(this.useFileSystemPublicRoutes && this.catchAllStaticMiddleware - ? [this.catchAllStaticMiddleware] + ...(this.useFileSystemPublicRoutes && this.catchAllMiddleware + ? [this.catchAllMiddleware] : []), ...this.fsRoutes, - ...(this.useFileSystemPublicRoutes && this.catchAllDynamicMiddleware - ? [this.catchAllDynamicMiddleware] - : []), // We only check the catch-all route if public page routes hasn't been // disabled ...(this.useFileSystemPublicRoutes @@ -311,15 +299,9 @@ export default class Router { const requireBasePath = testRoute.requireBasePath !== false const isCustomRoute = customRouteTypes.has(testRoute.type) const isPublicFolderCatchall = testRoute.name === 'public folder catchall' - const isStaticMiddlewareCatchall = - testRoute.name === 'static middleware catchall' - const isDynamicMiddlewareCatchall = - testRoute.name === 'dynamic middleware catchall' + const isMiddlewareCatchall = testRoute.name === 'middleware catchall' const keepBasePath = - isCustomRoute || - isPublicFolderCatchall || - isStaticMiddlewareCatchall || - isDynamicMiddlewareCatchall + isCustomRoute || isPublicFolderCatchall || isMiddlewareCatchall const keepLocale = isCustomRoute const currentPathnameNoBasePath = replaceBasePath( diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index 0bfa8112b5f2dea..dab158fffb9d7e1 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -81,10 +81,7 @@ export default class NextWebServer extends BaseServer { protected getMiddleware() { return [] } - protected generateCatchAllStaticMiddlewareRoute() { - return undefined - } - protected generateCatchAllDynamicMiddlewareRoute() { + protected generateCatchAllMiddlewareRoute() { return undefined } protected getFontManifest() { diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 331dc563b179044..2816ab180e804a4 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -36,8 +36,8 @@ import { searchParamsToUrlQuery } from './utils/querystring' import resolveRewrites from './utils/resolve-rewrites' import { getRouteMatcher } from './utils/route-matcher' import { getRouteRegex } from './utils/route-regex' +import { getMiddlewareRegex } from './utils/get-middleware-regex' import { formatWithValidation } from './utils/format-url' -import { getRoutingItems } from './utils/routing-items' declare global { interface Window { @@ -1888,18 +1888,10 @@ export default class Router implements BaseRouter { options.locale ) - const middlewareList = await this.pageLoader.getMiddlewareList() - const middleware = middlewareList.map(([page, ssr]) => ({ page, ssr })) - const routingItems = getRoutingItems(options.pages, middleware) - let requiresPreflight = false - for (const item of routingItems) { - if (item.match(cleanedAs)) { - if (item.isMiddleware) { - requiresPreflight = true - } - break - } - } + const fns = await this.pageLoader.getMiddlewareList() + const requiresPreflight = fns.some(([middleware, isSSR]) => { + return getRouteMatcher(getMiddlewareRegex(middleware, !isSSR))(cleanedAs) + }) if (!requiresPreflight) { return { type: 'next' } diff --git a/packages/next/shared/lib/router/utils/get-middleware-regex.ts b/packages/next/shared/lib/router/utils/get-middleware-regex.ts index 0b036e1a1243e62..446a05993683c56 100644 --- a/packages/next/shared/lib/router/utils/get-middleware-regex.ts +++ b/packages/next/shared/lib/router/utils/get-middleware-regex.ts @@ -1,20 +1,12 @@ import { getParametrizedRoute, RouteRegex } from './route-regex' -// Identify ^/[param]/ in route string -const FIRST_SEGMENT_DYNAMIC = /^\/\[[^/]+?\](?=\/|$)/ - -const NOT_API_ROUTE = '(?!/api(?:/|$))' - export function getMiddlewareRegex( normalizedRoute: string, catchAll: boolean = true ): RouteRegex { const result = getParametrizedRoute(normalizedRoute) - const notApiRegex = FIRST_SEGMENT_DYNAMIC.test(normalizedRoute) - ? NOT_API_ROUTE - : '' - let catchAllRegex = catchAll ? '(?!_next($|/)).*' : '' + let catchAllRegex = catchAll ? '(?!_next).*' : '' let catchAllGroupedRegex = catchAll ? '(?:(/.*)?)' : '' if ('routeKeys' in result) { @@ -29,10 +21,8 @@ export function getMiddlewareRegex( return { groups: result.groups, - namedRegex: `^${notApiRegex}${result.namedParameterizedRoute}${catchAllGroupedRegex}$`, - re: new RegExp( - `^${notApiRegex}${result.parameterizedRoute}${catchAllGroupedRegex}$` - ), + namedRegex: `^${result.namedParameterizedRoute}${catchAllGroupedRegex}$`, + re: new RegExp(`^${result.parameterizedRoute}${catchAllGroupedRegex}$`), routeKeys: result.routeKeys, } } @@ -46,8 +36,6 @@ export function getMiddlewareRegex( return { groups: {}, - re: new RegExp( - `^${notApiRegex}${result.parameterizedRoute}${catchAllGroupedRegex}$` - ), + re: new RegExp(`^${result.parameterizedRoute}${catchAllGroupedRegex}$`), } } diff --git a/packages/next/shared/lib/router/utils/index.ts b/packages/next/shared/lib/router/utils/index.ts index 1b756ad8632e578..aa4bce09122f324 100644 --- a/packages/next/shared/lib/router/utils/index.ts +++ b/packages/next/shared/lib/router/utils/index.ts @@ -1,6 +1,5 @@ export { getMiddlewareRegex } from './get-middleware-regex' export { getRouteMatcher } from './route-matcher' export { getRouteRegex } from './route-regex' -export { getRoutingItems, RoutingItem } from './routing-items' export { getSortedRoutes } from './sorted-routes' export { isDynamicRoute } from './is-dynamic' diff --git a/packages/next/shared/lib/router/utils/routing-items.ts b/packages/next/shared/lib/router/utils/routing-items.ts deleted file mode 100644 index 6f7b0a83a7e0c24..000000000000000 --- a/packages/next/shared/lib/router/utils/routing-items.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { getMiddlewareRegex } from './get-middleware-regex' -import { getRouteMatcher } from './route-matcher' -import { getRouteRegex } from './route-regex' -import { getSortedRoutes } from './sorted-routes' - -const MIDDLEWARE_SUFFIX = '/_middleware' - -export interface RoutingItem { - page: string - match: ReturnType - ssr?: boolean - isMiddleware?: boolean -} - -export function getRoutingItems( - pages: string[], - middleware: { page: string; ssr: boolean }[] -): RoutingItem[] { - // append the suffix so that `getSortedRoutes()` can handle middleware properly - const middlewarePages = middleware.map((m) => `${m.page}${MIDDLEWARE_SUFFIX}`) - - const middlewareMap = new Map(middleware.map((m) => [m.page, m])) - const sortedRoutes = getSortedRoutes([...pages, ...middlewarePages]) - - return sortedRoutes.map((page) => { - if (page.endsWith(MIDDLEWARE_SUFFIX)) { - const p = page.slice(0, -MIDDLEWARE_SUFFIX.length) || '/' - const { ssr } = middlewareMap.get(p)! - return { - match: getRouteMatcher(getMiddlewareRegex(p, !ssr)), - page: p, - ssr, - isMiddleware: true, - } - } else { - return { - match: getRouteMatcher(getRouteRegex(page)), - page, - } - } - }) -} diff --git a/packages/next/shared/lib/router/utils/sorted-routes.ts b/packages/next/shared/lib/router/utils/sorted-routes.ts index 455af0a17e54183..d3f0699bd1cf55a 100644 --- a/packages/next/shared/lib/router/utils/sorted-routes.ts +++ b/packages/next/shared/lib/router/utils/sorted-routes.ts @@ -4,7 +4,6 @@ class UrlNode { slugName: string | null = null restSlugName: string | null = null optionalRestSlugName: string | null = null - isMiddleware: boolean = false insert(urlPath: string): void { this._insert(urlPath.split('/').filter(Boolean), [], false) @@ -25,9 +24,6 @@ class UrlNode { if (this.optionalRestSlugName !== null) { childrenPaths.splice(childrenPaths.indexOf('[[...]]'), 1) } - if (this.isMiddleware) { - childrenPaths.splice(childrenPaths.indexOf('_middleware'), 1) - } const routes = childrenPaths .map((c) => this.children.get(c)!._smoosh(`${prefix}${c}/`)) @@ -50,12 +46,6 @@ class UrlNode { routes.unshift(r) } - if (this.isMiddleware) { - routes.unshift( - ...this.children.get('_middleware')!._smoosh(`${prefix}_middleware/`) - ) - } - if (this.restSlugName !== null) { routes.push( ...this.children @@ -192,8 +182,6 @@ class UrlNode { // nextSegment is overwritten to [] so that it can later be sorted specifically nextSegment = '[]' } - } else if (nextSegment === '_middleware' && urlPaths.length === 1) { - this.isMiddleware = true } // If this UrlNode doesn't have the nextSegment yet we create a new child UrlNode diff --git a/test/integration/custom-routes/test/index.test.js b/test/integration/custom-routes/test/index.test.js index 2c5fda4cee3ded4..404d5f7f977dac8 100644 --- a/test/integration/custom-routes/test/index.test.js +++ b/test/integration/custom-routes/test/index.test.js @@ -1201,7 +1201,7 @@ const runTests = (isDev = false) => { } expect(manifest).toEqual({ - version: 4, + version: 3, pages404: true, basePath: '', dataRoutes: [ diff --git a/test/integration/dynamic-routing/pages/middleware/[slug]/_middleware.js b/test/integration/dynamic-routing/pages/middleware/[slug]/_middleware.js deleted file mode 100644 index a2089b0349d3fb7..000000000000000 --- a/test/integration/dynamic-routing/pages/middleware/[slug]/_middleware.js +++ /dev/null @@ -1,5 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - return NextResponse.next() -} diff --git a/test/integration/dynamic-routing/test/index.test.js b/test/integration/dynamic-routing/test/index.test.js index 5301d08fb0b47c7..cc97e9a6b6b9013 100644 --- a/test/integration/dynamic-routing/test/index.test.js +++ b/test/integration/dynamic-routing/test/index.test.js @@ -1100,8 +1100,6 @@ function runTests({ dev, serverless }) { ) for (const route of manifest.dynamicRoutes) { - if (route.isMiddleware) continue - route.regex = normalizeRegEx(route.regex) // ensure regexes are valid @@ -1118,7 +1116,7 @@ function runTests({ dev, serverless }) { } expect(manifest).toEqual({ - version: 4, + version: 3, pages404: true, basePath: '', headers: [], @@ -1266,10 +1264,6 @@ function runTests({ dev, serverless }) { slug: 'slug', }, }, - { - isMiddleware: true, - page: '/middleware/[slug]', - }, { namedRegex: `^/on\\-mount/(?[^/]+?)(?:/)?$`, page: '/on-mount/[post]', diff --git a/test/integration/middleware/dynamic-route/next.config.js b/test/integration/middleware/dynamic-route/next.config.js deleted file mode 100644 index f548199a3b10a8f..000000000000000 --- a/test/integration/middleware/dynamic-route/next.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - i18n: { - locales: ['en', 'fr', 'nl'], - defaultLocale: 'en', - }, -} diff --git a/test/integration/middleware/dynamic-route/pages/[slug]/[id]/_middleware.js b/test/integration/middleware/dynamic-route/pages/[slug]/[id]/_middleware.js deleted file mode 100644 index cd610ba50e32ae7..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/[slug]/[id]/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-slug-id-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/[slug]/[id]/index.js b/test/integration/middleware/dynamic-route/pages/[slug]/[id]/index.js deleted file mode 100644 index 11f4614a671e7b9..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/[slug]/[id]/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Index() { - return

Dynamic route

-} diff --git a/test/integration/middleware/dynamic-route/pages/[slug]/_middleware.js b/test/integration/middleware/dynamic-route/pages/[slug]/_middleware.js deleted file mode 100644 index 50eb8248d9a7177..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/[slug]/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware(req) { - const response = NextResponse.next() - response.headers.set('x-slug-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/[slug]/index.js b/test/integration/middleware/dynamic-route/pages/[slug]/index.js deleted file mode 100644 index 11f4614a671e7b9..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/[slug]/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Index() { - return

Dynamic route

-} diff --git a/test/integration/middleware/dynamic-route/pages/[slug]/static.js b/test/integration/middleware/dynamic-route/pages/[slug]/static.js deleted file mode 100644 index df20ac0cca54b0d..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/[slug]/static.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Index() { - return

Nested Static route

-} diff --git a/test/integration/middleware/dynamic-route/pages/_middleware.js b/test/integration/middleware/dynamic-route/pages/_middleware.js deleted file mode 100644 index eb0704248449a96..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/_middleware.js +++ /dev/null @@ -1,19 +0,0 @@ -import { NextResponse } from 'next/server' - -export async function middleware(request) { - const response = NextResponse.next() - response.headers.set('req-url-basepath', request.nextUrl.basePath) - response.headers.set('req-url-pathname', request.nextUrl.pathname) - response.headers.set( - 'req-url-query', - JSON.stringify(Object.fromEntries(request.nextUrl.searchParams.entries())) - ) - response.headers.set('req-url-locale', request.nextUrl.locale) - if (request.page.name) { - response.headers.set('req-url-page', request.page.name) - } - if (request.page.params) { - response.headers.set('req-url-params', JSON.stringify(request.page.params)) - } - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/api/_middleware.js b/test/integration/middleware/dynamic-route/pages/api/_middleware.js deleted file mode 100644 index 19907fd0686a100..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/api/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-api-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/api/index.js b/test/integration/middleware/dynamic-route/pages/api/index.js deleted file mode 100644 index b2590beac3227a9..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/api/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function handler(req, res) { - res.status(200).json({ route: '/api' }) -} diff --git a/test/integration/middleware/dynamic-route/pages/catch-all/[...rest].js b/test/integration/middleware/dynamic-route/pages/catch-all/[...rest].js deleted file mode 100644 index 8cce0282416b07e..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/catch-all/[...rest].js +++ /dev/null @@ -1,3 +0,0 @@ -export default function CatchAll() { - return

Catch All route

-} diff --git a/test/integration/middleware/dynamic-route/pages/catch-all/_middleware.js b/test/integration/middleware/dynamic-route/pages/catch-all/_middleware.js deleted file mode 100644 index 7704289da422bb8..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/catch-all/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-catch-all-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/dir/_middleware.js b/test/integration/middleware/dynamic-route/pages/dir/_middleware.js deleted file mode 100644 index 87297804b9eb6c7..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/dir/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-dir-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/dir/index.js b/test/integration/middleware/dynamic-route/pages/dir/index.js deleted file mode 100644 index d87a6d6e3541972..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/dir/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Dir() { - return

Dir route

-} diff --git a/test/integration/middleware/dynamic-route/pages/index.js b/test/integration/middleware/dynamic-route/pages/index.js deleted file mode 100644 index 298195ba7bf5c35..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Home() { - return

Home Page

-} diff --git a/test/integration/middleware/dynamic-route/pages/optional-catch-all/[[...rest]].js b/test/integration/middleware/dynamic-route/pages/optional-catch-all/[[...rest]].js deleted file mode 100644 index efc395b5ff89ae0..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/optional-catch-all/[[...rest]].js +++ /dev/null @@ -1,3 +0,0 @@ -export default function OptionalCatchAll() { - return

Optional Catch All route

-} diff --git a/test/integration/middleware/dynamic-route/pages/optional-catch-all/_middleware.js b/test/integration/middleware/dynamic-route/pages/optional-catch-all/_middleware.js deleted file mode 100644 index cc1dd66fbaee410..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/optional-catch-all/_middleware.js +++ /dev/null @@ -1,7 +0,0 @@ -import { NextResponse } from 'next/server' - -export function middleware() { - const response = NextResponse.next() - response.headers.set('x-optional-catch-all-path', 'true') - return response -} diff --git a/test/integration/middleware/dynamic-route/pages/static.js b/test/integration/middleware/dynamic-route/pages/static.js deleted file mode 100644 index d683fec42acc0ac..000000000000000 --- a/test/integration/middleware/dynamic-route/pages/static.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function Index() { - return

Static route

-} diff --git a/test/integration/middleware/dynamic-route/public/robots.txt b/test/integration/middleware/dynamic-route/public/robots.txt deleted file mode 100644 index f5520669bf88cf4..000000000000000 --- a/test/integration/middleware/dynamic-route/public/robots.txt +++ /dev/null @@ -1,7 +0,0 @@ -User-agent: Googlebot -Disallow: /nogooglebot/ - -User-agent: * -Allow: / - -Sitemap: http://www.example.com/sitemap.xml diff --git a/test/integration/middleware/dynamic-route/test/index.test.js b/test/integration/middleware/dynamic-route/test/index.test.js deleted file mode 100644 index 02b20b3f0db2824..000000000000000 --- a/test/integration/middleware/dynamic-route/test/index.test.js +++ /dev/null @@ -1,307 +0,0 @@ -/* eslint-env jest */ - -import { join } from 'path' -import { - fetchViaHTTP, - findPort, - killApp, - launchApp, - nextBuild, - nextStart, -} from 'next-test-utils' - -jest.setTimeout(1000 * 60 * 2) -const context = {} -context.appDir = join(__dirname, '../') - -describe('Middleware dynamic tests', () => { - describe('dev mode', () => { - beforeAll(async () => { - context.appPort = await findPort() - context.app = await launchApp(context.appDir, context.appPort) - }) - afterAll(() => killApp(context.app)) - runTests() - runTests('/fr') - }) - describe('production mode', () => { - beforeAll(async () => { - await nextBuild(context.appDir) - - context.appPort = await findPort() - context.app = await nextStart(context.appDir, context.appPort) - }) - afterAll(() => killApp(context.app)) - runTests() - runTests('/fr') - }) -}) - -function runTests(localePath = '') { - const locale = localePath.slice(1) - - function fetch(path) { - return fetchViaHTTP(context.appPort, `${localePath}${path}`) - } - - it(`${localePath} should validate request url parameters from the root route`, async () => { - const res = await fetch('/') - expect(res.headers.get('req-url-pathname')).toBe('/') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-slug-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a static route`, async () => { - const res = await fetch('/static') - expect(res.headers.get('req-url-pathname')).toBe('/static') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-slug-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a dir route`, async () => { - const res = await fetch('/dir') - expect(res.headers.get('req-url-pathname')).toBe('/dir') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-dir-path')).toBe('true') - expect(res.headers.get('x-slug-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a dynamic route`, async () => { - const res = await fetch('/asdf') - expect(res.headers.get('req-url-pathname')).toBe('/asdf') - expect(res.headers.get('req-url-params')).toBe('{"slug":"asdf"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a dynamic route with query`, async () => { - const res = await fetch('/asdf?foo=bar') - expect(res.headers.get('req-url-pathname')).toBe('/asdf') - expect(res.headers.get('req-url-params')).toBe('{"slug":"asdf"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]') - expect(res.headers.get('req-url-query')).toBe('{"foo":"bar"}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a route on dynamic dir`, async () => { - const res = await fetch('/asdf/static') - expect(res.headers.get('req-url-pathname')).toBe('/asdf/static') - expect(res.headers.get('req-url-params')).toBe('{"slug":"asdf"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]/static') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a route on dynamic dir with static name`, async () => { - const res = await fetch('/static/static') - expect(res.headers.get('req-url-pathname')).toBe('/static/static') - expect(res.headers.get('req-url-params')).toBe('{"slug":"static"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]/static') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a route on dynamic dir with static dir name`, async () => { - const res = await fetch('/dir/static') - expect(res.headers.get('req-url-pathname')).toBe('/dir/static') - expect(res.headers.get('req-url-params')).toBe('{"slug":"dir"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]/static') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBe('true') - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBeNull() - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a nested dynamic route`, async () => { - const res = await fetch('/foo/bar') - expect(res.headers.get('req-url-pathname')).toBe('/foo/bar') - expect(res.headers.get('req-url-params')).toBe('{"slug":"foo","id":"bar"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]/[id]') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBe('true') - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a nested dynamic route with static dir name`, async () => { - const res = await fetch('/dir/asdf') - expect(res.headers.get('req-url-pathname')).toBe('/dir/asdf') - expect(res.headers.get('req-url-params')).toBe('{"slug":"dir","id":"asdf"}') - expect(res.headers.get('req-url-page')).toBe('/[slug]/[id]') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBe('true') - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBe('true') - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a nested non-existing route`, async () => { - const res = await fetch('/foo/bar/not-exist') - expect(res.headers.get('req-url-pathname')).toBe('/foo/bar/not-exist') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-page')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBe('true') - expect(res.headers.get('x-slug-id-path')).toBe('true') - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from a catch all route`, async () => { - const res = await fetch('/catch-all/foo/bar') - expect(res.headers.get('req-url-pathname')).toBe('/catch-all/foo/bar') - expect(res.headers.get('req-url-params')).toBe('{"rest":["foo","bar"]}') - expect(res.headers.get('req-url-page')).toBe('/catch-all/[...rest]') - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - expect(res.headers.get('x-catch-all-path')).toBe('true') - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - it(`${localePath} should validate request url parameters from an optional catch all route`, async () => { - const res = await fetch('/optional-catch-all/foo/bar') - expect(res.headers.get('req-url-pathname')).toBe( - '/optional-catch-all/foo/bar' - ) - expect(res.headers.get('req-url-params')).toBe('{"rest":["foo","bar"]}') - expect(res.headers.get('req-url-page')).toBe( - '/optional-catch-all/[[...rest]]' - ) - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - expect(res.headers.get('x-optional-catch-all-path')).toBe('true') - if (locale !== '') { - expect(res.headers.get('req-url-locale')).toBe(locale) - } - }) - - if (!localePath) { - it('should validate request url parameters from a _next route', async () => { - const res = await fetch('/_next/static/some-file.js') - expect(res.headers.get('req-url-pathname')).toBeNull() - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-page')).toBeNull() - expect(res.headers.get('req-url-query')).toBeNull() - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - }) - - it('should validate request url parameters from a api route', async () => { - const res = await fetch('/api') - expect(res.headers.get('req-url-pathname')).toBe('/api') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBe('true') - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - }) - - it('should validate request url parameters from a api route with query', async () => { - const res = await fetch('/api?foo=bar') - expect(res.headers.get('req-url-pathname')).toBe('/api') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{"foo":"bar"}') - expect(res.headers.get('x-api-path')).toBe('true') - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - }) - - it('should validate request url parameters from a non-existing api route', async () => { - const res = await fetch('/api/not-exist') - expect(res.headers.get('req-url-pathname')).toBe('/api/not-exist') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBe('true') - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - }) - - it('should validate request url parameters from a non-existing api route with query', async () => { - const res = await fetch('/api/not-exist?foo=bar') - expect(res.headers.get('req-url-pathname')).toBe('/api/not-exist') - expect(res.headers.get('req-url-params')).toBeNull() - expect(res.headers.get('req-url-query')).toBe('{"foo":"bar"}') - expect(res.headers.get('x-api-path')).toBe('true') - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - }) - - it('should validate request url parameters from a public route', async () => { - const res = await fetch('/robots.txt') - expect(res.headers.get('req-url-pathname')).toBe('/robots.txt') - - // This does not work as expected now - // see: https://github.com/vercel/next.js/issues/31721 - // expect(res.headers.get('req-url-params')).toBeNull() - - expect(res.headers.get('req-url-query')).toBe('{}') - expect(res.headers.get('x-api-path')).toBeNull() - expect(res.headers.get('x-dir-path')).toBeNull() - expect(res.headers.get('x-slug-path')).toBeNull() - expect(res.headers.get('x-slug-id-path')).toBeNull() - }) - } -} diff --git a/test/unit/page-route-sorter.test.ts b/test/unit/page-route-sorter.test.ts index eb15c23f1a963cd..a97375a09a8e820 100644 --- a/test/unit/page-route-sorter.test.ts +++ b/test/unit/page-route-sorter.test.ts @@ -66,88 +66,6 @@ describe('getSortedRoutes', () => { `) }) - it('correctly sorts with middleware', () => { - expect( - getSortedRoutes([ - '/posts', - '/[root-slug]', - '/[root-slug]/_middleware', - '/', - '/_middleware', - '/posts/[id]', - '/blog/[id]/comments/[cid]', - '/blog/abc/[id]', - '/blog/_middleware', - '/[...rest]', - '/blog/abc/post', - '/blog/abc', - '/blog/abc/_middleware', - '/blog/[id]/_middleware', - '/p1/[[...incl]]', - '/p1/_middleware', - '/p/[...rest]', - '/p/_middleware', - '/p2/[...rest]', - '/p2/[id]', - '/p2/[id]/abc', - '/p2/_middleware', - '/p2/[id]/_middleware', - '/p3/[[...rest]]', - '/p3/[id]', - '/p3/[id]/abc', - '/p3/_middleware', - '/p3/[id]/_middleware', - '/p4/_middleware/abc', - '/p4', - '/p4/not_middleware', - '/blog/[id]', - '/foo/[d]/bar/baz/[f]', - '/apples/_middleware', - '/apples/[ab]/[cd]/ef', - '/apples/[ab]/_middleware', - ]) - ).toMatchInlineSnapshot(` - Array [ - "/_middleware", - "/", - "/apples/_middleware", - "/apples/[ab]/_middleware", - "/apples/[ab]/[cd]/ef", - "/blog/_middleware", - "/blog/abc/_middleware", - "/blog/abc", - "/blog/abc/post", - "/blog/abc/[id]", - "/blog/[id]/_middleware", - "/blog/[id]", - "/blog/[id]/comments/[cid]", - "/foo/[d]/bar/baz/[f]", - "/p/_middleware", - "/p/[...rest]", - "/p1/_middleware", - "/p1/[[...incl]]", - "/p2/_middleware", - "/p2/[id]/_middleware", - "/p2/[id]", - "/p2/[id]/abc", - "/p2/[...rest]", - "/p3/_middleware", - "/p3/[id]/_middleware", - "/p3/[id]", - "/p3/[id]/abc", - "/p3/[[...rest]]", - "/p4", - "/p4/_middleware/abc", - "/p4/not_middleware", - "/posts", - "/posts/[id]", - "/[root-slug]/_middleware", - "/[root-slug]", - "/[...rest]", - ] - `) - }) - it('catches mismatched param names', () => { expect(() => getSortedRoutes([