forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
parallel routes: fix catch all route support (vercel#58215)
This PR fixes a bug where parallel routes would not apply appropriately on navigation when used within slots. The following scenarios: ``` /foo /bar /@slot /[...catchAll] ``` or ``` /foo /[...catchAll] /@slot /bar ``` will now function correctly when accessing /foo/bar, and Next.js will render both /bar and the catchall slots. The issue was that the tree constructed by `next-app-loader` for a given path, /foo/bar in the example, would not include the paths for the catch-all files at build time. The routing was done 1-1 when compiling files, where a path would only match one file, with parallel routes, a path could hit a defined path but also a catch all route at the same time in a different slot. The fix consists of adding another normalisation layer that will look for all catch-all in `appPaths` and iterate over the other paths and add the relevant information when needed. The tricky part was making sure that we only included the relevant paths to the loader: we don't want to overwrite a slot with a catch all if there's already a more specific subpath in that slot, i.e. if there's /foo/@slot/bar/page.js, no need to inject /foo/@slot/bar/[...catchAll]. One thing that is not supported right now is optional catch all routes, will add later. fixes vercel#48719 fixes vercel#49662
- Loading branch information
1 parent
b740fe8
commit 4024b25
Showing
7 changed files
with
72 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { AppPathnameNormalizer } from '../server/future/normalizers/built/app/app-pathname-normalizer' | ||
|
||
/** | ||
* This function will transform the appPaths in order to support catch-all routes and parallel routes. | ||
* It will traverse the appPaths, looking for catch-all routes and try to find parallel routes that could match | ||
* the catch-all. If it finds a match, it will add the catch-all to the parallel route's list of possible routes. | ||
* | ||
* @param appPaths The appPaths to transform | ||
*/ | ||
export function normalizeCatchAllRoutes( | ||
appPaths: Record<string, string[]>, | ||
normalizer = new AppPathnameNormalizer() | ||
) { | ||
const catchAllRoutes = [ | ||
...new Set(Object.values(appPaths).flat().filter(isCatchAllRoute)), | ||
] | ||
|
||
for (const appPath of Object.keys(appPaths)) { | ||
for (const catchAllRoute of catchAllRoutes) { | ||
const normalizedCatchAllRoute = normalizer.normalize(catchAllRoute) | ||
const normalizedCatchAllRouteBasePath = normalizedCatchAllRoute.slice( | ||
0, | ||
normalizedCatchAllRoute.indexOf('[') | ||
) | ||
|
||
if ( | ||
// first check if the appPath could match the catch-all | ||
appPath.startsWith(normalizedCatchAllRouteBasePath) && | ||
// then check if there's not already a slot value that could match the catch-all | ||
!appPaths[appPath].some((path) => hasMatchedSlots(path, catchAllRoute)) | ||
) { | ||
appPaths[appPath].push(catchAllRoute) | ||
} | ||
} | ||
} | ||
} | ||
|
||
function hasMatchedSlots(path1: string, path2: string): boolean { | ||
const slots1 = path1.split('/').filter((segment) => segment.startsWith('@')) | ||
const slots2 = path2.split('/').filter((segment) => segment.startsWith('@')) | ||
|
||
if (slots1.length !== slots2.length) return false | ||
|
||
for (let i = 0; i < slots1.length; i++) { | ||
if (slots1[i] !== slots2[i]) return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
function isCatchAllRoute(pathname: string): boolean { | ||
return pathname.includes('[...') || pathname.includes('[[...') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/@slot/baz/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Baz() { | ||
return 'baz slot' | ||
} |
3 changes: 3 additions & 0 deletions
3
test/e2e/app-dir/parallel-routes-and-interception/app/parallel-catchall/bar/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Foo() { | ||
return 'bar slot' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters