diff --git a/.changeset/brave-cougars-melt.md b/.changeset/brave-cougars-melt.md new file mode 100644 index 000000000..b613731f4 --- /dev/null +++ b/.changeset/brave-cougars-melt.md @@ -0,0 +1,5 @@ +--- +'@cloudflare/next-on-pages': patch +--- + +Fix route intercepts causing 404s for the non-intercepted route. diff --git a/.changeset/dull-wolves-arrive.md b/.changeset/dull-wolves-arrive.md new file mode 100644 index 000000000..d8404280d --- /dev/null +++ b/.changeset/dull-wolves-arrive.md @@ -0,0 +1,5 @@ +--- +'@cloudflare/next-on-pages': patch +--- + +Fix route intercept modals not getting all the parameters for a route. diff --git a/.prettierrc b/.prettierrc index e9c260d92..ff0e58491 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "printWidth": 80, "singleQuote": true, "arrowParens": "avoid", - "semi": true + "semi": true, + "trailingComma": "all" } diff --git a/packages/next-on-pages/templates/_worker.js/routes-matcher.ts b/packages/next-on-pages/templates/_worker.js/routes-matcher.ts index eed16db36..b9a523390 100644 --- a/packages/next-on-pages/templates/_worker.js/routes-matcher.ts +++ b/packages/next-on-pages/templates/_worker.js/routes-matcher.ts @@ -90,7 +90,10 @@ export class RoutesMatcher { */ private checkRouteMatch( route: VercelSource, - checkStatus?: boolean, + { + checkStatus, + checkIntercept, + }: { checkStatus: boolean; checkIntercept: boolean }, ): { routeMatch: MatchPCREResult; routeDest?: string } | undefined { const srcMatch = matchPCRE(route.src, this.path, route.caseSensitive); if (!srcMatch.match) return; @@ -136,6 +139,18 @@ export class RoutesMatcher { return; } + if (checkIntercept && route.dest) { + const interceptRouteRegex = /\/(\(\.+\))+/; + const destIsIntercept = interceptRouteRegex.test(route.dest); + const pathIsIntercept = interceptRouteRegex.test(this.path); + + // If the new destination is an intercept route, only allow it if the current path is also + // an intercept route. + if (destIsIntercept && !pathIsIntercept) { + return; + } + } + return { routeMatch: srcMatch, routeDest: hasFieldProps.routeDest }; } @@ -441,7 +456,13 @@ export class RoutesMatcher { ): Promise { const localeFriendlyRoute = this.getLocaleFriendlyRoute(rawRoute, phase); const { routeMatch, routeDest } = - this.checkRouteMatch(localeFriendlyRoute, phase === 'error') ?? {}; + this.checkRouteMatch(localeFriendlyRoute, { + checkStatus: phase === 'error', + // The build output config correctly maps relevant request paths to be intercepts in the + // `none` phase, while the `rewrite` phase can contain entries that rewrite to an intercept + // that matches requests that are not actually intercepts, causing a 404. + checkIntercept: phase === 'rewrite', + }) ?? {}; const route: VercelSource = { ...localeFriendlyRoute, dest: routeDest }; diff --git a/packages/next-on-pages/templates/_worker.js/utils/http.ts b/packages/next-on-pages/templates/_worker.js/utils/http.ts index 74685ed53..3c2c5d2eb 100644 --- a/packages/next-on-pages/templates/_worker.js/utils/http.ts +++ b/packages/next-on-pages/templates/_worker.js/utils/http.ts @@ -52,6 +52,11 @@ export function isUrl(url: string): boolean { * Next.js fails to derive the correct route parameters and so we need to set them manually. * https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/constants.ts#L3 * + * For params prefixed with `nxtI`, this is a route intercept. It sets the param without the prefix, + * and removes any intercepts from the param's value. This is so that the route intercept is able + * to have the correct route parameters for the page. + * https://github.com/vercel/next.js/blob/cdf2b79ea/packages/next/src/shared/lib/router/utils/route-regex.ts#L6 + * * @param target Target that search params will be applied to. * @param source Source search params to apply to the target. */ @@ -60,10 +65,13 @@ export function applySearchParams( source: URLSearchParams, ) { for (const [key, value] of source.entries()) { - const paramMatch = /^nxtP(.+)$/.exec(key); - if (paramMatch?.[1]) { + const nxtParamMatch = /^nxtP(.+)$/.exec(key); + const nxtInterceptMatch = /^nxtI(.+)$/.exec(key); + if (nxtParamMatch?.[1]) { target.set(key, value); - target.set(paramMatch[1], value); + target.set(nxtParamMatch[1], value); + } else if (nxtInterceptMatch?.[1]) { + target.set(nxtInterceptMatch[1], value.replace(/(\(\.+\))+/, '')); } else if ( !target.has(key) || (!!value && !target.getAll(key).includes(value))