diff --git a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts index aec39673abbac..8f0879241e62f 100644 --- a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts @@ -125,12 +125,19 @@ function getAppPathRequiredChunks( // - app/foo/page -> app/foo // - app/(group)/@named/foo/page -> app/foo // - app/(.)foo/(..)bar/loading -> app/bar +// - app/[...catchAll]/page -> app +// - app/foo/@slot/[...catchAll]/page -> app/foo function entryNameToGroupName(entryName: string) { let groupName = entryName .slice(0, entryName.lastIndexOf('/')) + // Remove slots .replace(/\/@[^/]+/g, '') // Remove the group with lookahead to make sure it's not interception route .replace(/\/\([^/]+\)(?=(\/|$))/g, '') + // Remove catch-all routes since they should be part of the parent group that the catch-all would apply to. + // This is necessary to support parallel routes since multiple page components can be rendered on the same page. + // In order to do that, we need to ensure that the manifests are merged together by putting them in the same group. + .replace(/\/\[?\[\.\.\.[^\]]*\]\]?/g, '') // Interception routes groupName = groupName diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx new file mode 100644 index 0000000000000..07d98879aa600 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/client-component.tsx @@ -0,0 +1,5 @@ +'use client' + +export function ClientComponent() { + return
catchall page client component
+} diff --git a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx index a305e6e15809b..69be5f30859b8 100644 --- a/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx +++ b/test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/[...catchAll]/page.tsx @@ -1,3 +1,9 @@ +import { ClientComponent } from './client-component' + export default function Page() { - return 'main catchall' + return ( +
+ main catchall +
+ ) } 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 0c4fc9d01a170..fdd66c166ca5c 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 @@ -330,7 +330,11 @@ createNextDescribe( await browser.elementByCss('[href="/parallel-catchall/baz"]').click() await check( () => browser.waitForElementByCss('#main').text(), - 'main catchall' + /main catchall/ + ) + await check( + () => browser.waitForElementByCss('#main').text(), + /catchall page client component/ ) await check( () => browser.waitForElementByCss('#slot-content').text(), diff --git a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx new file mode 100644 index 0000000000000..494aaeacfad03 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/client-component.tsx @@ -0,0 +1,5 @@ +'use client' + +export function ClientComponent() { + return
catchall slot client component
+} diff --git a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx index c53a5bb486dbe..6a63e87121129 100644 --- a/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx +++ b/test/e2e/app-dir/parallel-routes-catchall/app/@slot/[...catchAll]/page.tsx @@ -1,3 +1,9 @@ +import { ClientComponent } from './client-component' + export default function Page() { - return 'slot catchall' + return ( +
+ slot catchall +
+ ) } diff --git a/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts b/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts index 38952ae3ffffc..5a5aa0ee80383 100644 --- a/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts +++ b/test/e2e/app-dir/parallel-routes-catchall/parallel-routes-catchall.test.ts @@ -29,11 +29,19 @@ createNextDescribe( // so we'd expect to see the catch-all slot & the page content await check(() => browser.elementById('children').text(), /bar/) await check(() => browser.elementById('slot').text(), /slot catchall/) + await check( + () => browser.elementById('slot').text(), + /catchall slot client component/ + ) }) it('should match correctly when defining an explicit slot but no page', async () => { const browser = await next.browser('/') await check(() => browser.elementById('slot').text(), /slot catchall/) + await check( + () => browser.elementById('slot').text(), + /catchall slot client component/ + ) await browser.elementByCss('[href="/baz"]').click() @@ -46,12 +54,19 @@ createNextDescribe( it('should match both the catch-all page & slot', async () => { const browser = await next.browser('/') await check(() => browser.elementById('slot').text(), /slot catchall/) - + await check( + () => browser.elementById('slot').text(), + /catchall slot client component/ + ) await browser.elementByCss('[href="/quux"]').click() // quux doesn't have a page or slot defined. It should use the catch-all for both await check(() => browser.elementById('children').text(), /main catchall/) await check(() => browser.elementById('slot').text(), /slot catchall/) + await check( + () => browser.elementById('slot').text(), + /catchall slot client component/ + ) }) } )