Skip to content

Commit d166096

Browse files
ztannereps1lon
authored andcommitted
fix proxy matching for segment prefetch URLs (#89) (#96)
* fix: proxy should match segment prefetches for canonical urls * include .rsc handling
1 parent 9d50c0b commit d166096

2 files changed

Lines changed: 69 additions & 7 deletions

File tree

packages/next/src/build/analysis/get-page-static-info.test.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('get-page-static-infos', () => {
88
{
99
originalSource: '/middleware/path',
1010
regexp:
11-
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/path(\\.json)?[\\/#\\?]?$',
11+
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/path(\\.json|\\.rsc|\\.segments\\/.+\\.segment\\.rsc)?[\\/#\\?]?$',
1212
},
1313
]
1414
const result = getMiddlewareMatchers(matchers, { i18n: undefined })
@@ -21,25 +21,70 @@ describe('get-page-static-infos', () => {
2121
{
2222
originalSource: '/middleware/path',
2323
regexp:
24-
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/path(\\.json)?[\\/#\\?]?$',
24+
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/path(\\.json|\\.rsc|\\.segments\\/.+\\.segment\\.rsc)?[\\/#\\?]?$',
2525
},
2626
{
2727
originalSource: '/middleware/another-path',
2828
regexp:
29-
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/another-path(\\.json)?[\\/#\\?]?$',
29+
'^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/middleware\\/another-path(\\.json|\\.rsc|\\.segments\\/.+\\.segment\\.rsc)?[\\/#\\?]?$',
3030
},
3131
]
3232
const result = getMiddlewareMatchers(matchers, { i18n: undefined })
3333
expect(result).toStrictEqual(expected)
3434
})
3535

36-
it('matches /:id and /:id.json', () => {
36+
it('matches /:id and transport variants for the same route', () => {
3737
const matchers = ['/:id']
3838
const result = getMiddlewareMatchers(matchers, { i18n: undefined })[0]
3939
.regexp
4040
const regex = new RegExp(result)
4141
expect(regex.test('/apple')).toBe(true)
4242
expect(regex.test('/apple.json')).toBe(true)
43+
expect(regex.test('/apple.rsc')).toBe(true)
44+
})
45+
46+
it('matches App Router segment-prefetch routes for static matchers', () => {
47+
const regex = new RegExp(
48+
getMiddlewareMatchers('/dashboard', { i18n: undefined })[0].regexp
49+
)
50+
51+
expect(regex.test('/dashboard.rsc')).toBe(true)
52+
expect(
53+
regex.test('/dashboard.segments/$c$children/__PAGE__.segment.rsc')
54+
).toBe(true)
55+
expect(
56+
regex.test('/settings.segments/$c$children/__PAGE__.segment.rsc')
57+
).toBe(false)
58+
})
59+
60+
it('matches App Router segment-prefetch routes for nested matchers', () => {
61+
const regex = new RegExp(
62+
getMiddlewareMatchers('/dashboard/:path*', {
63+
i18n: undefined,
64+
})[0].regexp
65+
)
66+
67+
expect(
68+
regex.test(
69+
'/dashboard/settings.segments/$c$children/__PAGE__.segment.rsc'
70+
)
71+
).toBe(true)
72+
expect(
73+
regex.test(
74+
'/marketing/settings.segments/$c$children/__PAGE__.segment.rsc'
75+
)
76+
).toBe(false)
77+
})
78+
79+
it('matches the root App Router segment-prefetch transport route', () => {
80+
const regex = new RegExp(
81+
getMiddlewareMatchers('/', { i18n: undefined })[0].regexp
82+
)
83+
84+
expect(regex.test('/index.rsc')).toBe(true)
85+
expect(
86+
regex.test('/index.segments/$c$children/__PAGE__.segment.rsc')
87+
).toBe(true)
4388
})
4489
})
4590
})

packages/next/src/build/analysis/get-page-static-info.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ import {
1111
SERVER_RUNTIME,
1212
MIDDLEWARE_FILENAME,
1313
PROXY_FILENAME,
14+
RSC_SUFFIX,
15+
RSC_SEGMENT_SUFFIX,
16+
RSC_SEGMENTS_DIR_SUFFIX,
1417
} from '../../lib/constants'
1518
import { tryToParsePath } from '../../lib/try-to-parse-path'
1619
import { isAPIRoute } from '../../lib/is-api-route'
1720
import { isEdgeRuntime } from '../../lib/is-edge-runtime'
1821
import { RSC_MODULE_TYPES } from '../../shared/lib/constants'
22+
import { escapeStringRegexp } from '../../shared/lib/escape-regexp'
1923
import type { RSCMeta } from '../webpack/loaders/get-module-build-info'
2024
import { PAGE_TYPES } from '../../lib/page-types'
2125
import {
@@ -107,6 +111,13 @@ export interface PagesPageStaticInfo {
107111

108112
export type PageStaticInfo = AppPageStaticInfo | PagesPageStaticInfo
109113

114+
const APP_ROUTE_RSC_SUFFIX_MATCHER = escapeStringRegexp(RSC_SUFFIX)
115+
const APP_ROUTE_SEGMENT_PREFETCH_SUFFIX_MATCHER = `${escapeStringRegexp(RSC_SEGMENTS_DIR_SUFFIX)}/.+${escapeStringRegexp(RSC_SEGMENT_SUFFIX)}`
116+
const APP_ROUTE_TRANSPORT_SUFFIX_MATCHER = `${APP_ROUTE_RSC_SUFFIX_MATCHER}|${APP_ROUTE_SEGMENT_PREFETCH_SUFFIX_MATCHER}`
117+
const ROOT_APP_ROUTE_TRANSPORT_MATCHER = `/?index(?:${APP_ROUTE_TRANSPORT_SUFFIX_MATCHER})`
118+
const MIDDLEWARE_DATA_SUFFIX_MATCHER = `\\.json|${APP_ROUTE_TRANSPORT_SUFFIX_MATCHER}`
119+
const OPTIONAL_MIDDLEWARE_NEXT_DATA_PREFIX = '/:nextData(_next/data/[^/]{1,})?'
120+
110121
const CLIENT_MODULE_LABEL =
111122
/\/\* __next_internal_client_entry_do_not_use__ ([^ ]*) (cjs|auto) \*\//
112123

@@ -465,11 +476,17 @@ export function getMiddlewareMatchers(
465476
}`
466477
}
467478

468-
source = `/:nextData(_next/data/[^/]{1,})?${source}${
479+
// Match transport-specific route forms that resolve to the same page.
480+
// - Pages Router data routes: /_next/data/<build-id>/...
481+
// - App Router transport routes: .rsc, ...segments/...segment.rsc
482+
const sourceSuffix = `${
469483
isRoot
470-
? `(${nextConfig.i18n ? '|\\.json|' : ''}/?index|/?index\\.json)?`
471-
: '{(\\.json)}?'
484+
? `(${
485+
nextConfig.i18n ? '|\\.json|' : ''
486+
}/?index|/?index\\.json|${ROOT_APP_ROUTE_TRANSPORT_MATCHER})?`
487+
: `{(${MIDDLEWARE_DATA_SUFFIX_MATCHER})}?`
472488
}`
489+
source = `${OPTIONAL_MIDDLEWARE_NEXT_DATA_PREFIX}${source}${sourceSuffix}`
473490

474491
if (nextConfig.basePath) {
475492
source = `${nextConfig.basePath}${source}`

0 commit comments

Comments
 (0)