From 01b9603edcc5751f5e6d6a8a3af5a5632aa84a01 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 29 Feb 2024 08:34:11 -0800 Subject: [PATCH] Revert "Ensure dynamic routes dont match _next/static unexpectedly" (#62691) Reverting temporarily to allow investigation into separate issue eliminating this as also an issue. Reverts vercel/next.js#62559 --- packages/next/src/build/index.ts | 16 +- .../future/route-matchers/route-matcher.ts | 9 +- .../e2e/app-dir/app-static/app-static.test.ts | 37 +- test/e2e/edge-pages-support/index.test.ts | 44 +- .../e2e/getserversideprops/test/index.test.ts | 346 +++--- test/e2e/prerender.test.ts | 1022 +++++++++-------- .../custom-routes/test/index.test.js | 315 ++--- .../pages/[name]/[comment]/[...rest].js | 10 +- .../dynamic-routing/test/index.test.js | 115 +- test/integration/i18n-support/test/shared.js | 14 +- test/lib/next-test-utils.ts | 27 +- 11 files changed, 1035 insertions(+), 920 deletions(-) diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index c60e7bbdbab7e..62779226de62c 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -181,7 +181,6 @@ import { TurbopackManifestLoader } from '../server/dev/turbopack/manifest-loader import type { Entrypoints } from '../server/dev/turbopack/types' import { buildCustomRoute } from '../lib/build-custom-route' import { createProgress } from './progress' -import { modifyRouteRegex } from '../lib/redirect-status' interface ExperimentalBypassForInfo { experimentalBypassFor?: RouteHas[] @@ -287,17 +286,13 @@ export type RoutesManifest = { caseSensitive?: boolean } -function pageToRoute(page: string, restrictedPaths?: string[]) { +function pageToRoute(page: string) { const routeRegex = getNamedRouteRegex(page, true) return { page, - regex: normalizeRouteRegex( - restrictedPaths?.length - ? modifyRouteRegex(routeRegex.re.source, restrictedPaths) - : routeRegex.re.source - ), + regex: normalizeRouteRegex(routeRegex.re.source), routeKeys: routeRegex.routeKeys, - namedRegex: modifyRouteRegex(routeRegex.namedRegex, restrictedPaths), + namedRegex: routeRegex.namedRegex, } } @@ -1094,9 +1089,6 @@ export default async function build( const restrictedRedirectPaths = ['/_next'].map((p) => config.basePath ? `${config.basePath}${p}` : p ) - const restrictedDynamicPaths = ['/_next/static'].map((p) => - config.basePath ? `${config.basePath}${p}` : p - ) const routesManifestPath = path.join(distDir, ROUTES_MANIFEST) const routesManifest: RoutesManifest = nextBuildSpan @@ -1111,7 +1103,7 @@ export default async function build( for (const route of sortedRoutes) { if (isDynamicRoute(route)) { - dynamicRoutes.push(pageToRoute(route, restrictedDynamicPaths)) + dynamicRoutes.push(pageToRoute(route)) } else if (!isReservedPage(route)) { staticRoutes.push(pageToRoute(route)) } diff --git a/packages/next/src/server/future/route-matchers/route-matcher.ts b/packages/next/src/server/future/route-matchers/route-matcher.ts index 22fccdb183694..c5f953e16243f 100644 --- a/packages/next/src/server/future/route-matchers/route-matcher.ts +++ b/packages/next/src/server/future/route-matchers/route-matcher.ts @@ -7,7 +7,6 @@ import { type RouteMatchFn, } from '../../../shared/lib/router/utils/route-matcher' import { getRouteRegex } from '../../../shared/lib/router/utils/route-regex' -import { modifyRouteRegex } from '../../../lib/redirect-status' type RouteMatchResult = { params?: Record @@ -25,13 +24,7 @@ export class RouteMatcher { constructor(public readonly definition: D) { if (isDynamicRoute(definition.pathname)) { - const routeRegex = getRouteRegex(definition.pathname) - const origRegex = routeRegex.re.toString() - const modifiedRegex = modifyRouteRegex(origRegex, ['/_next/static']) - routeRegex.re = new RegExp( - modifiedRegex.substring(1, modifiedRegex.length - 1) - ) - this.dynamic = getRouteMatcher(routeRegex) + this.dynamic = getRouteMatcher(getRouteRegex(definition.pathname)) } } diff --git a/test/e2e/app-dir/app-static/app-static.test.ts b/test/e2e/app-dir/app-static/app-static.test.ts index 9cf524e74d121..9979d4db3b398 100644 --- a/test/e2e/app-dir/app-static/app-static.test.ts +++ b/test/e2e/app-dir/app-static/app-static.test.ts @@ -3,12 +3,7 @@ import cheerio from 'cheerio' import { promisify } from 'util' import { join } from 'path' import { createNextDescribe } from 'e2e-utils' -import { - check, - fetchViaHTTP, - normalizeRouteRegExes, - waitFor, -} from 'next-test-utils' +import { check, fetchViaHTTP, normalizeRegEx, waitFor } from 'next-test-utils' import stripAnsi from 'strip-ansi' const glob = promisify(globOrig) @@ -733,7 +728,13 @@ createNextDescribe( for (const key of Object.keys(curManifest.dynamicRoutes)) { const item = curManifest.dynamicRoutes[key] - normalizeRouteRegExes(item) + + if (item.dataRouteRegex) { + item.dataRouteRegex = normalizeRegEx(item.dataRouteRegex) + } + if (item.routeRegex) { + item.routeRegex = normalizeRegEx(item.routeRegex) + } } for (const key of Object.keys(curManifest.routes)) { @@ -1505,7 +1506,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/articles\\/([^\\/]+?)?$", + "routeRegex": "^\\/articles\\/([^\\/]+?)(?:\\/)?$", }, "/blog/[author]": { "dataRoute": "/blog/[author].rsc", @@ -1522,7 +1523,7 @@ createNextDescribe( }, ], "fallback": false, - "routeRegex": "^\\/blog\\/([^\\/]+?)?$", + "routeRegex": "^\\/blog\\/([^\\/]+?)(?:\\/)?$", }, "/blog/[author]/[slug]": { "dataRoute": "/blog/[author]/[slug].rsc", @@ -1539,7 +1540,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/blog\\/([^\\/]+?)\\/([^\\/]+?)?$", + "routeRegex": "^\\/blog\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$", }, "/dynamic-error/[id]": { "dataRoute": "/dynamic-error/[id].rsc", @@ -1556,7 +1557,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/dynamic\\-error\\/([^\\/]+?)?$", + "routeRegex": "^\\/dynamic\\-error\\/([^\\/]+?)(?:\\/)?$", }, "/force-static/[slug]": { "dataRoute": "/force-static/[slug].rsc", @@ -1573,7 +1574,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/force\\-static\\/([^\\/]+?)?$", + "routeRegex": "^\\/force\\-static\\/([^\\/]+?)(?:\\/)?$", }, "/gen-params-dynamic-revalidate/[slug]": { "dataRoute": "/gen-params-dynamic-revalidate/[slug].rsc", @@ -1590,7 +1591,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/gen\\-params\\-dynamic\\-revalidate\\/([^\\/]+?)?$", + "routeRegex": "^\\/gen\\-params\\-dynamic\\-revalidate\\/([^\\/]+?)(?:\\/)?$", }, "/hooks/use-pathname/[slug]": { "dataRoute": "/hooks/use-pathname/[slug].rsc", @@ -1607,7 +1608,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/hooks\\/use\\-pathname\\/([^\\/]+?)?$", + "routeRegex": "^\\/hooks\\/use\\-pathname\\/([^\\/]+?)(?:\\/)?$", }, "/partial-gen-params-no-additional-lang/[lang]/[slug]": { "dataRoute": "/partial-gen-params-no-additional-lang/[lang]/[slug].rsc", @@ -1624,7 +1625,7 @@ createNextDescribe( }, ], "fallback": false, - "routeRegex": "^\\/partial\\-gen\\-params\\-no\\-additional\\-lang\\/([^\\/]+?)\\/([^\\/]+?)?$", + "routeRegex": "^\\/partial\\-gen\\-params\\-no\\-additional\\-lang\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$", }, "/partial-gen-params-no-additional-slug/[lang]/[slug]": { "dataRoute": "/partial-gen-params-no-additional-slug/[lang]/[slug].rsc", @@ -1641,7 +1642,7 @@ createNextDescribe( }, ], "fallback": false, - "routeRegex": "^\\/partial\\-gen\\-params\\-no\\-additional\\-slug\\/([^\\/]+?)\\/([^\\/]+?)?$", + "routeRegex": "^\\/partial\\-gen\\-params\\-no\\-additional\\-slug\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$", }, "/ssg-draft-mode/[[...route]]": { "dataRoute": "/ssg-draft-mode/[[...route]].rsc", @@ -1658,7 +1659,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/ssg\\-draft\\-mode(?:\\/(.+?))?$", + "routeRegex": "^\\/ssg\\-draft\\-mode(?:\\/(.+?))?(?:\\/)?$", }, "/static-to-dynamic-error-forced/[id]": { "dataRoute": "/static-to-dynamic-error-forced/[id].rsc", @@ -1675,7 +1676,7 @@ createNextDescribe( }, ], "fallback": null, - "routeRegex": "^\\/static\\-to\\-dynamic\\-error\\-forced\\/([^\\/]+?)?$", + "routeRegex": "^\\/static\\-to\\-dynamic\\-error\\-forced\\/([^\\/]+?)(?:\\/)?$", }, } `) diff --git a/test/e2e/edge-pages-support/index.test.ts b/test/e2e/edge-pages-support/index.test.ts index dd983fa950de0..b39dfa22d53d9 100644 --- a/test/e2e/edge-pages-support/index.test.ts +++ b/test/e2e/edge-pages-support/index.test.ts @@ -1,5 +1,5 @@ import { createNextDescribe } from 'e2e-utils' -import { fetchViaHTTP, normalizeRouteRegExes } from 'next-test-utils' +import { fetchViaHTTP, normalizeRegEx } from 'next-test-utils' import cheerio from 'cheerio' import { join } from 'path' import escapeStringRegexp from 'escape-string-regexp' @@ -151,31 +151,31 @@ createNextDescribe( ) for (const route of manifest.dataRoutes) { - normalizeRouteRegExes(route) + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) } - expect(manifest.dataRoutes).toEqual( - [ - { - dataRouteRegex: `^/_next/data/${escapeStringRegexp( - next.buildId - )}/index.json$`, - page: '/', - }, - { - dataRouteRegex: `^/_next/data/${escapeStringRegexp( - next.buildId - )}/([^/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeStringRegexp( + expect(manifest.dataRoutes).toEqual([ + { + dataRouteRegex: normalizeRegEx( + `^/_next/data/${escapeStringRegexp(next.buildId)}/index.json$` + ), + page: '/', + }, + { + dataRouteRegex: normalizeRegEx( + `^/_next/data/${escapeStringRegexp( next.buildId - )}/(?[^/]+?)\\.json$`, - page: '/[id]', - routeKeys: { - nxtPid: 'nxtPid', - }, + )}/([^/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeStringRegexp( + next.buildId + )}/(?[^/]+?)\\.json$`, + page: '/[id]', + routeKeys: { + nxtPid: 'nxtPid', }, - ].map((item) => normalizeRouteRegExes(item)) - ) + }, + ]) }) } } diff --git a/test/e2e/getserversideprops/test/index.test.ts b/test/e2e/getserversideprops/test/index.test.ts index d72e3324c6d23..ba5857e249b41 100644 --- a/test/e2e/getserversideprops/test/index.test.ts +++ b/test/e2e/getserversideprops/test/index.test.ts @@ -8,7 +8,7 @@ import { fetchViaHTTP, getBrowserBodyText, getRedboxHeader, - normalizeRouteRegExes, + normalizeRegEx, renderViaHTTP, waitFor, } from 'next-test-utils' @@ -21,6 +21,180 @@ const appDir = join(__dirname, '../app') let buildId let next: NextInstance +const expectedManifestRoutes = () => [ + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/index.json$` + ), + page: '/', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/another.json$` + ), + page: '/another', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog.json$` + ), + page: '/blog', + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + buildId + )}/blog/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]', + routeKeys: { + nxtPpost: 'nxtPpost', + }, + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + buildId + )}/blog/(?[^/]+?)/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]/[comment]', + routeKeys: { + nxtPpost: 'nxtPpost', + nxtPcomment: 'nxtPcomment', + }, + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + buildId + )}/catchall/(?.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/catchall\\/(.+?)\\.json$` + ), + page: '/catchall/[...path]', + routeKeys: { + nxtPpath: 'nxtPpath', + }, + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/custom-cache.json$` + ), + page: '/custom-cache', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/default-revalidate.json$` + ), + page: '/default-revalidate', + }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/early-request-end.json$`, + page: '/early-request-end', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/enoent.json$` + ), + page: '/enoent', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/invalid-keys.json$` + ), + page: '/invalid-keys', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/non-json.json$` + ), + page: '/non-json', + }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/not-found.json$`, + page: '/not-found', + }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/not\\-found\\/([^\\/]+?)\\.json$`, + namedDataRouteRegex: `^/_next/data/${escapeRegex( + buildId + )}/not\\-found/(?[^/]+?)\\.json$`, + page: '/not-found/[slug]', + routeKeys: { + nxtPslug: 'nxtPslug', + }, + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/promise.json$` + ), + page: '/promise', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/promise\\/mutate-res.json$` + ), + page: '/promise/mutate-res', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/promise\\/mutate-res-no-streaming.json$` + ), + page: '/promise/mutate-res-no-streaming', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/promise\\/mutate-res-props.json$` + ), + page: '/promise/mutate-res-props', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/refresh.json$` + ), + page: '/refresh', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/slow.json$` + ), + page: '/slow', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/something.json$` + ), + page: '/something', + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + buildId + )}/user/(?[^/]+?)/profile\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/user\\/([^\\/]+?)\\/profile\\.json$` + ), + page: '/user/[user]/profile', + routeKeys: { + nxtPuser: 'nxtPuser', + }, + }, +] + const navigateTest = () => { it('should navigate between pages successfully', async () => { const toBuild = [ @@ -615,176 +789,10 @@ const runTests = (isDev = false, isDeploy = false) => { await next.readFile('.next/routes-manifest.json') ) for (const route of dataRoutes) { - normalizeRouteRegExes(route) + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) } - expect(dataRoutes).toEqual( - [ - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/index.json$`, - page: '/', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/another.json$`, - page: '/another', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/blog.json$`, - page: '/blog', - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/blog/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/blog\\/([^\\/]+?)\\.json$`, - page: '/blog/[post]', - routeKeys: { - nxtPpost: 'nxtPpost', - }, - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/blog/(?[^/]+?)/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`, - page: '/blog/[post]/[comment]', - routeKeys: { - nxtPpost: 'nxtPpost', - nxtPcomment: 'nxtPcomment', - }, - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/catchall/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/catchall\\/(.+?)\\.json$`, - page: '/catchall/[...path]', - routeKeys: { - nxtPpath: 'nxtPpath', - }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/custom-cache.json$`, - page: '/custom-cache', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/default-revalidate.json$`, - page: '/default-revalidate', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/early-request-end.json$`, - page: '/early-request-end', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/enoent.json$`, - page: '/enoent', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/invalid-keys.json$`, - page: '/invalid-keys', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/non-json.json$`, - page: '/non-json', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/not-found.json$`, - page: '/not-found', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/not\\-found\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/not\\-found/(?[^/]+?)\\.json$`, - page: '/not-found/[slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/promise.json$`, - page: '/promise', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/promise\\/mutate-res.json$`, - page: '/promise/mutate-res', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/promise\\/mutate-res-no-streaming.json$`, - page: '/promise/mutate-res-no-streaming', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/promise\\/mutate-res-props.json$`, - page: '/promise/mutate-res-props', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/refresh.json$`, - page: '/refresh', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/slow.json$`, - page: '/slow', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/something.json$`, - page: '/something', - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/user/(?[^/]+?)/profile\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/user\\/([^\\/]+?)\\/profile\\.json$`, - page: '/user/[user]/profile', - routeKeys: { - nxtPuser: 'nxtPuser', - }, - }, - ].map((item) => normalizeRouteRegExes(item)) - ) + expect(dataRoutes).toEqual(expectedManifestRoutes()) }) } diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index 9f4fbe01d4156..67068f6ade6dd 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -11,7 +11,7 @@ import { getBrowserBodyText, getRedboxHeader, hasRedbox, - normalizeRouteRegExes, + normalizeRegEx, renderViaHTTP, waitFor, } from 'next-test-utils' @@ -89,6 +89,205 @@ describe('Prerender', () => { return !cacheControl || !/no-store/.test(cacheControl) } + const expectedManifestRoutes = () => ({ + '/': { + dataRoute: `/_next/data/${next.buildId}/index.json`, + initialRevalidateSeconds: 2, + srcRoute: null, + }, + '/blog/[post3]': { + dataRoute: `/_next/data/${next.buildId}/blog/[post3].json`, + initialRevalidateSeconds: 10, + srcRoute: '/blog/[post]', + }, + '/blog/post-1': { + dataRoute: `/_next/data/${next.buildId}/blog/post-1.json`, + initialRevalidateSeconds: 10, + srcRoute: '/blog/[post]', + }, + '/blog/post-2': { + dataRoute: `/_next/data/${next.buildId}/blog/post-2.json`, + initialRevalidateSeconds: 10, + srcRoute: '/blog/[post]', + }, + '/blog/post-4': { + dataRoute: `/_next/data/${next.buildId}/blog/post-4.json`, + initialRevalidateSeconds: 10, + srcRoute: '/blog/[post]', + }, + '/blog/post-1/comment-1': { + dataRoute: `/_next/data/${next.buildId}/blog/post-1/comment-1.json`, + initialRevalidateSeconds: 2, + srcRoute: '/blog/[post]/[comment]', + }, + '/blog/post-2/comment-2': { + dataRoute: `/_next/data/${next.buildId}/blog/post-2/comment-2.json`, + initialRevalidateSeconds: 2, + srcRoute: '/blog/[post]/[comment]', + }, + '/blog/post.1': { + dataRoute: `/_next/data/${next.buildId}/blog/post.1.json`, + initialRevalidateSeconds: 10, + srcRoute: '/blog/[post]', + }, + '/catchall-explicit/another/value': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/another/value.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/first': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/first.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/hello/another': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/hello/another.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/second': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/second.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/[first]/[second]': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/[first]/[second].json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/[third]/[fourth]': { + dataRoute: `/_next/data/${next.buildId}/catchall-explicit/[third]/[fourth].json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-optional': { + dataRoute: `/_next/data/${next.buildId}/catchall-optional.json`, + initialRevalidateSeconds: false, + srcRoute: '/catchall-optional/[[...slug]]', + }, + '/catchall-optional/value': { + dataRoute: `/_next/data/${next.buildId}/catchall-optional/value.json`, + initialRevalidateSeconds: false, + srcRoute: '/catchall-optional/[[...slug]]', + }, + '/large-page-data': { + dataRoute: `/_next/data/${next.buildId}/large-page-data.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, + '/another': { + dataRoute: `/_next/data/${next.buildId}/another.json`, + initialRevalidateSeconds: 1, + srcRoute: null, + }, + '/preview': { + dataRoute: `/_next/data/${next.buildId}/preview.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, + '/api-docs/first': { + dataRoute: `/_next/data/${next.buildId}/api-docs/first.json`, + initialRevalidateSeconds: false, + srcRoute: '/api-docs/[...slug]', + }, + '/blocking-fallback-once/404-on-manual-revalidate': { + dataRoute: `/_next/data/${next.buildId}/blocking-fallback-once/404-on-manual-revalidate.json`, + initialRevalidateSeconds: false, + srcRoute: '/blocking-fallback-once/[slug]', + }, + '/blocking-fallback-some/a': { + dataRoute: `/_next/data/${next.buildId}/blocking-fallback-some/a.json`, + initialRevalidateSeconds: 1, + srcRoute: '/blocking-fallback-some/[slug]', + }, + '/blocking-fallback-some/b': { + dataRoute: `/_next/data/${next.buildId}/blocking-fallback-some/b.json`, + initialRevalidateSeconds: 1, + srcRoute: '/blocking-fallback-some/[slug]', + }, + '/blocking-fallback/lots-of-data': { + dataRoute: `/_next/data/${next.buildId}/blocking-fallback/lots-of-data.json`, + initialRevalidateSeconds: false, + srcRoute: '/blocking-fallback/[slug]', + }, + '/blocking-fallback/test-errors-1': { + dataRoute: `/_next/data/${next.buildId}/blocking-fallback/test-errors-1.json`, + initialRevalidateSeconds: 1, + srcRoute: '/blocking-fallback/[slug]', + }, + '/blog': { + dataRoute: `/_next/data/${next.buildId}/blog.json`, + initialRevalidateSeconds: 10, + srcRoute: null, + }, + '/default-revalidate': { + dataRoute: `/_next/data/${next.buildId}/default-revalidate.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, + '/dynamic/[first]': { + dataRoute: `/_next/data/${next.buildId}/dynamic/[first].json`, + initialRevalidateSeconds: false, + srcRoute: '/dynamic/[slug]', + }, + '/dynamic/[second]': { + dataRoute: `/_next/data/${next.buildId}/dynamic/[second].json`, + initialRevalidateSeconds: false, + srcRoute: '/dynamic/[slug]', + }, + // TODO: investigate index/index + // '/index': { + // dataRoute: `/_next/data/${next.buildId}/index/index.json`, + // initialRevalidateSeconds: false, + // srcRoute: null, + // }, + '/lang/de/about': { + dataRoute: `/_next/data/${next.buildId}/lang/de/about.json`, + initialRevalidateSeconds: false, + srcRoute: '/lang/[lang]/about', + }, + '/lang/en/about': { + dataRoute: `/_next/data/${next.buildId}/lang/en/about.json`, + initialRevalidateSeconds: false, + srcRoute: '/lang/[lang]/about', + }, + '/lang/es/about': { + dataRoute: `/_next/data/${next.buildId}/lang/es/about.json`, + initialRevalidateSeconds: false, + srcRoute: '/lang/[lang]/about', + }, + '/lang/fr/about': { + dataRoute: `/_next/data/${next.buildId}/lang/fr/about.json`, + initialRevalidateSeconds: false, + srcRoute: '/lang/[lang]/about', + }, + '/something': { + dataRoute: `/_next/data/${next.buildId}/something.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, + '/catchall/another/value': { + dataRoute: `/_next/data/${next.buildId}/catchall/another/value.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall/[...slug]', + }, + '/catchall/first': { + dataRoute: `/_next/data/${next.buildId}/catchall/first.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall/[...slug]', + }, + '/catchall/second': { + dataRoute: `/_next/data/${next.buildId}/catchall/second.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall/[...slug]', + }, + '/catchall/hello/another': { + dataRoute: `/_next/data/${next.buildId}/catchall/hello/another.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall/[...slug]', + }, + }) + const navigateTest = (isDev = false) => { it('should navigate between pages successfully', async () => { const toBuild = [ @@ -1091,263 +1290,303 @@ describe('Prerender', () => { ) for (const route of dataRoutes) { - normalizeRouteRegExes(route) + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) } - expect(dataRoutes).toEqual( - [ - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/index.json$`, - page: '/', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/another.json$`, - page: '/another', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/api\\-docs\\/(.+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/api\\-docs/(?.+?)\\.json$`, - page: '/api-docs/[...slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/bad-gssp.json$`, - page: '/bad-gssp', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/bad-ssr.json$`, - page: '/bad-ssr', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/blocking\\-fallback\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/blocking\\-fallback/(?[^/]+?)\\.json$`, - page: '/blocking-fallback/[slug]', - routeKeys: { nxtPslug: 'nxtPslug' }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/blocking\\-fallback\\-once\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/blocking\\-fallback\\-once/(?[^/]+?)\\.json$`, - page: '/blocking-fallback-once/[slug]', - routeKeys: { nxtPslug: 'nxtPslug' }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/blocking\\-fallback\\-some\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/blocking\\-fallback\\-some/(?[^/]+?)\\.json$`, - page: '/blocking-fallback-some/[slug]', - routeKeys: { nxtPslug: 'nxtPslug' }, - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/blog.json$`, - page: '/blog', - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/blog/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/blog\\/([^\\/]+?)\\.json$`, - page: '/blog/[post]', - routeKeys: { - nxtPpost: 'nxtPpost', - }, - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( + expect(dataRoutes).toEqual([ + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(next.buildId)}\\/index.json$` + ), + page: '/', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/blog/(?[^/]+?)/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/another.json$` + ), + page: '/another', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`, - page: '/blog/[post]/[comment]', - routeKeys: { - nxtPpost: 'nxtPpost', - nxtPcomment: 'nxtPcomment', - }, + )}\\/api\\-docs\\/(.+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/api\\-docs/(?.+?)\\.json$`, + page: '/api-docs/[...slug]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/catchall/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/bad-gssp.json$` + ), + page: '/bad-gssp', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/catchall\\/(.+?)\\.json$`, - page: '/catchall/[...slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( + )}\\/bad-ssr.json$` + ), + page: '/bad-ssr', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/catchall\\-explicit/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/blocking\\-fallback\\/([^\\/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/blocking\\-fallback/(?[^/]+?)\\.json$`, + page: '/blocking-fallback/[slug]', + routeKeys: { nxtPslug: 'nxtPslug' }, + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/catchall\\-explicit\\/(.+?)\\.json$`, - page: '/catchall-explicit/[...slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( + )}\\/blocking\\-fallback\\-once\\/([^\\/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/blocking\\-fallback\\-once/(?[^/]+?)\\.json$`, + page: '/blocking-fallback-once/[slug]', + routeKeys: { nxtPslug: 'nxtPslug' }, + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/catchall\\-optional(?:/(?.+?))?\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/blocking\\-fallback\\-some\\/([^\\/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/blocking\\-fallback\\-some/(?[^/]+?)\\.json$`, + page: '/blocking-fallback-some/[slug]', + routeKeys: { nxtPslug: 'nxtPslug' }, + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(next.buildId)}\\/blog.json$` + ), + page: '/blog', + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/blog/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/catchall\\-optional(?:\\/(.+?))?\\.json$`, - page: '/catchall-optional/[[...slug]]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, + )}\\/blog\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]', + routeKeys: { + nxtPpost: 'nxtPpost', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/blog/(?[^/]+?)/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/default-revalidate.json$`, - page: '/default-revalidate', + )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]/[comment]', + routeKeys: { + nxtPpost: 'nxtPpost', + nxtPcomment: 'nxtPcomment', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/dynamic\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/catchall/(?.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/dynamic/(?[^/]+?)\\.json$`, - page: '/dynamic/[slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, + )}\\/catchall\\/(.+?)\\.json$` + ), + page: '/catchall/[...slug]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - next.buildId - )}\\/fallback\\-only\\/([^\\/]+?)\\.json$`, - namedDataRouteRegex: `^/_next/data/${escapeRegex( + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/catchall\\-explicit/(?.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/fallback\\-only/(?[^/]+?)\\.json$`, - page: '/fallback-only/[slug]', - routeKeys: { - nxtPslug: 'nxtPslug', - }, + )}\\/catchall\\-explicit\\/(.+?)\\.json$` + ), + page: '/catchall-explicit/[...slug]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - // TODO: investigate index/index - // { - // dataRouteRegex: ( - // `^\\/_next\\/data\\/${escapeRegex( - // next.buildId - // )}\\/index\\/index.json$` - // ), - // page: '/index', - // }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/lang/(?[^/]+?)/about\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/catchall\\-optional(?:/(?.+?))?\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/lang\\/([^\\/]+?)\\/about\\.json$`, - page: '/lang/[lang]/about', - routeKeys: { - nxtPlang: 'nxtPlang', - }, + )}\\/catchall\\-optional(?:\\/(.+?))?\\.json$` + ), + page: '/catchall-optional/[[...slug]]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/large-page-data.json$`, - page: '/large-page-data', - }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/default-revalidate.json$` + ), + page: '/default-revalidate', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/large-page-data-ssr.json$`, - page: '/large-page-data-ssr', + )}\\/dynamic\\/([^\\/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/dynamic/(?[^/]+?)\\.json$`, + page: '/dynamic/[slug]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/non\\-json/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/non\\-json\\/([^\\/]+?)\\.json$`, - page: '/non-json/[p]', - routeKeys: { - nxtPp: 'nxtPp', - }, + )}\\/fallback\\-only\\/([^\\/]+?)\\.json$` + ), + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/fallback\\-only/(?[^/]+?)\\.json$`, + page: '/fallback-only/[slug]', + routeKeys: { + nxtPslug: 'nxtPslug', }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( - next.buildId - )}/non\\-json\\-blocking/(?[^/]+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + // TODO: investigate index/index + // { + // dataRouteRegex: normalizeRegEx( + // `^\\/_next\\/data\\/${escapeRegex( + // next.buildId + // )}\\/index\\/index.json$` + // ), + // page: '/index', + // }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/lang/(?[^/]+?)/about\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/non\\-json\\-blocking\\/([^\\/]+?)\\.json$`, - page: '/non-json-blocking/[p]', - routeKeys: { - nxtPp: 'nxtPp', - }, + )}\\/lang\\/([^\\/]+?)\\/about\\.json$` + ), + page: '/lang/[lang]/about', + routeKeys: { + nxtPlang: 'nxtPlang', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + next.buildId + )}\\/large-page-data.json$`, + page: '/large-page-data', + }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + next.buildId + )}\\/large-page-data-ssr.json$`, + page: '/large-page-data-ssr', + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/non\\-json/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/preview.json$`, - page: '/preview', + )}\\/non\\-json\\/([^\\/]+?)\\.json$` + ), + page: '/non-json/[p]', + routeKeys: { + nxtPp: 'nxtPp', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/non\\-json\\-blocking/(?[^/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/something.json$`, - page: '/something', + )}\\/non\\-json\\-blocking\\/([^\\/]+?)\\.json$` + ), + page: '/non-json-blocking/[p]', + routeKeys: { + nxtPp: 'nxtPp', }, - { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/ssr.json$`, - page: '/ssr', - }, - { - namedDataRouteRegex: `^/_next/data/${escapeRegex( + )}\\/preview.json$` + ), + page: '/preview', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}/user/(?[^/]+?)/profile\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + )}\\/something.json$` + ), + page: '/something', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(next.buildId)}\\/ssr.json$` + ), + page: '/ssr', + }, + { + namedDataRouteRegex: `^/_next/data/${escapeRegex( + next.buildId + )}/user/(?[^/]+?)/profile\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( next.buildId - )}\\/user\\/([^\\/]+?)\\/profile\\.json$`, - page: '/user/[user]/profile', - routeKeys: { - nxtPuser: 'nxtPuser', - }, + )}\\/user\\/([^\\/]+?)\\/profile\\.json$` + ), + page: '/user/[user]/profile', + routeKeys: { + nxtPuser: 'nxtPuser', }, - ].map((item) => normalizeRouteRegExes(item)) - ) + }, + ]) }) it('outputs a prerender-manifest correctly', async () => { @@ -1358,315 +1597,162 @@ describe('Prerender', () => { Object.keys(manifest.dynamicRoutes).forEach((key) => { const item = manifest.dynamicRoutes[key] - normalizeRouteRegExes(item) + + if (item.dataRouteRegex) { + item.dataRouteRegex = normalizeRegEx(item.dataRouteRegex) + } + if (item.routeRegex) { + item.routeRegex = normalizeRegEx(item.routeRegex) + } }) - const expectedDynamicRoutes = { + expect(manifest.version).toBe(4) + expect(manifest.routes).toEqual(expectedManifestRoutes()) + expect(manifest.dynamicRoutes).toEqual({ '/api-docs/[...slug]': { dataRoute: `/_next/data/${next.buildId}/api-docs/[...slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/api\\-docs\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/api\\-docs\\/(.+?)\\.json$` + ), fallback: '/api-docs/[...slug].html', - routeRegex: `^\\/api\\-docs\\/(.+?)(?:\\/)?$`, + routeRegex: normalizeRegEx(`^\\/api\\-docs\\/(.+?)(?:\\/)?$`), }, '/blocking-fallback-once/[slug]': { dataRoute: `/_next/data/${next.buildId}/blocking-fallback-once/[slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\-once\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\-once\\/([^\\/]+?)\\.json$` + ), fallback: null, - routeRegex: - '^\\/blocking\\-fallback\\-once\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/blocking\\-fallback\\-once\\/([^\\/]+?)(?:\\/)?$' + ), }, '/blocking-fallback-some/[slug]': { dataRoute: `/_next/data/${next.buildId}/blocking-fallback-some/[slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\-some\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\-some\\/([^\\/]+?)\\.json$` + ), fallback: null, - routeRegex: - '^\\/blocking\\-fallback\\-some\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/blocking\\-fallback\\-some\\/([^\\/]+?)(?:\\/)?$' + ), }, '/blocking-fallback/[slug]': { dataRoute: `/_next/data/${next.buildId}/blocking-fallback/[slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/blocking\\-fallback\\/([^\\/]+?)\\.json$` + ), fallback: null, - routeRegex: '^\\/blocking\\-fallback\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/blocking\\-fallback\\/([^\\/]+?)(?:\\/)?$' + ), }, '/blog/[post]': { fallback: '/blog/[post].html', dataRoute: `/_next/data/${next.buildId}/blog/[post].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blog\\/([^\\/]+?)\\.json$`, - routeRegex: '^\\/blog\\/([^\\/]+?)(?:\\/)?$', + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/blog\\/([^\\/]+?)\\.json$` + ), + routeRegex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'), }, '/blog/[post]/[comment]': { fallback: '/blog/[post]/[comment].html', dataRoute: `/_next/data/${next.buildId}/blog/[post]/[comment].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`, - routeRegex: '^\\/blog\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$', + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$` + ), + routeRegex: normalizeRegEx( + '^\\/blog\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$' + ), }, '/dynamic/[slug]': { dataRoute: `/_next/data/${next.buildId}/dynamic/[slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/dynamic\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/dynamic\\/([^\\/]+?)\\.json$` + ), fallback: false, - routeRegex: `^\\/dynamic\\/([^\\/]+?)(?:\\/)?$`, + routeRegex: normalizeRegEx(`^\\/dynamic\\/([^\\/]+?)(?:\\/)?$`), }, '/fallback-only/[slug]': { dataRoute: `/_next/data/${next.buildId}/fallback-only/[slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/fallback\\-only\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/fallback\\-only\\/([^\\/]+?)\\.json$` + ), fallback: '/fallback-only/[slug].html', - routeRegex: '^\\/fallback\\-only\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/fallback\\-only\\/([^\\/]+?)(?:\\/)?$' + ), }, '/lang/[lang]/about': { dataRoute: `/_next/data/${next.buildId}/lang/[lang]/about.json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/lang\\/([^\\/]+?)\\/about\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/lang\\/([^\\/]+?)\\/about\\.json$` + ), fallback: false, - routeRegex: '^\\/lang\\/([^\\/]+?)\\/about(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/lang\\/([^\\/]+?)\\/about(?:\\/)?$' + ), }, '/non-json-blocking/[p]': { dataRoute: `/_next/data/${next.buildId}/non-json-blocking/[p].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/non\\-json\\-blocking\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/non\\-json\\-blocking\\/([^\\/]+?)\\.json$` + ), fallback: null, - routeRegex: '^\\/non\\-json\\-blocking\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/non\\-json\\-blocking\\/([^\\/]+?)(?:\\/)?$' + ), }, '/non-json/[p]': { dataRoute: `/_next/data/${next.buildId}/non-json/[p].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/non\\-json\\/([^\\/]+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/non\\-json\\/([^\\/]+?)\\.json$` + ), fallback: '/non-json/[p].html', - routeRegex: '^\\/non\\-json\\/([^\\/]+?)(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/non\\-json\\/([^\\/]+?)(?:\\/)?$' + ), }, '/user/[user]/profile': { fallback: '/user/[user]/profile.html', dataRoute: `/_next/data/${next.buildId}/user/[user]/profile.json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/user\\/([^\\/]+?)\\/profile\\.json$`, - routeRegex: `^\\/user\\/([^\\/]+?)\\/profile(?:\\/)?$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/user\\/([^\\/]+?)\\/profile\\.json$` + ), + routeRegex: normalizeRegEx( + `^\\/user\\/([^\\/]+?)\\/profile(?:\\/)?$` + ), }, '/catchall/[...slug]': { fallback: '/catchall/[...slug].html', - routeRegex: '^\\/catchall\\/(.+?)(?:\\/)?$', + routeRegex: normalizeRegEx('^\\/catchall\\/(.+?)(?:\\/)?$'), dataRoute: `/_next/data/${next.buildId}/catchall/[...slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\/(.+?)\\.json$` + ), }, '/catchall-optional/[[...slug]]': { dataRoute: `/_next/data/${next.buildId}/catchall-optional/[[...slug]].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\-optional(?:\\/(.+?))?\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\-optional(?:\\/(.+?))?\\.json$` + ), fallback: false, - routeRegex: '^\\/catchall\\-optional(?:\\/(.+?))?(?:\\/)?$', + routeRegex: normalizeRegEx( + '^\\/catchall\\-optional(?:\\/(.+?))?(?:\\/)?$' + ), }, '/catchall-explicit/[...slug]': { dataRoute: `/_next/data/${next.buildId}/catchall-explicit/[...slug].json`, - dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\-explicit\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\-explicit\\/(.+?)\\.json$` + ), fallback: false, - routeRegex: '^\\/catchall\\-explicit\\/(.+?)(?:\\/)?$', - }, - } - Object.keys(expectedDynamicRoutes).forEach((key) => { - const item = expectedDynamicRoutes[key] - normalizeRouteRegExes(item) - }) - - const expectedManifestRoutes = { - '/': { - dataRoute: `/_next/data/${next.buildId}/index.json`, - initialRevalidateSeconds: 2, - srcRoute: null, - }, - '/blog/[post3]': { - dataRoute: `/_next/data/${next.buildId}/blog/[post3].json`, - initialRevalidateSeconds: 10, - srcRoute: '/blog/[post]', - }, - '/blog/post-1': { - dataRoute: `/_next/data/${next.buildId}/blog/post-1.json`, - initialRevalidateSeconds: 10, - srcRoute: '/blog/[post]', - }, - '/blog/post-2': { - dataRoute: `/_next/data/${next.buildId}/blog/post-2.json`, - initialRevalidateSeconds: 10, - srcRoute: '/blog/[post]', - }, - '/blog/post-4': { - dataRoute: `/_next/data/${next.buildId}/blog/post-4.json`, - initialRevalidateSeconds: 10, - srcRoute: '/blog/[post]', - }, - '/blog/post-1/comment-1': { - dataRoute: `/_next/data/${next.buildId}/blog/post-1/comment-1.json`, - initialRevalidateSeconds: 2, - srcRoute: '/blog/[post]/[comment]', - }, - '/blog/post-2/comment-2': { - dataRoute: `/_next/data/${next.buildId}/blog/post-2/comment-2.json`, - initialRevalidateSeconds: 2, - srcRoute: '/blog/[post]/[comment]', - }, - '/blog/post.1': { - dataRoute: `/_next/data/${next.buildId}/blog/post.1.json`, - initialRevalidateSeconds: 10, - srcRoute: '/blog/[post]', + routeRegex: normalizeRegEx( + '^\\/catchall\\-explicit\\/(.+?)(?:\\/)?$' + ), }, - '/catchall-explicit/another/value': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/another/value.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-explicit/first': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/first.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-explicit/hello/another': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/hello/another.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-explicit/second': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/second.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-explicit/[first]/[second]': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/[first]/[second].json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-explicit/[third]/[fourth]': { - dataRoute: `/_next/data/${next.buildId}/catchall-explicit/[third]/[fourth].json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall-explicit/[...slug]', - }, - '/catchall-optional': { - dataRoute: `/_next/data/${next.buildId}/catchall-optional.json`, - initialRevalidateSeconds: false, - srcRoute: '/catchall-optional/[[...slug]]', - }, - '/catchall-optional/value': { - dataRoute: `/_next/data/${next.buildId}/catchall-optional/value.json`, - initialRevalidateSeconds: false, - srcRoute: '/catchall-optional/[[...slug]]', - }, - '/large-page-data': { - dataRoute: `/_next/data/${next.buildId}/large-page-data.json`, - initialRevalidateSeconds: false, - srcRoute: null, - }, - '/another': { - dataRoute: `/_next/data/${next.buildId}/another.json`, - initialRevalidateSeconds: 1, - srcRoute: null, - }, - '/preview': { - dataRoute: `/_next/data/${next.buildId}/preview.json`, - initialRevalidateSeconds: false, - srcRoute: null, - }, - '/api-docs/first': { - dataRoute: `/_next/data/${next.buildId}/api-docs/first.json`, - initialRevalidateSeconds: false, - srcRoute: '/api-docs/[...slug]', - }, - '/blocking-fallback-once/404-on-manual-revalidate': { - dataRoute: `/_next/data/${next.buildId}/blocking-fallback-once/404-on-manual-revalidate.json`, - initialRevalidateSeconds: false, - srcRoute: '/blocking-fallback-once/[slug]', - }, - '/blocking-fallback-some/a': { - dataRoute: `/_next/data/${next.buildId}/blocking-fallback-some/a.json`, - initialRevalidateSeconds: 1, - srcRoute: '/blocking-fallback-some/[slug]', - }, - '/blocking-fallback-some/b': { - dataRoute: `/_next/data/${next.buildId}/blocking-fallback-some/b.json`, - initialRevalidateSeconds: 1, - srcRoute: '/blocking-fallback-some/[slug]', - }, - '/blocking-fallback/lots-of-data': { - dataRoute: `/_next/data/${next.buildId}/blocking-fallback/lots-of-data.json`, - initialRevalidateSeconds: false, - srcRoute: '/blocking-fallback/[slug]', - }, - '/blocking-fallback/test-errors-1': { - dataRoute: `/_next/data/${next.buildId}/blocking-fallback/test-errors-1.json`, - initialRevalidateSeconds: 1, - srcRoute: '/blocking-fallback/[slug]', - }, - '/blog': { - dataRoute: `/_next/data/${next.buildId}/blog.json`, - initialRevalidateSeconds: 10, - srcRoute: null, - }, - '/default-revalidate': { - dataRoute: `/_next/data/${next.buildId}/default-revalidate.json`, - initialRevalidateSeconds: false, - srcRoute: null, - }, - '/dynamic/[first]': { - dataRoute: `/_next/data/${next.buildId}/dynamic/[first].json`, - initialRevalidateSeconds: false, - srcRoute: '/dynamic/[slug]', - }, - '/dynamic/[second]': { - dataRoute: `/_next/data/${next.buildId}/dynamic/[second].json`, - initialRevalidateSeconds: false, - srcRoute: '/dynamic/[slug]', - }, - // TODO: investigate index/index - // '/index': { - // dataRoute: `/_next/data/${next.buildId}/index/index.json`, - // initialRevalidateSeconds: false, - // srcRoute: null, - // }, - '/lang/de/about': { - dataRoute: `/_next/data/${next.buildId}/lang/de/about.json`, - initialRevalidateSeconds: false, - srcRoute: '/lang/[lang]/about', - }, - '/lang/en/about': { - dataRoute: `/_next/data/${next.buildId}/lang/en/about.json`, - initialRevalidateSeconds: false, - srcRoute: '/lang/[lang]/about', - }, - '/lang/es/about': { - dataRoute: `/_next/data/${next.buildId}/lang/es/about.json`, - initialRevalidateSeconds: false, - srcRoute: '/lang/[lang]/about', - }, - '/lang/fr/about': { - dataRoute: `/_next/data/${next.buildId}/lang/fr/about.json`, - initialRevalidateSeconds: false, - srcRoute: '/lang/[lang]/about', - }, - '/something': { - dataRoute: `/_next/data/${next.buildId}/something.json`, - initialRevalidateSeconds: false, - srcRoute: null, - }, - '/catchall/another/value': { - dataRoute: `/_next/data/${next.buildId}/catchall/another/value.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall/[...slug]', - }, - '/catchall/first': { - dataRoute: `/_next/data/${next.buildId}/catchall/first.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall/[...slug]', - }, - '/catchall/second': { - dataRoute: `/_next/data/${next.buildId}/catchall/second.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall/[...slug]', - }, - '/catchall/hello/another': { - dataRoute: `/_next/data/${next.buildId}/catchall/hello/another.json`, - initialRevalidateSeconds: 1, - srcRoute: '/catchall/[...slug]', - }, - } - Object.keys(expectedManifestRoutes).forEach((key) => { - const item = expectedManifestRoutes[key] - normalizeRouteRegExes(item) }) - - expect(manifest.version).toBe(4) - expect(manifest.routes).toEqual(expectedManifestRoutes) - expect(manifest.dynamicRoutes).toEqual(expectedDynamicRoutes) }) it('outputs prerendered files correctly', async () => { diff --git a/test/integration/custom-routes/test/index.test.js b/test/integration/custom-routes/test/index.test.js index d4f5529464134..bfd00ddf73e12 100644 --- a/test/integration/custom-routes/test/index.test.js +++ b/test/integration/custom-routes/test/index.test.js @@ -20,9 +20,9 @@ import { renderViaHTTP, getBrowserBodyText, waitFor, + normalizeRegEx, hasRedbox, check, - normalizeRouteRegExes, } from 'next-test-utils' let appDir = join(__dirname, '..') @@ -1482,16 +1482,17 @@ const runTests = (isDev = false) => { ) for (const route of [ - ...manifest.staticRoutes, ...manifest.dynamicRoutes, ...manifest.rewrites.beforeFiles, ...manifest.rewrites.afterFiles, ...manifest.rewrites.fallback, ...manifest.redirects, ...manifest.headers, - ...manifest.dataRoutes, ]) { - normalizeRouteRegExes(route) + route.regex = normalizeRegEx(route.regex) + } + for (const route of manifest.dataRoutes) { + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) } expect(manifest).toEqual({ @@ -1501,9 +1502,11 @@ const runTests = (isDev = false) => { basePath: '', dataRoutes: [ { - dataRouteRegex: `^/_next/data/${escapeRegex( - buildId - )}/blog\\-catchall/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^/_next/data/${escapeRegex( + buildId + )}/blog\\-catchall/(.+?)\\.json$` + ), namedDataRouteRegex: `^/_next/data/${escapeRegex( buildId )}/blog\\-catchall/(?.+?)\\.json$`, @@ -1524,11 +1527,13 @@ const runTests = (isDev = false) => { nxtPslug: 'nxtPslug', }, }, - ].map((item) => normalizeRouteRegExes(item)), + ], redirects: [ { destination: '/:path+', - regex: '^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))\\/$', + regex: normalizeRegEx( + '^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))\\/$' + ), source: '/:path+/', statusCode: 308, internal: true, @@ -1542,7 +1547,9 @@ const runTests = (isDev = false) => { value: '(?.*)', }, ], - regex: '^(?!\\/_next)\\/missing-redirect-1(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/missing-redirect-1(?:\\/)?$' + ), source: '/missing-redirect-1', statusCode: 307, }, @@ -1554,7 +1561,9 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^(?!\\/_next)\\/missing-redirect-2(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/missing-redirect-2(?:\\/)?$' + ), source: '/missing-redirect-2', statusCode: 307, }, @@ -1567,14 +1576,17 @@ const runTests = (isDev = false) => { value: '(?true)', }, ], - regex: '^(?!\\/_next)\\/missing-redirect-3(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/missing-redirect-3(?:\\/)?$' + ), source: '/missing-redirect-3', statusCode: 307, }, { destination: '/:lang/about', - regex: - '^(?!\\/_next)\\/redirect\\/me\\/to-about(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/redirect\\/me\\/to-about(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/redirect/me/to-about/:lang', statusCode: 307, }, @@ -1582,127 +1594,141 @@ const runTests = (isDev = false) => { source: '/docs/router-status/:code', destination: '/docs/v2/network/status-codes#:code', statusCode: 301, - regex: - '^(?!\\/_next)\\/docs\\/router-status(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/docs\\/router-status(?:\\/([^\\/]+?))(?:\\/)?$' + ), }, { source: '/docs/github', destination: '/docs/v2/advanced/now-for-github', statusCode: 301, - regex: '^(?!\\/_next)\\/docs\\/github(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/docs\\/github(?:\\/)?$'), }, { source: '/docs/v2/advanced/:all(.*)', destination: '/docs/v2/more/:all', statusCode: 301, - regex: '^(?!\\/_next)\\/docs\\/v2\\/advanced(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/docs\\/v2\\/advanced(?:\\/(.*))(?:\\/)?$' + ), }, { source: '/hello/:id/another', destination: '/blog/:id', statusCode: 307, - regex: '^(?!\\/_next)\\/hello(?:\\/([^\\/]+?))\\/another(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/hello(?:\\/([^\\/]+?))\\/another(?:\\/)?$' + ), }, { source: '/redirect1', destination: '/', statusCode: 307, - regex: '^(?!\\/_next)\\/redirect1(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redirect1(?:\\/)?$'), }, { source: '/redirect2', destination: '/', statusCode: 301, - regex: '^(?!\\/_next)\\/redirect2(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redirect2(?:\\/)?$'), }, { source: '/redirect3', destination: '/another', statusCode: 302, - regex: '^(?!\\/_next)\\/redirect3(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redirect3(?:\\/)?$'), }, { source: '/redirect4', destination: '/', statusCode: 308, - regex: '^(?!\\/_next)\\/redirect4(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redirect4(?:\\/)?$'), }, { source: '/redir-chain1', destination: '/redir-chain2', statusCode: 301, - regex: '^(?!\\/_next)\\/redir-chain1(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redir-chain1(?:\\/)?$'), }, { source: '/redir-chain2', destination: '/redir-chain3', statusCode: 302, - regex: '^(?!\\/_next)\\/redir-chain2(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redir-chain2(?:\\/)?$'), }, { source: '/redir-chain3', destination: '/', statusCode: 303, - regex: '^(?!\\/_next)\\/redir-chain3(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redir-chain3(?:\\/)?$'), }, { destination: 'https://google.com', - regex: '^(?!\\/_next)\\/to-external(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/to-external(?:\\/)?$'), source: '/to-external', statusCode: 307, }, { destination: '/with-params?first=:section&second=:name', - regex: - '^(?!\\/_next)\\/query-redirect(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/query-redirect(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/query-redirect/:section/:name', statusCode: 307, }, { destination: '/got-unnamed', - regex: - '^(?!\\/_next)\\/unnamed(?:\\/(first|second))(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/unnamed(?:\\/(first|second))(?:\\/(.*))(?:\\/)?$' + ), source: '/unnamed/(first|second)/(.*)', statusCode: 307, }, { destination: '/:0', - regex: - '^(?!\\/_next)\\/named-like-unnamed(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/named-like-unnamed(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/named-like-unnamed/:0', statusCode: 307, }, { destination: '/thank-you-next', - regex: '^(?!\\/_next)\\/redirect-override(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/redirect-override(?:\\/)?$'), source: '/redirect-override', statusCode: 307, }, { destination: '/:first/:second', - regex: - '^(?!\\/_next)\\/docs(?:\\/(integrations|now-cli))\\/v2(.*)(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/docs(?:\\/(integrations|now-cli))\\/v2(.*)(?:\\/)?$' + ), source: '/docs/:first(integrations|now-cli)/v2:second(.*)', statusCode: 307, }, { destination: '/somewhere', - regex: - '^(?!\\/_next)\\/catchall-redirect(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/catchall-redirect(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/catchall-redirect/:path*', statusCode: 307, }, { destination: 'https://authserver.example.com/set-password?returnUrl=https%3A%2F%2Fwww.example.com/login', - regex: '^(?!\\/_next)\\/to-external-with-query(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/to-external-with-query(?:\\/)?$' + ), source: '/to-external-with-query', statusCode: 307, }, { destination: 'https://authserver.example.com/set-password?returnUrl=https://www.example.com/login', - regex: '^(?!\\/_next)\\/to-external-with-query-2(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)\\/to-external-with-query-2(?:\\/)?$' + ), source: '/to-external-with-query-2', statusCode: 307, }, @@ -1715,7 +1741,7 @@ const runTests = (isDev = false) => { value: '(?.*)', }, ], - regex: '^(?!\\/_next)\\/has-redirect-1(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-1(?:\\/)?$'), source: '/has-redirect-1', statusCode: 307, }, @@ -1727,7 +1753,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^(?!\\/_next)\\/has-redirect-2(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-2(?:\\/)?$'), source: '/has-redirect-2', statusCode: 307, }, @@ -1740,7 +1766,7 @@ const runTests = (isDev = false) => { value: 'true', }, ], - regex: '^(?!\\/_next)\\/has-redirect-3(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-3(?:\\/)?$'), source: '/has-redirect-3', statusCode: 307, }, @@ -1752,7 +1778,7 @@ const runTests = (isDev = false) => { value: 'example.com', }, ], - regex: '^(?!\\/_next)\\/has-redirect-4(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-4(?:\\/)?$'), source: '/has-redirect-4', statusCode: 307, }, @@ -1764,7 +1790,9 @@ const runTests = (isDev = false) => { type: 'header', }, ], - regex: '^(?!\\/_next)(?:\\/([^\\/]+?))\\/has-redirect-5(?:\\/)?$', + regex: normalizeRegEx( + '^(?!\\/_next)(?:\\/([^\\/]+?))\\/has-redirect-5(?:\\/)?$' + ), source: '/:path/has-redirect-5', statusCode: 307, }, @@ -1776,13 +1804,13 @@ const runTests = (isDev = false) => { value: '(?.*)-test.example.com', }, ], - regex: '^(?!\\/_next)\\/has-redirect-6(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-6(?:\\/)?$'), source: '/has-redirect-6', statusCode: 307, }, { source: '/has-redirect-7', - regex: '^(?!\\/_next)\\/has-redirect-7(?:\\/)?$', + regex: normalizeRegEx('^(?!\\/_next)\\/has-redirect-7(?:\\/)?$'), has: [ { type: 'query', @@ -1793,7 +1821,7 @@ const runTests = (isDev = false) => { destination: '/somewhere?value=:hello', statusCode: 307, }, - ].map((item) => normalizeRouteRegExes(item)), + ], headers: [ { headers: [ @@ -1809,7 +1837,7 @@ const runTests = (isDev = false) => { value: '(?.*)', }, ], - regex: '^\\/missing-headers-1(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-headers-1(?:\\/)?$'), source: '/missing-headers-1', }, { @@ -1825,7 +1853,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/missing-headers-2(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-headers-2(?:\\/)?$'), source: '/missing-headers-2', }, { @@ -1842,7 +1870,7 @@ const runTests = (isDev = false) => { value: '(?true)', }, ], - regex: '^\\/missing-headers-3(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-headers-3(?:\\/)?$'), source: '/missing-headers-3', }, { @@ -1856,7 +1884,7 @@ const runTests = (isDev = false) => { value: 'hello again', }, ], - regex: '^\\/add-header(?:\\/)?$', + regex: normalizeRegEx('^\\/add-header(?:\\/)?$'), source: '/add-header', }, { @@ -1870,7 +1898,7 @@ const runTests = (isDev = false) => { value: 'second', }, ], - regex: '^\\/my-headers(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx('^\\/my-headers(?:\\/(.*))(?:\\/)?$'), source: '/my-headers/(.*)', }, { @@ -1925,7 +1953,9 @@ const runTests = (isDev = false) => { "default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com/:path", }, ], - regex: '^\\/my-other-header(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/my-other-header(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/my-other-header/:path', }, { @@ -1935,7 +1965,7 @@ const runTests = (isDev = false) => { value: 'https://example.com', }, ], - regex: '^\\/without-params\\/url(?:\\/)?$', + regex: normalizeRegEx('^\\/without-params\\/url(?:\\/)?$'), source: '/without-params/url', }, { @@ -1945,8 +1975,9 @@ const runTests = (isDev = false) => { value: 'https://example.com/:path*', }, ], - regex: - '^\\/with-params\\/url(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/with-params\\/url(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/with-params/url/:path*', }, { @@ -1956,8 +1987,9 @@ const runTests = (isDev = false) => { value: 'https://example.com:8080?hello=:path*', }, ], - regex: - '^\\/with-params\\/url2(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/with-params\\/url2(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/with-params/url2/:path*', }, { @@ -1967,7 +1999,9 @@ const runTests = (isDev = false) => { value: 'applied-everywhere', }, ], - regex: '^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/:path*', }, { @@ -1981,7 +2015,7 @@ const runTests = (isDev = false) => { value: 'end', }, ], - regex: '^\\/named-pattern(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx('^\\/named-pattern(?:\\/(.*))(?:\\/)?$'), source: '/named-pattern/:path(.*)', }, { @@ -1991,8 +2025,9 @@ const runTests = (isDev = false) => { value: ':path*', }, ], - regex: - '^\\/catchall-header(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/catchall-header(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/catchall-header/:path*', }, { @@ -2009,7 +2044,7 @@ const runTests = (isDev = false) => { value: 'header', }, ], - regex: '^\\/has-header-1(?:\\/)?$', + regex: normalizeRegEx('^\\/has-header-1(?:\\/)?$'), source: '/has-header-1', }, { @@ -2025,7 +2060,7 @@ const runTests = (isDev = false) => { value: 'value', }, ], - regex: '^\\/has-header-2(?:\\/)?$', + regex: normalizeRegEx('^\\/has-header-2(?:\\/)?$'), source: '/has-header-2', }, { @@ -2042,7 +2077,7 @@ const runTests = (isDev = false) => { value: 'yuuuup', }, ], - regex: '^\\/has-header-3(?:\\/)?$', + regex: normalizeRegEx('^\\/has-header-3(?:\\/)?$'), source: '/has-header-3', }, { @@ -2058,10 +2093,10 @@ const runTests = (isDev = false) => { value: 'yuuuup', }, ], - regex: '^\\/has-header-4(?:\\/)?$', + regex: normalizeRegEx('^\\/has-header-4(?:\\/)?$'), source: '/has-header-4', }, - ].map((item) => normalizeRouteRegExes(item)), + ], rewrites: { beforeFiles: [ { @@ -2072,174 +2107,188 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/hello(?:\\/)?$', + regex: normalizeRegEx('^\\/hello(?:\\/)?$'), source: '/hello', }, { destination: '/blog/:path*', - regex: - '^\\/old-blog(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/old-blog(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/old-blog/:path*', }, { destination: 'https://example.vercel.sh', - regex: '^\\/overridden(?:\\/)?$', + regex: normalizeRegEx('^\\/overridden(?:\\/)?$'), source: '/overridden', }, { destination: '/_sport/nfl/:path*', - regex: - '^\\/nfl(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/nfl(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/nfl/:path*', }, - ].map((item) => normalizeRouteRegExes(item)), + ], afterFiles: [ { destination: `http://localhost:${externalServerPort}/_next/webpack-hmr?page=/about`, - regex: '^\\/to-websocket(?:\\/)?$', + regex: normalizeRegEx('^\\/to-websocket(?:\\/)?$'), source: '/to-websocket', }, { destination: '/hello', - regex: '^\\/websocket-to-page(?:\\/)?$', + regex: normalizeRegEx('^\\/websocket-to-page(?:\\/)?$'), source: '/websocket-to-page', }, { destination: 'http://localhost:12233', - regex: '^\\/to-nowhere(?:\\/)?$', + regex: normalizeRegEx('^\\/to-nowhere(?:\\/)?$'), source: '/to-nowhere', }, { destination: '/auto-export/hello?rewrite=1', - regex: '^\\/rewriting-to-auto-export(?:\\/)?$', + regex: normalizeRegEx('^\\/rewriting-to-auto-export(?:\\/)?$'), source: '/rewriting-to-auto-export', }, { destination: '/auto-export/another?rewrite=1', - regex: - '^\\/rewriting-to-another-auto-export(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/rewriting-to-another-auto-export(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/rewriting-to-another-auto-export/:path*', }, { destination: '/another/one', - regex: '^\\/to-another(?:\\/)?$', + regex: normalizeRegEx('^\\/to-another(?:\\/)?$'), source: '/to-another', }, { destination: '/404', - regex: '^\\/nav(?:\\/)?$', + regex: normalizeRegEx('^\\/nav(?:\\/)?$'), source: '/nav', }, { source: '/hello-world', destination: '/static/hello.txt', - regex: '^\\/hello-world(?:\\/)?$', + regex: normalizeRegEx('^\\/hello-world(?:\\/)?$'), }, { source: '/', destination: '/another', - regex: '^\\/(?:\\/)?$', + regex: normalizeRegEx('^\\/(?:\\/)?$'), }, { source: '/another', destination: '/multi-rewrites', - regex: '^\\/another(?:\\/)?$', + regex: normalizeRegEx('^\\/another(?:\\/)?$'), }, { source: '/first', destination: '/hello', - regex: '^\\/first(?:\\/)?$', + regex: normalizeRegEx('^\\/first(?:\\/)?$'), }, { source: '/second', destination: '/hello-again', - regex: '^\\/second(?:\\/)?$', + regex: normalizeRegEx('^\\/second(?:\\/)?$'), }, { destination: '/hello', - regex: '^\\/to-hello(?:\\/)?$', + regex: normalizeRegEx('^\\/to-hello(?:\\/)?$'), source: '/to-hello', }, { destination: '/blog/post-2', - regex: '^\\/blog\\/post-1(?:\\/)?$', + regex: normalizeRegEx('^\\/blog\\/post-1(?:\\/)?$'), source: '/blog/post-1', }, { source: '/test/:path', destination: '/:path', - regex: '^\\/test(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx('^\\/test(?:\\/([^\\/]+?))(?:\\/)?$'), }, { source: '/test-overwrite/:something/:another', destination: '/params/this-should-be-the-value', - regex: - '^\\/test-overwrite(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/test-overwrite(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$' + ), }, { source: '/params/:something', destination: '/with-params', - regex: '^\\/params(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx('^\\/params(?:\\/([^\\/]+?))(?:\\/)?$'), }, { destination: '/with-params?first=:section&second=:name', - regex: - '^\\/query-rewrite(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/query-rewrite(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/query-rewrite/:section/:name', }, { destination: '/_next/:path*', - regex: - '^\\/hidden\\/_next(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/hidden\\/_next(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/hidden/_next/:path*', }, { destination: `http://localhost:${externalServerPort}/:path*`, - regex: - '^\\/proxy-me(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/proxy-me(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/proxy-me/:path*', }, { destination: '/api/hello', - regex: '^\\/api-hello(?:\\/)?$', + regex: normalizeRegEx('^\\/api-hello(?:\\/)?$'), source: '/api-hello', }, { destination: '/api/hello?name=:first*', - regex: '^\\/api-hello-regex(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx('^\\/api-hello-regex(?:\\/(.*))(?:\\/)?$'), source: '/api-hello-regex/:first(.*)', }, { destination: '/api/hello?hello=:name', - regex: '^\\/api-hello-param(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/api-hello-param(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/api-hello-param/:name', }, { destination: '/api/dynamic/:name?hello=:name', - regex: '^\\/api-dynamic-param(?:\\/([^\\/]+?))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/api-dynamic-param(?:\\/([^\\/]+?))(?:\\/)?$' + ), source: '/api-dynamic-param/:name', }, { destination: '/with-params', - regex: '^(?:\\/([^\\/]+?))\\/post-321(?:\\/)?$', + regex: normalizeRegEx('^(?:\\/([^\\/]+?))\\/post-321(?:\\/)?$'), source: '/:path/post-321', }, { destination: '/with-params', - regex: - '^\\/unnamed-params\\/nested(?:\\/(.*))(?:\\/([^\\/]+?))(?:\\/(.*))(?:\\/)?$', + regex: normalizeRegEx( + '^\\/unnamed-params\\/nested(?:\\/(.*))(?:\\/([^\\/]+?))(?:\\/(.*))(?:\\/)?$' + ), source: '/unnamed-params/nested/(.*)/:test/(.*)', }, { destination: '/with-params', - regex: - '^\\/catchall-rewrite(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/catchall-rewrite(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/catchall-rewrite/:path*', }, { destination: '/with-params?another=:path*', - regex: - '^\\/catchall-query(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', + regex: normalizeRegEx( + '^\\/catchall-query(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$' + ), source: '/catchall-query/:path*', }, { @@ -2251,7 +2300,7 @@ const runTests = (isDev = false) => { value: '(?.*)', }, ], - regex: '^\\/has-rewrite-1(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-1(?:\\/)?$'), source: '/has-rewrite-1', }, { @@ -2262,7 +2311,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/has-rewrite-2(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-2(?:\\/)?$'), source: '/has-rewrite-2', }, { @@ -2274,7 +2323,7 @@ const runTests = (isDev = false) => { value: '(?true)', }, ], - regex: '^\\/has-rewrite-3(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-3(?:\\/)?$'), source: '/has-rewrite-3', }, { @@ -2285,7 +2334,7 @@ const runTests = (isDev = false) => { value: 'example.com', }, ], - regex: '^\\/has-rewrite-4(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-4(?:\\/)?$'), source: '/has-rewrite-4', }, { @@ -2296,7 +2345,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/has-rewrite-5(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-5(?:\\/)?$'), source: '/has-rewrite-5', }, { @@ -2308,7 +2357,7 @@ const runTests = (isDev = false) => { value: 'with-params', }, ], - regex: '^\\/has-rewrite-6(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-6(?:\\/)?$'), source: '/has-rewrite-6', }, { @@ -2320,7 +2369,7 @@ const runTests = (isDev = false) => { value: '(?with-params|hello)', }, ], - regex: '^\\/has-rewrite-7(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-7(?:\\/)?$'), source: '/has-rewrite-7', }, { @@ -2331,7 +2380,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/has-rewrite-8(?:\\/)?$', + regex: normalizeRegEx('^\\/has-rewrite-8(?:\\/)?$'), source: '/has-rewrite-8', }, { @@ -2343,7 +2392,7 @@ const runTests = (isDev = false) => { value: '(?.*)', }, ], - regex: '^\\/missing-rewrite-1(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-rewrite-1(?:\\/)?$'), source: '/missing-rewrite-1', }, { @@ -2354,7 +2403,7 @@ const runTests = (isDev = false) => { type: 'query', }, ], - regex: '^\\/missing-rewrite-2(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-rewrite-2(?:\\/)?$'), source: '/missing-rewrite-2', }, { @@ -2366,12 +2415,12 @@ const runTests = (isDev = false) => { value: '(?true)', }, ], - regex: '^\\/missing-rewrite-3(?:\\/)?$', + regex: normalizeRegEx('^\\/missing-rewrite-3(?:\\/)?$'), source: '/missing-rewrite-3', }, { destination: '/hello', - regex: '^\\/blog\\/about(?:\\/)?$', + regex: normalizeRegEx('^\\/blog\\/about(?:\\/)?$'), source: '/blog/about', }, { @@ -2380,14 +2429,14 @@ const runTests = (isDev = false) => { '^\\/overridden(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$', source: '/overridden/:path*', }, - ].map((item) => normalizeRouteRegExes(item)), + ], fallback: [], }, dynamicRoutes: [ { namedRegex: '^/_sport/(?[^/]+?)(?:/)?$', page: '/_sport/[slug]', - regex: '^\\/_sport\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/_sport\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -2395,7 +2444,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/_sport/(?[^/]+?)/test(?:/)?$', page: '/_sport/[slug]/test', - regex: '^\\/_sport\\/([^\\/]+?)\\/test(?:\\/)?$', + regex: normalizeRegEx('^\\/_sport\\/([^\\/]+?)\\/test(?:\\/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -2403,7 +2452,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/another/(?[^/]+?)(?:/)?$', page: '/another/[id]', - regex: '^\\/another\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/another\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPid: 'nxtPid', }, @@ -2411,7 +2460,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/api/dynamic/(?[^/]+?)(?:/)?$', page: '/api/dynamic/[slug]', - regex: '^\\/api\\/dynamic\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/api\\/dynamic\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -2419,7 +2468,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/auto\\-export/(?[^/]+?)(?:/)?$', page: '/auto-export/[slug]', - regex: '^\\/auto\\-export\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/auto\\-export\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -2427,7 +2476,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/blog/(?[^/]+?)(?:/)?$', page: '/blog/[post]', - regex: '^\\/blog\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPpost: 'nxtPpost', }, @@ -2435,7 +2484,7 @@ const runTests = (isDev = false) => { { namedRegex: '^/blog\\-catchall/(?.+?)(?:/)?$', page: '/blog-catchall/[...slug]', - regex: '^\\/blog\\-catchall\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/blog\\-catchall\\/(.+?)(?:\\/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -2448,7 +2497,7 @@ const runTests = (isDev = false) => { nxtPslug: 'nxtPslug', }, }, - ].map((item) => normalizeRouteRegExes(item)), + ], staticRoutes: [ { namedRegex: '^/auto\\-export/another(?:/)?$', @@ -2504,7 +2553,7 @@ const runTests = (isDev = false) => { regex: '^/with\\-params(?:/)?$', routeKeys: {}, }, - ].map((item) => normalizeRouteRegExes(item)), + ], rsc: { header: 'RSC', contentTypeHeader: 'text/x-component', diff --git a/test/integration/dynamic-routing/pages/[name]/[comment]/[...rest].js b/test/integration/dynamic-routing/pages/[name]/[comment]/[...rest].js index 5478de48085fb..74e30e4905d55 100644 --- a/test/integration/dynamic-routing/pages/[name]/[comment]/[...rest].js +++ b/test/integration/dynamic-routing/pages/[name]/[comment]/[...rest].js @@ -1,15 +1,7 @@ // this checks priority issues with catch-all routes that // can match `_next/data/build-id/path.json -export function getServerSideProps({ req }) { - if (req.url.startsWith('/_next/static')) { - return { - props: { - now: Date.now(), - }, - } - } - +export function getServerSideProps() { return { notFound: true, } diff --git a/test/integration/dynamic-routing/test/index.test.js b/test/integration/dynamic-routing/test/index.test.js index 3399553c7c02d..2e5d1bfd056ef 100644 --- a/test/integration/dynamic-routing/test/index.test.js +++ b/test/integration/dynamic-routing/test/index.test.js @@ -13,10 +13,10 @@ import { waitFor, nextBuild, nextStart, + normalizeRegEx, check, hasRedbox, getRedboxHeader, - normalizeRouteRegExes, } from 'next-test-utils' import cheerio from 'cheerio' import escapeRegex from 'escape-string-regexp' @@ -28,13 +28,6 @@ const appDir = join(__dirname, '../') const buildIdPath = join(appDir, '.next/BUILD_ID') function runTests({ dev }) { - it('should not match dynamic route for non-existent _next/static asset', async () => { - const res = await fetchViaHTTP(appPort, '/_next/static/non-existent') - - expect(res.status).toBe(404) - expect(await res.text()).not.toContain('nested catch-all') - }) - if (!dev) { it('should have correct cache entries on prefetch', async () => { const browser = await webdriver(appPort, '/') @@ -1241,15 +1234,21 @@ function runTests({ dev }) { join(appDir, '.next/routes-manifest.json') ) - manifest.staticRoutes = manifest.staticRoutes.map((item) => - normalizeRouteRegExes(item) - ) - manifest.dynamicRoutes = manifest.dynamicRoutes.map((item) => - normalizeRouteRegExes(item) - ) - manifest.dataRoutes = manifest.dataRoutes.map((item) => - normalizeRouteRegExes(item) - ) + for (const route of manifest.dynamicRoutes) { + route.regex = normalizeRegEx(route.regex) + + // ensure regexes are valid + new RegExp(route.regex) + new RegExp(route.namedRegex) + } + + for (const route of manifest.dataRoutes) { + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) + + // ensure regexes are valid + new RegExp(route.dataRouteRegex) + new RegExp(route.namedDataRouteRegex) + } expect(manifest).toEqual({ version: 3, @@ -1271,7 +1270,7 @@ function runTests({ dev }) { regex: '^/another(?:/)?$', routeKeys: {}, }, - ].map((item) => normalizeRouteRegExes(item)), + ], redirects: expect.arrayContaining([]), dataRoutes: [ { @@ -1302,9 +1301,11 @@ function runTests({ dev }) { namedDataRouteRegex: `^/_next/data/${escapeRegex( buildId )}/p1/p2/all\\-ssg/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/p1\\/p2\\/all\\-ssg\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/all\\-ssg\\/(.+?)\\.json$` + ), page: '/p1/p2/all-ssg/[...rest]', routeKeys: { nxtPrest: 'nxtPrest', @@ -1314,9 +1315,11 @@ function runTests({ dev }) { namedDataRouteRegex: `^/_next/data/${escapeRegex( buildId )}/p1/p2/nested\\-all\\-ssg/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)\\.json$` + ), page: '/p1/p2/nested-all-ssg/[...rest]', routeKeys: { nxtPrest: 'nxtPrest', @@ -1326,18 +1329,22 @@ function runTests({ dev }) { namedDataRouteRegex: `^/_next/data/${escapeRegex( buildId )}/p1/p2/predefined\\-ssg/(?.+?)\\.json$`, - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)\\.json$` + ), page: '/p1/p2/predefined-ssg/[...rest]', routeKeys: { nxtPrest: 'nxtPrest', }, }, { - dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/([^\\/]+?)\\/([^\\/]+?)\\/(.+?)\\.json$`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/([^\\/]+?)\\/([^\\/]+?)\\/(.+?)\\.json$` + ), namedDataRouteRegex: `^/_next/data/${escapeRegex( buildId )}/(?[^/]+?)/(?[^/]+?)/(?.+?)\\.json$`, @@ -1348,12 +1355,12 @@ function runTests({ dev }) { nxtPrest: 'nxtPrest', }, }, - ].map((item) => normalizeRouteRegExes(item)), + ], dynamicRoutes: [ { namedRegex: '^/b/(?[^/]+?)(?:/)?$', page: '/b/[123]', - regex: '^\\/b\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/b\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtP123: 'nxtP123', }, @@ -1361,7 +1368,9 @@ function runTests({ dev }) { { namedRegex: `^/blog/(?[^/]+?)/comment/(?[^/]+?)(?:/)?$`, page: '/blog/[name]/comment/[id]', - regex: '^\\/blog\\/([^\\/]+?)\\/comment\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx( + '^\\/blog\\/([^\\/]+?)\\/comment\\/([^\\/]+?)(?:\\/)?$' + ), routeKeys: { nxtPname: 'nxtPname', nxtPid: 'nxtPid', @@ -1370,7 +1379,7 @@ function runTests({ dev }) { { namedRegex: '^/c/(?[^/]+?)(?:/)?$', page: '/c/[alongparamnameshouldbeallowedeventhoughweird]', - regex: '^\\/c\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/c\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { a: 'nxtPalongparamnameshouldbeallowedeventhoughweird', }, @@ -1378,7 +1387,7 @@ function runTests({ dev }) { { namedRegex: '^/catchall\\-dash/(?.+?)(?:/)?$', page: '/catchall-dash/[...hello-world]', - regex: '^\\/catchall\\-dash\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/catchall\\-dash\\/(.+?)(?:\\/)?$'), routeKeys: { nxtPhelloworld: 'nxtPhello-world', }, @@ -1386,7 +1395,7 @@ function runTests({ dev }) { { namedRegex: '^/d/(?[^/]+?)(?:/)?$', page: '/d/[id]', - regex: '^\\/d\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/d\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPid: 'nxtPid', }, @@ -1394,7 +1403,7 @@ function runTests({ dev }) { { namedRegex: '^/dash/(?[^/]+?)(?:/)?$', page: '/dash/[hello-world]', - regex: '^\\/dash\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/dash\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPhelloworld: 'nxtPhello-world', }, @@ -1402,7 +1411,7 @@ function runTests({ dev }) { { namedRegex: '^/index/(?.+?)(?:/)?$', page: '/index/[...slug]', - regex: '^/index/(.+?)(?:/)?$', + regex: normalizeRegEx('^/index/(.+?)(?:/)?$'), routeKeys: { nxtPslug: 'nxtPslug', }, @@ -1410,7 +1419,7 @@ function runTests({ dev }) { { namedRegex: `^/on\\-mount/(?[^/]+?)(?:/)?$`, page: '/on-mount/[post]', - regex: '^\\/on\\-mount\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/on\\-mount\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPpost: 'nxtPpost', }, @@ -1418,7 +1427,7 @@ function runTests({ dev }) { { namedRegex: `^/p1/p2/all\\-ssg/(?.+?)(?:/)?$`, page: '/p1/p2/all-ssg/[...rest]', - regex: '^\\/p1\\/p2\\/all\\-ssg\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssg\\/(.+?)(?:\\/)?$'), routeKeys: { nxtPrest: 'nxtPrest', }, @@ -1426,7 +1435,7 @@ function runTests({ dev }) { { namedRegex: `^/p1/p2/all\\-ssr/(?.+?)(?:/)?$`, page: '/p1/p2/all-ssr/[...rest]', - regex: '^\\/p1\\/p2\\/all\\-ssr\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssr\\/(.+?)(?:\\/)?$'), routeKeys: { nxtPrest: 'nxtPrest', }, @@ -1434,7 +1443,9 @@ function runTests({ dev }) { { namedRegex: `^/p1/p2/nested\\-all\\-ssg/(?.+?)(?:/)?$`, page: '/p1/p2/nested-all-ssg/[...rest]', - regex: '^\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx( + '^\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)(?:\\/)?$' + ), routeKeys: { nxtPrest: 'nxtPrest', }, @@ -1442,7 +1453,9 @@ function runTests({ dev }) { { namedRegex: `^/p1/p2/predefined\\-ssg/(?.+?)(?:/)?$`, page: '/p1/p2/predefined-ssg/[...rest]', - regex: '^\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx( + '^\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)(?:\\/)?$' + ), routeKeys: { nxtPrest: 'nxtPrest', }, @@ -1450,7 +1463,7 @@ function runTests({ dev }) { { namedRegex: `^/(?[^/]+?)(?:/)?$`, page: '/[name]', - regex: '^\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPname: 'nxtPname', }, @@ -1458,7 +1471,7 @@ function runTests({ dev }) { { namedRegex: `^/(?[^/]+?)/comments(?:/)?$`, page: '/[name]/comments', - regex: '^\\/([^\\/]+?)\\/comments(?:\\/)?$', + regex: normalizeRegEx('^\\/([^\\/]+?)\\/comments(?:\\/)?$'), routeKeys: { nxtPname: 'nxtPname', }, @@ -1466,7 +1479,9 @@ function runTests({ dev }) { { namedRegex: `^/(?[^/]+?)/on\\-mount\\-redir(?:/)?$`, page: '/[name]/on-mount-redir', - regex: '^\\/([^\\/]+?)\\/on\\-mount\\-redir(?:\\/)?$', + regex: normalizeRegEx( + '^\\/([^\\/]+?)\\/on\\-mount\\-redir(?:\\/)?$' + ), routeKeys: { nxtPname: 'nxtPname', }, @@ -1474,7 +1489,7 @@ function runTests({ dev }) { { namedRegex: `^/(?[^/]+?)/(?[^/]+?)(?:/)?$`, page: '/[name]/[comment]', - regex: '^\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$', + regex: normalizeRegEx('^\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'), routeKeys: { nxtPname: 'nxtPname', nxtPcomment: 'nxtPcomment', @@ -1484,14 +1499,16 @@ function runTests({ dev }) { namedRegex: '^/(?[^/]+?)/(?[^/]+?)/(?.+?)(?:/)?$', page: '/[name]/[comment]/[...rest]', - regex: '^\\/([^\\/]+?)\\/([^\\/]+?)\\/(.+?)(?:\\/)?$', + regex: normalizeRegEx( + '^\\/([^\\/]+?)\\/([^\\/]+?)\\/(.+?)(?:\\/)?$' + ), routeKeys: { nxtPcomment: 'nxtPcomment', nxtPname: 'nxtPname', nxtPrest: 'nxtPrest', }, }, - ].map((item) => normalizeRouteRegExes(item)), + ], rsc: { header: 'RSC', contentTypeHeader: 'text/x-component', diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index f9b65575e9a7c..f9a9242f5d3e3 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -12,8 +12,8 @@ import { fetchViaHTTP, renderViaHTTP, waitFor, + normalizeRegEx, check, - normalizeRouteRegExes, } from 'next-test-utils' const domainLocales = ['go', 'go-BE', 'do', 'do-BE'] @@ -767,7 +767,9 @@ export function runTests(ctx) { for (const key of Object.keys(prerenderManifest.dynamicRoutes).sort()) { const item = prerenderManifest.dynamicRoutes[key] - dynamicRoutes[key] = normalizeRouteRegExes(item) + item.routeRegex = normalizeRegEx(item.routeRegex) + item.dataRouteRegex = normalizeRegEx(item.dataRouteRegex) + dynamicRoutes[key] = item } expect( @@ -1165,25 +1167,25 @@ export function runTests(ctx) { ).toMatchInlineSnapshot(` "{ "/gsp/fallback/[slug]": { - "routeRegex": "^\\/gsp\\/fallback\\/([^\\/]+?)?$", + "routeRegex": "^\\/gsp\\/fallback\\/([^\\/]+?)(?:\\/)?$", "dataRoute": "/_next/data/BUILD_ID/gsp/fallback/[slug].json", "fallback": "/gsp/fallback/[slug].html", "dataRouteRegex": "^\\/_next\\/data\\/BUILD_ID\\/gsp\\/fallback\\/([^\\/]+?)\\.json$" }, "/gsp/no-fallback/[slug]": { - "routeRegex": "^\\/gsp\\/no\\-fallback\\/([^\\/]+?)?$", + "routeRegex": "^\\/gsp\\/no\\-fallback\\/([^\\/]+?)(?:\\/)?$", "dataRoute": "/_next/data/BUILD_ID/gsp/no-fallback/[slug].json", "fallback": false, "dataRouteRegex": "^\\/_next\\/data\\/BUILD_ID\\/gsp\\/no\\-fallback\\/([^\\/]+?)\\.json$" }, "/not-found/blocking-fallback/[slug]": { - "routeRegex": "^\\/not\\-found\\/blocking\\-fallback\\/([^\\/]+?)?$", + "routeRegex": "^\\/not\\-found\\/blocking\\-fallback\\/([^\\/]+?)(?:\\/)?$", "dataRoute": "/_next/data/BUILD_ID/not-found/blocking-fallback/[slug].json", "fallback": null, "dataRouteRegex": "^\\/_next\\/data\\/BUILD_ID\\/not\\-found\\/blocking\\-fallback\\/([^\\/]+?)\\.json$" }, "/not-found/fallback/[slug]": { - "routeRegex": "^\\/not\\-found\\/fallback\\/([^\\/]+?)?$", + "routeRegex": "^\\/not\\-found\\/fallback\\/([^\\/]+?)(?:\\/)?$", "dataRoute": "/_next/data/BUILD_ID/not-found/fallback/[slug].json", "fallback": "/not-found/fallback/[slug].html", "dataRouteRegex": "^\\/_next\\/data\\/BUILD_ID\\/not\\-found\\/fallback\\/([^\\/]+?)\\.json$" diff --git a/test/lib/next-test-utils.ts b/test/lib/next-test-utils.ts index 03ecefa285324..b458c8364cd39 100644 --- a/test/lib/next-test-utils.ts +++ b/test/lib/next-test-utils.ts @@ -860,32 +860,7 @@ export function getBrowserBodyText(browser: BrowserInterface) { } export function normalizeRegEx(src: string) { - return ( - new RegExp(src).source - .replace(/\^\//g, '^\\/') - // normalize our ignores at the top-level so each - // snapshot doesn't need to be updated each time - .replace('(?!\\/_next\\/static)', '') - .replace('?(?:\\/)?$', '?$') - .replace('(?:\\/)?$', '?$') - ) -} - -export const normalizeRouteRegExes = (item: any) => { - const fields = [ - 'regex', - 'routeRegex', - 'namedRegex', - 'namedDataRouteRegex', - 'dataRouteRegex', - ] - - for (const field of fields) { - if (typeof item[field] === 'string') { - item[field] = normalizeRegEx(item[field]) - } - } - return item + return new RegExp(src).source.replace(/\^\//g, '^\\/') } function readJson(path: string) {