diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts
index 8bd0d3cc58f91..29f4cc928e5ae 100644
--- a/packages/next/src/build/index.ts
+++ b/packages/next/src/build/index.ts
@@ -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$')
),
}
diff --git a/packages/next/src/build/webpack/loaders/next-metadata-image-loader.ts b/packages/next/src/build/webpack/loaders/next-metadata-image-loader.ts
index 7bc81b91c66b1..2341bf5654b9a 100644
--- a/packages/next/src/build/webpack/loaders/next-metadata-image-loader.ts
+++ b/packages/next/src/build/webpack/loaders/next-metadata-image-loader.ts
@@ -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)};
diff --git a/packages/next/src/lib/constants.ts b/packages/next/src/lib/constants.ts
index 5290d2b30cee7..127503b22d3c7 100644
--- a/packages/next/src/lib/constants.ts
+++ b/packages/next/src/lib/constants.ts
@@ -1,5 +1,7 @@
import type { ServerRuntime } from '../../types'
+export const NEXT_QUERY_PARAM_PREFIX = 'nextParam'
+
// in seconds
export const CACHE_ONE_YEAR = 31536000
diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts
index 055157eb223c3..98d3ed5be034f 100644
--- a/packages/next/src/server/base-server.ts
+++ b/packages/next/src/server/base-server.ts
@@ -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
+ {JSON.stringify(Object.fromEntries(useSearchParams()))} +
> ) } diff --git a/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js b/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js index 02df487f6da92..80029dd814759 100644 --- a/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js +++ b/test/e2e/app-dir/app/app/dynamic/[category]/[id]/page.js @@ -1,4 +1,4 @@ -export default function IdPage({ children, params }) { +export default function IdPage({ children, params, searchParams }) { return ( <>@@ -6,6 +6,8 @@ export default function IdPage({ children, params }) { {JSON.stringify(params)}
{children} + +{JSON.stringify(searchParams)}
> ) } diff --git a/test/e2e/app-dir/app/index.test.ts b/test/e2e/app-dir/app/index.test.ts index c49a6fead4258..5290fa52305e6 100644 --- a/test/e2e/app-dir/app/index.test.ts +++ b/test/e2e/app-dir/app/index.test.ts @@ -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('/') diff --git a/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts b/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts index a58ffc9b5ba5b..ec459464e3350 100644 --- a/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts +++ b/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts @@ -5,6 +5,8 @@ createNextDescribe( 'parallel-routes-and-interception', { files: __dirname, + // TODO: remove after deployment handling is updated + skipDeployment: true, }, ({ next }) => { describe('parallel routes', () => { diff --git a/test/e2e/app-dir/parallel-routes-and-interception/tsconfig.json b/test/e2e/app-dir/parallel-routes-and-interception/tsconfig.json deleted file mode 100644 index d2bc2ac5e3cea..0000000000000 --- a/test/e2e/app-dir/parallel-routes-and-interception/tsconfig.json +++ /dev/null @@ -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"] -} diff --git a/test/e2e/edge-render-getserversideprops/index.test.ts b/test/e2e/edge-render-getserversideprops/index.test.ts index 8d6133490cae9..7726aee46e869 100644 --- a/test/e2e/edge-render-getserversideprops/index.test.ts +++ b/test/e2e/edge-render-getserversideprops/index.test.ts @@ -140,10 +140,10 @@ describe('edge-render-getserversideprops', () => { ), namedDataRouteRegex: `^/_next/data/${escapeStringRegexp( next.buildId - )}/(?[^/]+?)\\.json$`,
+ )}/non\\-json/(? [^/]+?)\\.json$`,
+ )}/non\\-json\\-blocking/(?