Skip to content

Commit

Permalink
Implement middleware support for Turbopack (#46397)
Browse files Browse the repository at this point in the history
This implements middleware support for Turbopack's route resolver. In
vercel/turborepo#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 vercel/turborepo#3930 to
land first~~ Merged.
Fixes https://linear.app/vercel/issue/WEB-624

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
jridgewell and ijjk authored Mar 1, 2023
1 parent b0b5cd8 commit 5306df8
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/next/src/build/analysis/get-page-static-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ async function tryToReadFile(filePath: string, shouldThrow: boolean) {
}
}

function getMiddlewareMatchers(
export function getMiddlewareMatchers(
matcherOrMatchers: unknown,
nextConfig: NextConfig
): MiddlewareMatcher[] {
Expand Down
69 changes: 64 additions & 5 deletions packages/next/src/server/lib/route-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 } =
Expand All @@ -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()

// @ts-expect-error
devServer.customRoutes = await loadCustomRoutes(nextConfig)
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
Expand All @@ -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',
Expand Down Expand Up @@ -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
})
Expand All @@ -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')
Expand Down

0 comments on commit 5306df8

Please sign in to comment.