diff --git a/packages/next/src/lib/generate-interception-routes-rewrites.ts b/packages/next/src/lib/generate-interception-routes-rewrites.ts index 9247ae66566e2..0eb34537cb5e9 100644 --- a/packages/next/src/lib/generate-interception-routes-rewrites.ts +++ b/packages/next/src/lib/generate-interception-routes-rewrites.ts @@ -10,11 +10,14 @@ import type { Rewrite } from './load-custom-routes' // a function that converts normalised paths (e.g. /foo/[bar]/[baz]) to the format expected by pathToRegexp (e.g. /foo/:bar/:baz) function toPathToRegexpPath(path: string): string { return path.replace(/\[\[?([^\]]+)\]\]?/g, (_, capture) => { + // path-to-regexp only supports word characters, so we replace any non-word characters with underscores + const paramName = capture.replace(/\W+/g, '_') + // handle catch-all segments (e.g. /foo/bar/[...baz] or /foo/bar/[[...baz]]) - if (capture.startsWith('...')) { - return `:${capture.slice(3)}*` + if (paramName.startsWith('...')) { + return `:${paramName.slice(3)}*` } - return ':' + capture + return ':' + paramName }) } diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/(.)some-page/page.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/(.)some-page/page.tsx new file mode 100644 index 0000000000000..134e0f7f9de15 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/(.)some-page/page.tsx @@ -0,0 +1,8 @@ +export default function Page({ params }) { + return ( +
+ Hello from [this-is-my-route]/@intercept/some-page. Param:{' '} + {params['this-is-my-route']} +
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/default.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/default.tsx new file mode 100644 index 0000000000000..c17431379f962 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/@intercept/default.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return null +} diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/layout.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/layout.tsx new file mode 100644 index 0000000000000..bbb1f0cc14cc9 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/layout.tsx @@ -0,0 +1,14 @@ +export default function Layout({ + children, + intercept, +}: { + children: React.ReactNode + intercept: React.ReactNode +}) { + return ( +
+
{children}
+
{intercept}
+
+ ) +} diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/page.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/page.tsx new file mode 100644 index 0000000000000..e8127244c2b66 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/page.tsx @@ -0,0 +1,9 @@ +import Link from 'next/link' + +export default function Page() { + return ( + + Trigger Interception + + ) +} diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/some-page/page.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/some-page/page.tsx new file mode 100644 index 0000000000000..41ec03cf05bb5 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/interception-route-special-params/[this-is-my-route]/some-page/page.tsx @@ -0,0 +1,8 @@ +export default function Page({ params }) { + return ( +
+ Hello from [this-is-my-route]/some-page. Param:{' '} + {params['this-is-my-route']} +
+ ) +} 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 74b4e3d584b94..570bf2a75bd88 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 @@ -873,6 +873,43 @@ createNextDescribe( await check(() => browser.waitForElementByCss('#main-slot').text(), '1') }) + it('should intercept on routes that contain hyphenated/special dynamic params', async () => { + const browser = await next.browser( + '/interception-route-special-params/some-random-param' + ) + + await browser + .elementByCss( + "[href='/interception-route-special-params/some-random-param/some-page']" + ) + .click() + + const interceptionText = + 'Hello from [this-is-my-route]/@intercept/some-page. Param: some-random-param' + const pageText = + 'Hello from [this-is-my-route]/some-page. Param: some-random-param' + + await retry(async () => { + expect(await browser.elementByCss('body').text()).toContain( + interceptionText + ) + + expect(await browser.elementByCss('body').text()).not.toContain( + pageText + ) + }) + + await browser.refresh() + + await retry(async () => { + expect(await browser.elementByCss('body').text()).toContain(pageText) + + expect(await browser.elementByCss('body').text()).not.toContain( + interceptionText + ) + }) + }) + if (isNextStart) { it('should not have /default paths in the prerender manifest', async () => { const prerenderManifest = JSON.parse(