Update search params/route params handling on deploy (#47930)

This ensures we prefix the dynamic route params in the query so that
they can be kept separate from actual query params from the initial
request.

Fixes: https://github.com/vercel/next.js/issues/43139
This commit is contained in:
JJ Kasper 2023-04-05 14:14:40 -07:00 committed by GitHub
parent 1fbbba6ffe
commit e3e22f5bed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 231 additions and 164 deletions

View file

@ -190,7 +190,7 @@ async function generateClientSsgManifest(
}
function pageToRoute(page: string) {
const routeRegex = getNamedRouteRegex(page)
const routeRegex = getNamedRouteRegex(page, true)
return {
page,
regex: normalizeRouteRegex(routeRegex.re.source),
@ -1975,7 +1975,8 @@ export default async function build(
if (isDynamicRoute(page)) {
const routeRegex = getNamedRouteRegex(
dataRoute.replace(/\.json$/, '')
dataRoute.replace(/\.json$/, ''),
true
)
dataRouteRegex = normalizeRouteRegex(
@ -2392,7 +2393,7 @@ export default async function build(
// dynamicParams for non-static paths?
finalDynamicRoutes[page] = {
routeRegex: normalizeRouteRegex(
getNamedRouteRegex(page).re.source
getNamedRouteRegex(page, false).re.source
),
dataRoute,
// if dynamicParams are enabled treat as fallback:
@ -2404,7 +2405,8 @@ export default async function build(
? null
: normalizeRouteRegex(
getNamedRouteRegex(
dataRoute.replace(/\.rsc$/, '')
dataRoute.replace(/\.rsc$/, ''),
false
).re.source.replace(/\(\?:\\\/\)\?\$$/, '\\.rsc$')
),
}
@ -2769,7 +2771,7 @@ export default async function build(
finalDynamicRoutes[tbdRoute] = {
routeRegex: normalizeRouteRegex(
getNamedRouteRegex(tbdRoute).re.source
getNamedRouteRegex(tbdRoute, false).re.source
),
dataRoute,
fallback: ssgBlockingFallbackPages.has(tbdRoute)
@ -2779,7 +2781,8 @@ export default async function build(
: false,
dataRouteRegex: normalizeRouteRegex(
getNamedRouteRegex(
dataRoute.replace(/\.json$/, '')
dataRoute.replace(/\.json$/, ''),
false
).re.source.replace(/\(\?:\\\/\)\?\$$/, '\\.json$')
),
}

View file

@ -59,7 +59,7 @@ async function nextMetadataImageLoader(this: any, content: Buffer) {
const exportedImageData = { ...exported }
export default (props) => {
const pathname = ${JSON.stringify(route)}
const routeRegex = getNamedRouteRegex(pathname)
const routeRegex = getNamedRouteRegex(pathname, false)
const route = interpolateDynamicPath(pathname, props.params, routeRegex)
const imageData = {
@ -111,7 +111,7 @@ async function nextMetadataImageLoader(this: any, content: Buffer) {
export default (props) => {
const pathname = ${JSON.stringify(route)}
const routeRegex = getNamedRouteRegex(pathname)
const routeRegex = getNamedRouteRegex(pathname, false)
const route = interpolateDynamicPath(pathname, props.params, routeRegex)
const imageData = ${JSON.stringify(imageData)};

View file

@ -1,5 +1,7 @@
import type { ServerRuntime } from '../../types'
export const NEXT_QUERY_PARAM_PREFIX = 'nextParam'
// in seconds
export const CACHE_ONE_YEAR = 31536000

View file

@ -101,6 +101,7 @@ import { RouteKind } from './future/route-kind'
import { handleInternalServerErrorResponse } from './future/route-modules/helpers/response-handlers'
import { parseNextReferrerFromHeaders } from './lib/parse-next-referrer'
import { fromNodeHeaders, toNodeHeaders } from './web/utils'
import { NEXT_QUERY_PARAM_PREFIX } from '../lib/constants'
export type FindComponentsResult = {
components: LoadComponentsReturnType
@ -783,6 +784,24 @@ export default abstract class Server<ServerOptions extends Options = Options> {
addRequestMeta(req, '_nextRewroteUrl', parsedUrl.pathname!)
addRequestMeta(req, '_nextDidRewrite', true)
}
const routeParamKeys = new Set<string>()
for (const key of Object.keys(parsedUrl.query)) {
const value = parsedUrl.query[key]
if (
key !== NEXT_QUERY_PARAM_PREFIX &&
key.startsWith(NEXT_QUERY_PARAM_PREFIX)
) {
const normalizedKey = key.substring(
NEXT_QUERY_PARAM_PREFIX.length
)
parsedUrl.query[normalizedKey] = value
routeParamKeys.add(normalizedKey)
delete parsedUrl.query[key]
}
}
// interpolate dynamic params and normalize URL if needed
if (pageIsDynamic) {
@ -861,7 +880,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {
matchedPath = utils.interpolateDynamicPath(srcPathname, params)
req.url = utils.interpolateDynamicPath(req.url!, params)
}
Object.assign(parsedUrl.query, params)
}
if (pageIsDynamic || didRewrite) {
@ -870,6 +888,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
...Object.keys(utils.defaultRouteRegex?.groups || {}),
])
}
for (const key of routeParamKeys) {
delete parsedUrl.query[key]
}
parsedUrl.pathname = `${this.nextConfig.basePath || ''}${
matchedPath === '/' && this.nextConfig.basePath ? '' : matchedPath
}`

View file

@ -23,6 +23,7 @@ import { TEMPORARY_REDIRECT_STATUS } from '../shared/lib/constants'
import { addRequestMeta } from './request-meta'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import { normalizeRscPath } from '../shared/lib/router/utils/app-paths'
import { NEXT_QUERY_PARAM_PREFIX } from '../lib/constants'
export function normalizeVercelUrl(
req: BaseNextRequest | IncomingMessage,
@ -37,8 +38,14 @@ export function normalizeVercelUrl(
const _parsedUrl = parseUrl(req.url!, true)
delete (_parsedUrl as any).search
for (const param of paramKeys || Object.keys(defaultRouteRegex.groups)) {
delete _parsedUrl.query[param]
for (const key of Object.keys(_parsedUrl.query)) {
if (
(key !== NEXT_QUERY_PARAM_PREFIX &&
key.startsWith(NEXT_QUERY_PARAM_PREFIX)) ||
(paramKeys || Object.keys(defaultRouteRegex.groups)).includes(key)
) {
delete _parsedUrl.query[key]
}
}
req.url = formatUrl(_parsedUrl)
}
@ -107,7 +114,7 @@ export function getUtils({
let defaultRouteMatches: ParsedUrlQuery | undefined
if (pageIsDynamic) {
defaultRouteRegex = getNamedRouteRegex(page)
defaultRouteRegex = getNamedRouteRegex(page, false)
dynamicRouteMatcher = getRouteMatcher(defaultRouteRegex)
defaultRouteMatches = dynamicRouteMatcher(page) as ParsedUrlQuery
}

View file

@ -290,7 +290,7 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
pathname = this.serverOptions.webServerConfig.page
if (isDynamicRoute(pathname)) {
const routeRegex = getNamedRouteRegex(pathname)
const routeRegex = getNamedRouteRegex(pathname, false)
pathname = interpolateDynamicPath(pathname, query, routeRegex)
normalizeVercelUrl(
req,

View file

@ -1,6 +1,8 @@
import { escapeStringRegexp } from '../../escape-regexp'
import { removeTrailingSlash } from './remove-trailing-slash'
const NEXT_QUERY_PARAM_PREFIX = 'nextParam'
export interface Group {
pos: number
repeat: boolean
@ -88,7 +90,7 @@ function buildGetSafeRouteKey() {
}
}
function getNamedParametrizedRoute(route: string) {
function getNamedParametrizedRoute(route: string, prefixRouteKeys: boolean) {
const segments = removeTrailingSlash(route).slice(1).split('/')
const getSafeRouteKey = buildGetSafeRouteKey()
const routeKeys: { [named: string]: string } = {}
@ -115,7 +117,13 @@ function getNamedParametrizedRoute(route: string) {
cleanedKey = getSafeRouteKey()
}
routeKeys[cleanedKey] = key
if (prefixRouteKeys) {
cleanedKey = `${NEXT_QUERY_PARAM_PREFIX}${cleanedKey}`
routeKeys[cleanedKey] = `${NEXT_QUERY_PARAM_PREFIX}${key}`
} else {
routeKeys[cleanedKey] = `${key}`
}
return repeat
? optional
? `(?:/(?<${cleanedKey}>.+?))?`
@ -133,10 +141,16 @@ function getNamedParametrizedRoute(route: string) {
/**
* This function extends `getRouteRegex` generating also a named regexp where
* each group is named along with a routeKeys object that indexes the assigned
* named group with its corresponding key.
* named group with its corresponding key. When the routeKeys need to be
* prefixed to uniquely identify internally the "prefixRouteKey" arg should
* be "true" currently this is only the case when creating the routes-manifest
* during the build
*/
export function getNamedRouteRegex(normalizedRoute: string) {
const result = getNamedParametrizedRoute(normalizedRoute)
export function getNamedRouteRegex(
normalizedRoute: string,
prefixRouteKey: boolean
) {
const result = getNamedParametrizedRoute(normalizedRoute, prefixRouteKey)
return {
...getRouteRegex(normalizedRoute),
namedRegex: `^${result.namedParameterizedRoute}(?:/)?$`,
@ -163,7 +177,10 @@ export function getNamedMiddlewareRegex(
}
}
const { namedParameterizedRoute } = getNamedParametrizedRoute(normalizedRoute)
const { namedParameterizedRoute } = getNamedParametrizedRoute(
normalizedRoute,
false
)
let catchAllGroupedRegex = catchAll ? '(?:(/.*)?)' : ''
return {
namedRegex: `^${namedParameterizedRoute}${catchAllGroupedRegex}$`,

View file

@ -1,5 +1,7 @@
'use client'
import { useSearchParams } from 'next/navigation'
export default function IdPage({ children, params }) {
return (
<>
@ -8,6 +10,10 @@ export default function IdPage({ children, params }) {
<span id="id-page-params">{JSON.stringify(params)}</span>
</p>
{children}
<p id="search-params">
{JSON.stringify(Object.fromEntries(useSearchParams()))}
</p>
</>
)
}

View file

@ -1,4 +1,4 @@
export default function IdPage({ children, params }) {
export default function IdPage({ children, params, searchParams }) {
return (
<>
<p>
@ -6,6 +6,8 @@ export default function IdPage({ children, params }) {
<span id="id-page-params">{JSON.stringify(params)}</span>
</p>
{children}
<p id="search-params">{JSON.stringify(searchParams)}</p>
</>
)
}

View file

@ -10,6 +10,35 @@ createNextDescribe(
files: __dirname,
},
({ next, isNextDev: isDev, isNextStart, isNextDeploy }) => {
it('should have correct searchParams and params (server)', async () => {
const html = await next.render('/dynamic/category-1/id-2?query1=value2')
const $ = cheerio.load(html)
expect(JSON.parse($('#id-page-params').text())).toEqual({
category: 'category-1',
id: 'id-2',
})
expect(JSON.parse($('#search-params').text())).toEqual({
query1: 'value2',
})
})
it('should have correct searchParams and params (client)', async () => {
const browser = await next.browser(
'/dynamic-client/category-1/id-2?query1=value2'
)
const html = await browser.eval('document.documentElement.innerHTML')
const $ = cheerio.load(html)
expect(JSON.parse($('#id-page-params').text())).toEqual({
category: 'category-1',
id: 'id-2',
})
expect(JSON.parse($('#search-params').text())).toEqual({
query1: 'value2',
})
})
if (!isDev) {
it('should successfully detect app route during prefetch', async () => {
const browser = await next.browser('/')

View file

@ -5,6 +5,8 @@ createNextDescribe(
'parallel-routes-and-interception',
{
files: __dirname,
// TODO: remove after deployment handling is updated
skipDeployment: true,
},
({ next }) => {
describe('parallel routes', () => {

View file

@ -1,24 +0,0 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

View file

@ -140,10 +140,10 @@ describe('edge-render-getserversideprops', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeStringRegexp(
next.buildId
)}/(?<id>[^/]+?)\\.json$`,
)}/(?<nextParamid>[^/]+?)\\.json$`,
page: '/[id]',
routeKeys: {
id: 'id',
nextParamid: 'nextParamid',
},
},
])

View file

@ -43,19 +43,19 @@ const expectedManifestRoutes = () => [
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)\\.json$`,
)}/blog/(?<nextParampost>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog\\/([^\\/]+?)\\.json$`
),
page: '/blog/[post]',
routeKeys: {
post: 'post',
nextParampost: 'nextParampost',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)/(?<comment>[^/]+?)\\.json$`,
)}/blog/(?<nextParampost>[^/]+?)/(?<nextParamcomment>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
@ -63,20 +63,20 @@ const expectedManifestRoutes = () => [
),
page: '/blog/[post]/[comment]',
routeKeys: {
post: 'post',
comment: 'comment',
nextParampost: 'nextParampost',
nextParamcomment: 'nextParamcomment',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/catchall/(?<path>.+?)\\.json$`,
)}/catchall/(?<nextParampath>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/catchall\\/(.+?)\\.json$`
),
page: '/catchall/[...path]',
routeKeys: {
path: 'path',
nextParampath: 'nextParampath',
},
},
{
@ -127,10 +127,10 @@ const expectedManifestRoutes = () => [
)}\\/not\\-found\\/([^\\/]+?)\\.json$`,
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/not\\-found/(?<slug>[^/]+?)\\.json$`,
)}/not\\-found/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/not-found/[slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
@ -182,7 +182,7 @@ const expectedManifestRoutes = () => [
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/user/(?<user>[^/]+?)/profile\\.json$`,
)}/user/(?<nextParamuser>[^/]+?)/profile\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
@ -190,7 +190,7 @@ const expectedManifestRoutes = () => [
),
page: '/user/[user]/profile',
routeKeys: {
user: 'user',
nextParamuser: 'nextParamuser',
},
},
]

View file

@ -1316,10 +1316,10 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/api\\-docs/(?<slug>.+?)\\.json$`,
)}/api\\-docs/(?<nextParamslug>.+?)\\.json$`,
page: '/api-docs/[...slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
@ -1346,9 +1346,9 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/blocking\\-fallback/(?<slug>[^/]+?)\\.json$`,
)}/blocking\\-fallback/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/blocking-fallback/[slug]',
routeKeys: { slug: 'slug' },
routeKeys: { nextParamslug: 'nextParamslug' },
},
{
dataRouteRegex: normalizeRegEx(
@ -1358,9 +1358,9 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/blocking\\-fallback\\-once/(?<slug>[^/]+?)\\.json$`,
)}/blocking\\-fallback\\-once/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/blocking-fallback-once/[slug]',
routeKeys: { slug: 'slug' },
routeKeys: { nextParamslug: 'nextParamslug' },
},
{
dataRouteRegex: normalizeRegEx(
@ -1370,9 +1370,9 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/blocking\\-fallback\\-some/(?<slug>[^/]+?)\\.json$`,
)}/blocking\\-fallback\\-some/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/blocking-fallback-some/[slug]',
routeKeys: { slug: 'slug' },
routeKeys: { nextParamslug: 'nextParamslug' },
},
{
dataRouteRegex: normalizeRegEx(
@ -1383,7 +1383,7 @@ describe('Prerender', () => {
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/blog/(?<post>[^/]+?)\\.json$`,
)}/blog/(?<nextParampost>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1391,13 +1391,13 @@ describe('Prerender', () => {
),
page: '/blog/[post]',
routeKeys: {
post: 'post',
nextParampost: 'nextParampost',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/blog/(?<post>[^/]+?)/(?<comment>[^/]+?)\\.json$`,
)}/blog/(?<nextParampost>[^/]+?)/(?<nextParamcomment>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1405,14 +1405,14 @@ describe('Prerender', () => {
),
page: '/blog/[post]/[comment]',
routeKeys: {
post: 'post',
comment: 'comment',
nextParampost: 'nextParampost',
nextParamcomment: 'nextParamcomment',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/catchall/(?<slug>.+?)\\.json$`,
)}/catchall/(?<nextParamslug>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1420,13 +1420,13 @@ describe('Prerender', () => {
),
page: '/catchall/[...slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/catchall\\-explicit/(?<slug>.+?)\\.json$`,
)}/catchall\\-explicit/(?<nextParamslug>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1434,13 +1434,13 @@ describe('Prerender', () => {
),
page: '/catchall-explicit/[...slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/catchall\\-optional(?:/(?<slug>.+?))?\\.json$`,
)}/catchall\\-optional(?:/(?<nextParamslug>.+?))?\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1448,7 +1448,7 @@ describe('Prerender', () => {
),
page: '/catchall-optional/[[...slug]]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
@ -1467,10 +1467,10 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/dynamic/(?<slug>[^/]+?)\\.json$`,
)}/dynamic/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/dynamic/[slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
@ -1481,10 +1481,10 @@ describe('Prerender', () => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/fallback\\-only/(?<slug>[^/]+?)\\.json$`,
)}/fallback\\-only/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/fallback-only/[slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
// TODO: investigate index/index
@ -1499,7 +1499,7 @@ describe('Prerender', () => {
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/lang/(?<lang>[^/]+?)/about\\.json$`,
)}/lang/(?<nextParamlang>[^/]+?)/about\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1507,7 +1507,7 @@ describe('Prerender', () => {
),
page: '/lang/[lang]/about',
routeKeys: {
lang: 'lang',
nextParamlang: 'nextParamlang',
},
},
{
@ -1525,7 +1525,7 @@ describe('Prerender', () => {
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/non\\-json/(?<p>[^/]+?)\\.json$`,
)}/non\\-json/(?<nextParamp>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1533,13 +1533,13 @@ describe('Prerender', () => {
),
page: '/non-json/[p]',
routeKeys: {
p: 'p',
nextParamp: 'nextParamp',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/non\\-json\\-blocking/(?<p>[^/]+?)\\.json$`,
)}/non\\-json\\-blocking/(?<nextParamp>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1547,7 +1547,7 @@ describe('Prerender', () => {
),
page: '/non-json-blocking/[p]',
routeKeys: {
p: 'p',
nextParamp: 'nextParamp',
},
},
{
@ -1575,7 +1575,7 @@ describe('Prerender', () => {
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
next.buildId
)}/user/(?<user>[^/]+?)/profile\\.json$`,
)}/user/(?<nextParamuser>[^/]+?)/profile\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
next.buildId
@ -1583,7 +1583,7 @@ describe('Prerender', () => {
),
page: '/user/[user]/profile',
routeKeys: {
user: 'user',
nextParamuser: 'nextParamuser',
},
},
])

View file

@ -1544,10 +1544,10 @@ const runTests = (isDev = false, isTurbo = false) => {
),
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog\\-catchall/(?<slug>.+?)\\.json$`,
)}/blog\\-catchall/(?<nextParamslug>.+?)\\.json$`,
page: '/blog-catchall/[...slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
@ -1556,10 +1556,10 @@ const runTests = (isDev = false, isTurbo = false) => {
)}\\/overridden\\/([^\\/]+?)\\.json$`,
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/overridden/(?<slug>[^/]+?)\\.json$`,
)}/overridden/(?<nextParamslug>[^/]+?)\\.json$`,
page: '/overridden/[slug]',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
],
@ -2464,67 +2464,67 @@ const runTests = (isDev = false, isTurbo = false) => {
},
dynamicRoutes: [
{
namedRegex: '^/_sport/(?<slug>[^/]+?)(?:/)?$',
namedRegex: '^/_sport/(?<nextParamslug>[^/]+?)(?:/)?$',
page: '/_sport/[slug]',
regex: normalizeRegEx('^\\/_sport\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: '^/_sport/(?<slug>[^/]+?)/test(?:/)?$',
namedRegex: '^/_sport/(?<nextParamslug>[^/]+?)/test(?:/)?$',
page: '/_sport/[slug]/test',
regex: normalizeRegEx('^\\/_sport\\/([^\\/]+?)\\/test(?:\\/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: '^/another/(?<id>[^/]+?)(?:/)?$',
namedRegex: '^/another/(?<nextParamid>[^/]+?)(?:/)?$',
page: '/another/[id]',
regex: normalizeRegEx('^\\/another\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
id: 'id',
nextParamid: 'nextParamid',
},
},
{
namedRegex: '^/api/dynamic/(?<slug>[^/]+?)(?:/)?$',
namedRegex: '^/api/dynamic/(?<nextParamslug>[^/]+?)(?:/)?$',
page: '/api/dynamic/[slug]',
regex: normalizeRegEx('^\\/api\\/dynamic\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: '^/auto\\-export/(?<slug>[^/]+?)(?:/)?$',
namedRegex: '^/auto\\-export/(?<nextParamslug>[^/]+?)(?:/)?$',
page: '/auto-export/[slug]',
regex: normalizeRegEx('^\\/auto\\-export\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: '^/blog/(?<post>[^/]+?)(?:/)?$',
namedRegex: '^/blog/(?<nextParampost>[^/]+?)(?:/)?$',
page: '/blog/[post]',
regex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
post: 'post',
nextParampost: 'nextParampost',
},
},
{
namedRegex: '^/blog\\-catchall/(?<slug>.+?)(?:/)?$',
namedRegex: '^/blog\\-catchall/(?<nextParamslug>.+?)(?:/)?$',
page: '/blog-catchall/[...slug]',
regex: normalizeRegEx('^\\/blog\\-catchall\\/(.+?)(?:\\/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: '^/overridden/(?<slug>[^/]+?)(?:/)?$',
namedRegex: '^/overridden/(?<nextParamslug>[^/]+?)(?:/)?$',
page: '/overridden/[slug]',
regex: '^\\/overridden\\/([^\\/]+?)(?:\\/)?$',
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
],

View file

@ -1257,10 +1257,10 @@ function runTests({ dev }) {
)}\\/b\\/([^\\/]+?)\\.json$`,
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/b/(?<a>[^/]+?)\\.json$`,
)}/b/(?<nextParama>[^/]+?)\\.json$`,
page: '/b/[123]',
routeKeys: {
a: '123',
nextParama: 'nextParam123',
},
},
{
@ -1269,16 +1269,17 @@ function runTests({ dev }) {
)}\\/c\\/([^\\/]+?)\\.json$`,
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/c/(?<a>[^/]+?)\\.json$`,
)}/c/(?<nextParama>[^/]+?)\\.json$`,
page: '/c/[alongparamnameshouldbeallowedeventhoughweird]',
routeKeys: {
a: 'alongparamnameshouldbeallowedeventhoughweird',
nextParama:
'nextParamalongparamnameshouldbeallowedeventhoughweird',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/all\\-ssg/(?<rest>.+?)\\.json$`,
)}/p1/p2/all\\-ssg/(?<nextParamrest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
@ -1286,13 +1287,13 @@ function runTests({ dev }) {
),
page: '/p1/p2/all-ssg/[...rest]',
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/nested\\-all\\-ssg/(?<rest>.+?)\\.json$`,
)}/p1/p2/nested\\-all\\-ssg/(?<nextParamrest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
@ -1300,13 +1301,13 @@ function runTests({ dev }) {
),
page: '/p1/p2/nested-all-ssg/[...rest]',
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/predefined\\-ssg/(?<rest>.+?)\\.json$`,
)}/p1/p2/predefined\\-ssg/(?<nextParamrest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
@ -1314,147 +1315,148 @@ function runTests({ dev }) {
),
page: '/p1/p2/predefined-ssg/[...rest]',
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
],
dynamicRoutes: [
{
namedRegex: '^/b/(?<a>[^/]+?)(?:/)?$',
namedRegex: '^/b/(?<nextParama>[^/]+?)(?:/)?$',
page: '/b/[123]',
regex: normalizeRegEx('^\\/b\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
a: '123',
nextParama: 'nextParam123',
},
},
{
namedRegex: `^/blog/(?<name>[^/]+?)/comment/(?<id>[^/]+?)(?:/)?$`,
namedRegex: `^/blog/(?<nextParamname>[^/]+?)/comment/(?<nextParamid>[^/]+?)(?:/)?$`,
page: '/blog/[name]/comment/[id]',
regex: normalizeRegEx(
'^\\/blog\\/([^\\/]+?)\\/comment\\/([^\\/]+?)(?:\\/)?$'
),
routeKeys: {
name: 'name',
id: 'id',
nextParamname: 'nextParamname',
nextParamid: 'nextParamid',
},
},
{
namedRegex: '^/c/(?<a>[^/]+?)(?:/)?$',
namedRegex: '^/c/(?<nextParama>[^/]+?)(?:/)?$',
page: '/c/[alongparamnameshouldbeallowedeventhoughweird]',
regex: normalizeRegEx('^\\/c\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
a: 'alongparamnameshouldbeallowedeventhoughweird',
nextParama:
'nextParamalongparamnameshouldbeallowedeventhoughweird',
},
},
{
namedRegex: '^/catchall\\-dash/(?<helloworld>.+?)(?:/)?$',
namedRegex: '^/catchall\\-dash/(?<nextParamhelloworld>.+?)(?:/)?$',
page: '/catchall-dash/[...hello-world]',
regex: normalizeRegEx('^\\/catchall\\-dash\\/(.+?)(?:\\/)?$'),
routeKeys: {
helloworld: 'hello-world',
nextParamhelloworld: 'nextParamhello-world',
},
},
{
namedRegex: '^/d/(?<id>[^/]+?)(?:/)?$',
namedRegex: '^/d/(?<nextParamid>[^/]+?)(?:/)?$',
page: '/d/[id]',
regex: normalizeRegEx('^\\/d\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
id: 'id',
nextParamid: 'nextParamid',
},
},
{
namedRegex: '^/dash/(?<helloworld>[^/]+?)(?:/)?$',
namedRegex: '^/dash/(?<nextParamhelloworld>[^/]+?)(?:/)?$',
page: '/dash/[hello-world]',
regex: normalizeRegEx('^\\/dash\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
helloworld: 'hello-world',
nextParamhelloworld: 'nextParamhello-world',
},
},
{
namedRegex: '^/index/(?<slug>.+?)(?:/)?$',
namedRegex: '^/index/(?<nextParamslug>.+?)(?:/)?$',
page: '/index/[...slug]',
regex: normalizeRegEx('^/index/(.+?)(?:/)?$'),
routeKeys: {
slug: 'slug',
nextParamslug: 'nextParamslug',
},
},
{
namedRegex: `^/on\\-mount/(?<post>[^/]+?)(?:/)?$`,
namedRegex: `^/on\\-mount/(?<nextParampost>[^/]+?)(?:/)?$`,
page: '/on-mount/[post]',
regex: normalizeRegEx('^\\/on\\-mount\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
post: 'post',
nextParampost: 'nextParampost',
},
},
{
namedRegex: `^/p1/p2/all\\-ssg/(?<rest>.+?)(?:/)?$`,
namedRegex: `^/p1/p2/all\\-ssg/(?<nextParamrest>.+?)(?:/)?$`,
page: '/p1/p2/all-ssg/[...rest]',
regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssg\\/(.+?)(?:\\/)?$'),
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedRegex: `^/p1/p2/all\\-ssr/(?<rest>.+?)(?:/)?$`,
namedRegex: `^/p1/p2/all\\-ssr/(?<nextParamrest>.+?)(?:/)?$`,
page: '/p1/p2/all-ssr/[...rest]',
regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssr\\/(.+?)(?:\\/)?$'),
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedRegex: `^/p1/p2/nested\\-all\\-ssg/(?<rest>.+?)(?:/)?$`,
namedRegex: `^/p1/p2/nested\\-all\\-ssg/(?<nextParamrest>.+?)(?:/)?$`,
page: '/p1/p2/nested-all-ssg/[...rest]',
regex: normalizeRegEx(
'^\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)(?:\\/)?$'
),
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedRegex: `^/p1/p2/predefined\\-ssg/(?<rest>.+?)(?:/)?$`,
namedRegex: `^/p1/p2/predefined\\-ssg/(?<nextParamrest>.+?)(?:/)?$`,
page: '/p1/p2/predefined-ssg/[...rest]',
regex: normalizeRegEx(
'^\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)(?:\\/)?$'
),
routeKeys: {
rest: 'rest',
nextParamrest: 'nextParamrest',
},
},
{
namedRegex: `^/(?<name>[^/]+?)(?:/)?$`,
namedRegex: `^/(?<nextParamname>[^/]+?)(?:/)?$`,
page: '/[name]',
regex: normalizeRegEx('^\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
name: 'name',
nextParamname: 'nextParamname',
},
},
{
namedRegex: `^/(?<name>[^/]+?)/comments(?:/)?$`,
namedRegex: `^/(?<nextParamname>[^/]+?)/comments(?:/)?$`,
page: '/[name]/comments',
regex: normalizeRegEx('^\\/([^\\/]+?)\\/comments(?:\\/)?$'),
routeKeys: {
name: 'name',
nextParamname: 'nextParamname',
},
},
{
namedRegex: `^/(?<name>[^/]+?)/on\\-mount\\-redir(?:/)?$`,
namedRegex: `^/(?<nextParamname>[^/]+?)/on\\-mount\\-redir(?:/)?$`,
page: '/[name]/on-mount-redir',
regex: normalizeRegEx(
'^\\/([^\\/]+?)\\/on\\-mount\\-redir(?:\\/)?$'
),
routeKeys: {
name: 'name',
nextParamname: 'nextParamname',
},
},
{
namedRegex: `^/(?<name>[^/]+?)/(?<comment>[^/]+?)(?:/)?$`,
namedRegex: `^/(?<nextParamname>[^/]+?)/(?<nextParamcomment>[^/]+?)(?:/)?$`,
page: '/[name]/[comment]',
regex: normalizeRegEx('^\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: {
name: 'name',
comment: 'comment',
nextParamname: 'nextParamname',
nextParamcomment: 'nextParamcomment',
},
},
],

View file

@ -193,7 +193,7 @@ describe('Required Server Files', () => {
it('should render dynamic SSR page correctly with x-matched-path', async () => {
const html = await renderViaHTTP(
appPort,
'/some-other-path?slug=first',
'/some-other-path?nextParamslug=first',
undefined,
{
headers: {
@ -259,7 +259,7 @@ describe('Required Server Files', () => {
it('should return data correctly with x-matched-path', async () => {
const res = await fetchViaHTTP(
appPort,
`/_next/data/${buildId}/dynamic/first.json?slug=first`,
`/_next/data/${buildId}/dynamic/first.json?nextParamslug=first`,
undefined,
{
headers: {

View file

@ -89,7 +89,7 @@ async function createNextInstall({
let combinedDependencies = dependencies
if (!(packageJson && packageJson.nextPrivateSkipLocalDeps)) {
if (!(packageJson && packageJson.nextParamateSkipLocalDeps)) {
const pkgPaths = await rootSpan
.traceChild('linkPackages')
.traceAsyncFn(() =>

View file

@ -313,7 +313,7 @@ describe('should set-up next', () => {
it('should render dynamic SSR page correctly with x-matched-path', async () => {
const html = await renderViaHTTP(
appPort,
'/some-other-path?slug=first',
'/some-other-path?nextParamslug=first',
undefined,
{
headers: {
@ -330,7 +330,7 @@ describe('should set-up next', () => {
const html2 = await renderViaHTTP(
appPort,
'/some-other-path?slug=second',
'/some-other-path?nextParamslug=second',
undefined,
{
headers: {
@ -393,7 +393,7 @@ describe('should set-up next', () => {
it('should return data correctly with x-matched-path', async () => {
const res = await fetchViaHTTP(
appPort,
`/_next/data/${next.buildId}/en/dynamic/first.json?slug=first`,
`/_next/data/${next.buildId}/en/dynamic/first.json?nextParamslug=first`,
undefined,
{
headers: {
@ -640,7 +640,7 @@ describe('should set-up next', () => {
const res = await fetchViaHTTP(
appPort,
'/optional-ssp',
{ rest: '', another: 'value' },
{ nextParamrest: '', another: 'value' },
{
headers: {
'x-matched-path': '/optional-ssp/[[...rest]]',
@ -722,7 +722,7 @@ describe('should set-up next', () => {
const res = await fetchViaHTTP(
appPort,
'/api/optional',
{ rest: '', another: 'value' },
{ nextParamrest: '', another: 'value' },
{
headers: {
'x-matched-path': '/api/optional/[[...rest]]',

View file

@ -570,7 +570,7 @@ describe('should set-up next', () => {
it('should render dynamic SSR page correctly with x-matched-path', async () => {
const html = await renderViaHTTP(
appPort,
'/some-other-path?slug=first',
'/some-other-path?nextParamslug=first',
undefined,
{
headers: {
@ -587,7 +587,7 @@ describe('should set-up next', () => {
const html2 = await renderViaHTTP(
appPort,
'/some-other-path?slug=second',
'/some-other-path?nextParamslug=second',
undefined,
{
headers: {
@ -703,7 +703,7 @@ describe('should set-up next', () => {
it('should return data correctly with x-matched-path', async () => {
const res = await fetchViaHTTP(
appPort,
`/_next/data/${next.buildId}/dynamic/first.json?slug=first`,
`/_next/data/${next.buildId}/dynamic/first.json?nextParamslug=first`,
undefined,
{
headers: {
@ -1145,7 +1145,7 @@ describe('should set-up next', () => {
const res = await fetchViaHTTP(
appPort,
'/api/optional/index',
{ rest: 'index', another: 'value' },
{ nextParamrest: 'index', another: 'value' },
{
headers: {
'x-matched-path': '/api/optional/[[...rest]]',