Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -851,5 +851,7 @@
"850": "metadataBase is not a valid URL: %s",
"851": "Pass either `webpack` or `turbopack`, not both.",
"852": "Only custom servers can pass `webpack`, `turbo`, or `turbopack`.",
"853": "Turbopack build failed"
"853": "Turbopack build failed",
"854": "No reference found for param: %s in reference: %s",
"855": "No reference found for segment: %s with reference: %s"
}
5 changes: 3 additions & 2 deletions packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ function Router({
}
}, [])

const { cache, tree, nextUrl, focusAndScrollRef } = state
const { cache, tree, nextUrl, focusAndScrollRef, previousNextUrl } = state

const matchingHead = useMemo(() => {
return findHeadInCache(cache, tree[1])
Expand All @@ -423,8 +423,9 @@ function Router({
tree,
focusAndScrollRef,
nextUrl,
previousNextUrl,
}
}, [tree, focusAndScrollRef, nextUrl])
}, [tree, focusAndScrollRef, nextUrl, previousNextUrl])

let head
if (matchingHead !== null) {
Expand Down
9 changes: 8 additions & 1 deletion packages/next/src/client/components/layout-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,14 @@ function InnerLayoutRouter({
new URL(url, location.origin),
{
flightRouterState: refetchTree,
nextUrl: includeNextUrl ? context.nextUrl : null,
nextUrl: includeNextUrl
? // We always send the last next-url, not the current when
// performing a dynamic request. This is because we update
// the next-url after a navigation, but we want the same
// interception route to be matched that used the last
// next-url.
context.previousNextUrl || context.nextUrl
: null,
}
).then((serverResponse) => {
startTransition(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ describe('createInitialRouterState', () => {
},
cache: expectedCache,
nextUrl: '/linking',
previousNextUrl: null,
}

expect(state).toMatchObject(expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export function createInitialRouterState({
// the || operator is intentional, the pathname can be an empty string
(extractPathFromFlightRouterState(initialTree) || location?.pathname) ??
null,
previousNextUrl: null,
}

if (process.env.NODE_ENV !== 'development' && location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ export function handleMutable(
// shouldScroll is true by default, can override to false.
const shouldScroll = mutable.shouldScroll ?? true

let previousNextUrl = state.previousNextUrl
let nextUrl = state.nextUrl

if (isNotUndefined(mutable.patchedTree)) {
// If we received a patched tree, we need to compute the changed path.
const changedPath = computeChangedPath(state.tree, mutable.patchedTree)
if (changedPath) {
// If the tree changed, we need to update the nextUrl
previousNextUrl = nextUrl
nextUrl = changedPath
} else if (!nextUrl) {
// if the tree ends up being the same (ie, no changed path), and we don't have a nextUrl, then we should use the canonicalUrl
Expand Down Expand Up @@ -84,5 +86,6 @@ export function handleMutable(
? mutable.patchedTree
: state.tree,
nextUrl,
previousNextUrl: previousNextUrl,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,12 @@ export function navigateReducer(
new URL(updatedCanonicalUrl, url.origin),
{
flightRouterState: dynamicRequestTree,
nextUrl: state.nextUrl,
// We always send the last next-url, not the current when
// performing a dynamic request. This is because we update
// the next-url after a navigation, but we want the same
// interception route to be matched that used the last
// next-url.
nextUrl: state.previousNextUrl || state.nextUrl,
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ export function restoreReducer(
// Restore provided tree
tree: treeToRestore,
nextUrl: extractPathFromFlightRouterState(treeToRestore) ?? url.pathname,
previousNextUrl: null,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,14 @@ export function serverActionReducer(
// Otherwise the server action might be intercepted with the wrong action id
// (ie, one that corresponds with the intercepted route)
const nextUrl =
state.nextUrl && hasInterceptionRouteInCurrentTree(state.tree)
? state.nextUrl
// We always send the last next-url, not the current when
// performing a dynamic request. This is because we update
// the next-url after a navigation, but we want the same
// interception route to be matched that used the last
// next-url.
(state.previousNextUrl || state.nextUrl) &&
hasInterceptionRouteInCurrentTree(state.tree)
? state.previousNextUrl || state.nextUrl
: null

const navigatedAt = Date.now()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ export type AppRouterState = {
* The underlying "url" representing the UI state, which is used for intercepting routes.
*/
nextUrl: string | null

/**
* The previous next-url that was used previous to a dynamic navigation.
*/
previousNextUrl: string | null
}

export type ReadonlyReducerState = Readonly<AppRouterState>
Expand Down
14 changes: 13 additions & 1 deletion packages/next/src/lib/build-custom-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,19 @@ export function buildCustomRoute(
)
}

const regex = normalizeRouteRegex(source)
// If this is an internal rewrite and it already provides a regex, use it
// otherwise, normalize the source to a regex.
let regex: string
if (
!route.internal ||
type !== 'rewrite' ||
!('regex' in route) ||
typeof route.regex !== 'string'
) {
regex = normalizeRouteRegex(source)
} else {
regex = route.regex
}

if (type !== 'redirect') {
return { ...route, regex }
Expand Down
Loading
Loading