Skip to content

Commit bc74b4b

Browse files
committed
chore: handle dynamic isr routes
1 parent 03fa2d0 commit bc74b4b

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

packages/runtime/src/helpers/redirects.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,10 @@ const generateDynamicRewrites = ({
233233
route: route.page,
234234
buildId,
235235
basePath,
236-
to: HANDLER_FUNCTION_PATH,
236+
to: ODB_FUNCTION_PATH,
237237
i18n,
238-
withData: false,
238+
dataRoute: prerenderedDynamicRoutes[route.page].dataRoute,
239+
withData: true,
239240
}),
240241
)
241242
} else if (prerenderedDynamicRoutes[route.page].fallback === false && !is404Isr) {

packages/runtime/src/helpers/utils.ts

+7
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ export const toNetlifyRoute = (nextRoute: string): Array<string> => {
5252
netlifyRoutes.unshift(netlifyRoute)
5353
}
5454

55+
// RSC data routes get an extra route with `/index.rsc` so that the router
56+
// can match a different URL with and without a trailing slash
57+
if (nextRoute.endsWith('.rsc')) {
58+
const rscIndexRoute = nextRoute.replace(/\.rsc$/, '/index.rsc')
59+
netlifyRoutes.push(rscIndexRoute)
60+
}
61+
5562
return netlifyRoutes.map((route) =>
5663
route
5764
// Replace catch-all, e.g., [...slug]
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,74 @@
11
import type { EdgeFunction } from 'https://edge.netlify.com'
22

3-
interface PrerenderedRoute {
3+
export declare type SsgRoute = {
44
initialRevalidateSeconds: number | false
55
srcRoute: string | null
6-
dataRoute: string | null
6+
dataRoute: string
7+
}
8+
export declare type DynamicSsgRoute = {
9+
routeRegex: string
10+
fallback: string | null | false
11+
dataRoute: string
12+
dataRouteRegex: string
13+
}
14+
export declare type PrerenderManifest = {
15+
version: 3
16+
routes: {
17+
[route: string]: SsgRoute
18+
}
19+
dynamicRoutes: {
20+
[route: string]: DynamicSsgRoute
21+
}
22+
notFoundRoutes: string[]
723
}
824

9-
export const getRscDataRouter = (prerenderedRoutes: Record<string, PrerenderedRoute>): EdgeFunction => {
10-
const routeEntries: Array<[string, PrerenderedRoute]> = Object.entries(prerenderedRoutes)
11-
const routes = new Map(routeEntries)
25+
const noop = () => {}
1226

13-
const rscDataRoutes = new Set(
14-
routeEntries.map(([, { dataRoute }]) => dataRoute).filter((dataRoute) => dataRoute?.endsWith('.rsc')),
27+
// Ensure that routes with and without a trailing slash map to different ODB paths
28+
const rscifyPath = (route: string) => {
29+
if (route.endsWith('/')) {
30+
return route + 'index.rsc'
31+
}
32+
return route + '.rsc'
33+
}
34+
35+
export const getRscDataRouter = ({ routes: staticRoutes, dynamicRoutes }: PrerenderManifest): EdgeFunction => {
36+
const staticRouteSet = new Set(
37+
Object.entries(staticRoutes)
38+
.filter(([, { dataRoute }]) => dataRoute.endsWith('.rsc'))
39+
.map(([route]) => route),
1540
)
1641

42+
const dynamicRouteMatcher = Object.values(dynamicRoutes)
43+
.filter(({ dataRoute }) => dataRoute.endsWith('.rsc'))
44+
.map(({ dataRouteRegex }) => new RegExp(dataRouteRegex))
45+
46+
const matchesDynamicRscDataRoute = (pathname: string) => {
47+
return dynamicRouteMatcher.some((matcher) => matcher.test(pathname))
48+
}
49+
50+
const matchesStaticRscDataRoute = (pathname: string) => {
51+
const key = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname
52+
return staticRouteSet.has(key)
53+
}
54+
55+
const matchesRscRoute = (pathname: string) => {
56+
return matchesStaticRscDataRoute(pathname) || matchesDynamicRscDataRoute(pathname)
57+
}
58+
1759
return (request, context) => {
1860
const debug = request.headers.has('x-next-debug-logging')
19-
const log = (...args: unknown[]) => {
20-
if (debug) {
21-
console.log(...args)
22-
}
23-
}
61+
const log = debug ? (...args: unknown[]) => console.log(...args) : noop
2462
const url = new URL(request.url)
25-
// Manifest always has the route without a trailing slash
26-
const pathname = url.pathname.endsWith('/') ? url.pathname.slice(0, -1) : url.pathname
2763
// If this is a static RSC request, rewrite to the data route
2864
if (request.headers.get('rsc') === '1') {
2965
log('Is rsc request')
30-
const route = routes.get(pathname)
31-
if (route?.dataRoute) {
66+
if (matchesRscRoute(url.pathname)) {
3267
request.headers.set('x-rsc-route', url.pathname)
33-
return context.rewrite(new URL(route.dataRoute, request.url))
68+
const target = rscifyPath(url.pathname)
69+
log('Rewriting to', target)
70+
return context.rewrite(target)
3471
}
35-
} else if (rscDataRoutes.has(pathname)) {
36-
log('Is rsc data request. Adding rsc header')
37-
request.headers.set('rsc', '1')
3872
}
3973
}
4074
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import prerenderManifest from '../edge-shared/prerender-manifest.json' assert { type: 'json' }
2-
import { getRscDataRouter } from '../edge-shared/rsc-data.ts'
2+
import { getRscDataRouter, PrerenderManifest } from '../edge-shared/rsc-data.ts'
33

4-
const handler = getRscDataRouter(prerenderManifest.routes)
4+
const handler = getRscDataRouter(prerenderManifest as PrerenderManifest)
55
export default handler

0 commit comments

Comments
 (0)