Skip to content

Commit

Permalink
fix the dynamic routing of middleware (#32601)
Browse files Browse the repository at this point in the history
* fix the dynamic routing of middleware

* add middleware to dynamicRoutes of routes-manifest

* remove unused import

* fix middleware routing with static paths

* update manifest version test

* prevent to match with api route using regex

* use iterator instead of generator

* do not use Iterator

* fix type

* fix type

* remove unused import

* apply the fix for support colons

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
nkzawa and ijjk committed Mar 28, 2022
1 parent 5d1f33f commit 53d1b00
Show file tree
Hide file tree
Showing 34 changed files with 922 additions and 261 deletions.
35 changes: 26 additions & 9 deletions packages/next/build/index.ts
Expand Up @@ -447,12 +447,18 @@ export default async function build(
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dynamicRoutes: Array<{
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dynamicRoutes: Array<
| {
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}
| {
page: string
isMiddleware: true
}
>
dataRoutes: Array<{
page: string
routeKeys?: { [key: string]: string }
Expand All @@ -471,14 +477,14 @@ export default async function build(
localeDetection?: false
}
} = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => ({
version: 3,
version: 4,
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) && !page.match(MIDDLEWARE_ROUTE))
.map(pageToRoute),
.filter((page) => isDynamicRoute(page))
.map(pageToRouteOrMiddleware),
staticRoutes: getSortedRoutes(pageKeys)
.filter(
(page) =>
Expand Down Expand Up @@ -2190,3 +2196,14 @@ 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)
}
91 changes: 48 additions & 43 deletions packages/next/server/base-server.ts
Expand Up @@ -29,10 +29,9 @@ import {
TEMPORARY_REDIRECT_STATUS,
} from '../shared/lib/constants'
import {
getRouteMatcher,
getRouteRegex,
getSortedRoutes,
getRoutingItems,
isDynamicRoute,
RoutingItem,
} from '../shared/lib/router/utils'
import {
setLazyProp,
Expand Down Expand Up @@ -71,12 +70,6 @@ export type FindComponentsResult = {
query: NextParsedUrlQuery
}

interface RoutingItem {
page: string
match: ReturnType<typeof getRouteMatcher>
ssr?: boolean
}

export interface Options {
/**
* Object containing the configuration next.config.js
Expand Down Expand Up @@ -177,8 +170,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

Expand All @@ -190,7 +183,8 @@ export default abstract class Server {
protected abstract generateImageRoutes(): Route[]
protected abstract generateStaticRoutes(): Route[]
protected abstract generateFsStaticRoutes(): Route[]
protected abstract generateCatchAllMiddlewareRoute(): Route | undefined
protected abstract generateCatchAllStaticMiddlewareRoute(): Route | undefined
protected abstract generateCatchAllDynamicMiddlewareRoute(): Route | undefined
protected abstract generateRewrites({
restrictedRedirectPaths,
}: {
Expand All @@ -201,14 +195,6 @@ export default abstract class Server {
fallback: Route[]
}
protected abstract getFilesystemPaths(): Set<string>
protected abstract getMiddleware(): {
match: (pathname: string | null | undefined) =>
| false
| {
[paramName: string]: string | string[]
}
page: string
}[]
protected abstract findPageComponents(
pathname: string,
query?: NextParsedUrlQuery,
Expand Down Expand Up @@ -654,9 +640,11 @@ export default abstract class Server {
fallback: Route[]
}
fsRoutes: Route[]
internalFsRoutes: Route[]
redirects: Route[]
catchAllRoute: Route
catchAllMiddleware?: Route
catchAllStaticMiddleware?: Route
catchAllDynamicMiddleware?: Route
pageChecker: PageChecker
useFileSystemPublicRoutes: boolean
dynamicRoutes: DynamicRoutes | undefined
Expand All @@ -666,7 +654,7 @@ export default abstract class Server {
const imageRoutes = this.generateImageRoutes()
const staticFilesRoutes = this.generateStaticRoutes()

const fsRoutes: Route[] = [
const internalFsRoutes: Route[] = [
...this.generateFsStaticRoutes(),
{
match: route('/_next/data/:path*'),
Expand Down Expand Up @@ -756,10 +744,10 @@ export default abstract class Server {
}
},
},
...publicRoutes,
...staticFilesRoutes,
]

const fsRoutes: Route[] = [...publicRoutes, ...staticFilesRoutes]

const restrictedRedirectPaths = this.nextConfig.basePath
? [`${this.nextConfig.basePath}/_next`]
: ['/_next']
Expand All @@ -778,7 +766,10 @@ export default abstract class Server {
)

const rewrites = this.generateRewrites({ restrictedRedirectPaths })
const catchAllMiddleware = this.generateCatchAllMiddlewareRoute()
const catchAllStaticMiddleware =
this.generateCatchAllStaticMiddlewareRoute()
const catchAllDynamicMiddleware =
this.generateCatchAllDynamicMiddlewareRoute()

const catchAllRoute: Route = {
match: route('/:path*'),
Expand Down Expand Up @@ -813,7 +804,7 @@ export default abstract class Server {
}
}

if (pathname === '/api' || pathname.startsWith('/api/')) {
if (isApiRoute(pathname)) {
delete query._nextBubbleNoFallback

const handled = await this.handleApiRequest(req, res, pathname, query)
Expand Down Expand Up @@ -842,19 +833,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,
catchAllMiddleware,
catchAllStaticMiddleware,
catchAllDynamicMiddleware,
useFileSystemPublicRoutes,
dynamicRoutes: this.dynamicRoutes,
basePath: this.nextConfig.basePath,
Expand Down Expand Up @@ -931,24 +922,34 @@ 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<RoutingItem> {
const addedPages = new Set<string>()

return getSortedRoutes(
Object.keys(this.pagesManifest!).map(
(page) =>
normalizeLocalePath(page, this.nextConfig.i18n?.locales).pathname
return this.allRoutes!.filter((item) => {
if (
item.isMiddleware ||
addedPages.has(item.page) ||
!isDynamicRoute(item.page)
)
)
.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))
return false
addedPages.add(item.page)
return true
})
}

protected async run(
Expand Down Expand Up @@ -1926,6 +1927,10 @@ 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
Expand Down

0 comments on commit 53d1b00

Please sign in to comment.