Implement middleware support for Turbopack (#46397)
This implements middleware support for Turbopack's route resolver. In https://github.com/vercel/turbo/pull/3930, I'm updating the data that we pass to include a new `MiddlewareConfig`, which includes the files needed for invoking the edge function and the matchers extracted from the middleware's static `export config = {}`. ~~This needs to wait for https://github.com/vercel/turbo/pull/3930 to land first~~ Merged. Fixes https://linear.app/vercel/issue/WEB-624 --------- Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
parent
b0b5cd8dc0
commit
5306df81c6
2 changed files with 65 additions and 6 deletions
|
@ -140,7 +140,7 @@ async function tryToReadFile(filePath: string, shouldThrow: boolean) {
|
|||
}
|
||||
}
|
||||
|
||||
function getMiddlewareMatchers(
|
||||
export function getMiddlewareMatchers(
|
||||
matcherOrMatchers: unknown,
|
||||
nextConfig: NextConfig
|
||||
): MiddlewareMatcher[] {
|
||||
|
|
|
@ -5,7 +5,14 @@ import { RouteKind } from '../future/route-kind'
|
|||
import { DefaultRouteMatcherManager } from '../future/route-matcher-managers/default-route-matcher-manager'
|
||||
import { RouteMatch } from '../future/route-matches/route-match'
|
||||
import type { PageChecker, Route } from '../router'
|
||||
import { getMiddlewareMatchers } from '../../build/analysis/get-page-static-info'
|
||||
import { getMiddlewareRouteMatcher } from '../../shared/lib/router/utils/middleware-route-matcher'
|
||||
import { join } from 'path'
|
||||
|
||||
type MiddlewareConfig = {
|
||||
matcher: string[]
|
||||
files: string[]
|
||||
}
|
||||
type RouteResult =
|
||||
| {
|
||||
type: 'rewrite'
|
||||
|
@ -48,7 +55,11 @@ class DevRouteMatcherManager extends DefaultRouteMatcherManager {
|
|||
}
|
||||
}
|
||||
|
||||
export async function makeResolver(dir: string, nextConfig: NextConfig) {
|
||||
export async function makeResolver(
|
||||
dir: string,
|
||||
nextConfig: NextConfig,
|
||||
middleware: MiddlewareConfig
|
||||
) {
|
||||
const url = require('url') as typeof import('url')
|
||||
const { default: Router } = require('../router') as typeof import('../router')
|
||||
const { getPathMatch } =
|
||||
|
@ -65,14 +76,57 @@ export async function makeResolver(dir: string, nextConfig: NextConfig) {
|
|||
const devServer = new DevServer({
|
||||
dir,
|
||||
conf: nextConfig,
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
})
|
||||
|
||||
await devServer.matchers.reload()
|
||||
|
||||
if (middleware.files?.length) {
|
||||
// @ts-expect-error
|
||||
devServer.customRoutes = await loadCustomRoutes(nextConfig)
|
||||
|
||||
const matchers = middleware.matcher
|
||||
? getMiddlewareMatchers(middleware.matcher, nextConfig)
|
||||
: [{ regexp: '.*' }]
|
||||
// @ts-expect-error
|
||||
devServer.middleware = {
|
||||
page: '/',
|
||||
match: getMiddlewareRouteMatcher(matchers),
|
||||
matchers,
|
||||
}
|
||||
|
||||
type GetEdgeFunctionInfo =
|
||||
typeof DevServer['prototype']['getEdgeFunctionInfo']
|
||||
const getEdgeFunctionInfo = (
|
||||
original: GetEdgeFunctionInfo
|
||||
): GetEdgeFunctionInfo => {
|
||||
return (params: { page: string; middleware: boolean }) => {
|
||||
if (params.middleware) {
|
||||
return {
|
||||
name: 'middleware',
|
||||
paths: middleware.files.map((file) => join(process.cwd(), file)),
|
||||
env: [],
|
||||
wasm: [],
|
||||
assets: [],
|
||||
}
|
||||
}
|
||||
return original(params)
|
||||
}
|
||||
}
|
||||
// @ts-expect-error protected
|
||||
devServer.getEdgeFunctionInfo = getEdgeFunctionInfo(
|
||||
// @ts-expect-error protected
|
||||
devServer.getEdgeFunctionInfo.bind(devServer)
|
||||
)
|
||||
// @ts-expect-error protected
|
||||
devServer.hasMiddleware = () => true
|
||||
}
|
||||
|
||||
const routeResults = new WeakMap<any, string>()
|
||||
const routes = devServer.generateRoutes.bind(devServer)()
|
||||
const routes = devServer.generateRoutes()
|
||||
// @ts-expect-error protected
|
||||
const catchAllMiddleware = devServer.generateCatchAllMiddlewareRoute(true)
|
||||
|
||||
routes.matchers = new DevRouteMatcherManager(
|
||||
// @ts-expect-error internal method
|
||||
|
@ -81,6 +135,7 @@ export async function makeResolver(dir: string, nextConfig: NextConfig) {
|
|||
|
||||
const router = new Router({
|
||||
...routes,
|
||||
catchAllMiddleware,
|
||||
catchAllRoute: {
|
||||
match: getPathMatch('/:path*'),
|
||||
name: 'catchall route',
|
||||
|
@ -112,6 +167,7 @@ export async function makeResolver(dir: string, nextConfig: NextConfig) {
|
|||
route.type === 'redirect' ||
|
||||
route.type === 'header' ||
|
||||
route.name === 'catchall route' ||
|
||||
route.name === 'middleware catchall' ||
|
||||
route.name?.includes('check')
|
||||
return matches
|
||||
})
|
||||
|
@ -122,9 +178,12 @@ export async function makeResolver(dir: string, nextConfig: NextConfig) {
|
|||
) {
|
||||
const req = new NodeNextRequest(_req)
|
||||
const res = new NodeNextResponse(_res)
|
||||
const parsedUrl = url.parse(req.url!, true)
|
||||
// @ts-expect-error protected
|
||||
devServer.attachRequestMeta(req, parsedUrl)
|
||||
;(req as any)._initUrl = req.url
|
||||
|
||||
await router.execute.bind(router)(req, res, url.parse(req.url!, true))
|
||||
await router.execute(req, res, parsedUrl)
|
||||
|
||||
if (!res.originalResponse.headersSent) {
|
||||
res.setHeader('x-nextjs-route-result', '1')
|
||||
|
|
Loading…
Reference in a new issue