Add namedRegex and routeKeys to routes manifest (#12250)

This commit is contained in:
JJ Kasper 2020-04-28 02:59:47 -05:00 committed by GitHub
parent af33ebc35d
commit 52686690a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 24 deletions

View file

@ -299,10 +299,15 @@ export default async function build(dir: string, conf = null): Promise<void> {
redirects: redirects.map(r => buildCustomRoute(r, 'redirect')),
rewrites: rewrites.map(r => buildCustomRoute(r, 'rewrite')),
headers: headers.map(r => buildCustomRoute(r, 'header')),
dynamicRoutes: getSortedRoutes(dynamicRoutes).map(page => ({
dynamicRoutes: getSortedRoutes(dynamicRoutes).map(page => {
const routeRegex = getRouteRegex(page)
return {
page,
regex: getRouteRegex(page).re.source,
})),
regex: routeRegex.re.source,
namedRegex: routeRegex.namedRegex,
routeKeys: Object.keys(routeRegex.groups),
}
}),
}
await mkdir(distDir, { recursive: true })
@ -633,20 +638,37 @@ export default async function build(dir: string, conf = null): Promise<void> {
`${pagePath}.json`
)
return {
page,
dataRouteRegex: isDynamicRoute(page)
? getRouteRegex(dataRoute.replace(/\.json$/, '')).re.source.replace(
let dataRouteRegex: string
let routeKeys: string[] | undefined
let namedDataRouteRegex: string | undefined
if (isDynamicRoute(page)) {
const routeRegex = getRouteRegex(dataRoute.replace(/\.json$/, ''))
dataRouteRegex = routeRegex.re.source.replace(
/\(\?:\\\/\)\?\$$/,
'\\.json$'
)
: new RegExp(
namedDataRouteRegex = routeRegex.namedRegex!.replace(
/\(\?:\/\)\?\$$/,
'\\.json$'
)
routeKeys = Object.keys(routeRegex.groups)
} else {
dataRouteRegex = new RegExp(
`^${path.posix.join(
'/_next/data',
escapeStringRegexp(buildId),
`${pagePath}.json`
)}$`
).source,
).source
}
return {
page,
routeKeys,
dataRouteRegex,
namedDataRouteRegex,
}
})

View file

@ -1,14 +1,18 @@
// this isn't importing the escape-string-regex module
// to reduce bytes
function escapeRegex(str: string) {
return str.replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&')
}
export function getRouteRegex(
normalizedRoute: string
): {
re: RegExp
namedRegex?: string
groups: { [groupName: string]: { pos: number; repeat: boolean } }
} {
// Escape all characters that could be considered RegEx
const escapedRoute = (normalizedRoute.replace(/\/$/, '') || '/').replace(
/[|\\{}()[\]^$+*?.-]/g,
'\\$&'
)
const escapedRoute = escapeRegex(normalizedRoute.replace(/\/$/, '') || '/')
const groups: { [groupName: string]: { pos: number; repeat: boolean } } = {}
let groupIndex = 1
@ -28,8 +32,34 @@ export function getRouteRegex(
}
)
let namedParameterizedRoute: string | undefined
// dead code eliminate for browser since it's only needed
// while generating routes-manifest
if (typeof window === 'undefined') {
namedParameterizedRoute = escapedRoute.replace(
/\/\\\[([^/]+?)\\\](?=\/|$)/g,
(_, $1) => {
const isCatchAll = /^(\\\.){3}/.test($1)
const key = $1
// Un-escape key
.replace(/\\([|\\{}()[\]^$+*?.-])/g, '$1')
.replace(/^\.{3}/, '')
return isCatchAll
? `/(?<${escapeRegex(key)}>.+?)`
: `/(?<${escapeRegex(key)}>[^/]+?)`
}
)
}
return {
re: new RegExp('^' + parameterizedRoute + '(?:/)?$', 'i'),
groups,
...(namedParameterizedRoute
? {
namedRegex: `^${namedParameterizedRoute}(?:/)?$`,
}
: {}),
}
}

View file

@ -773,16 +773,22 @@ const runTests = (isDev = false) => {
],
dynamicRoutes: [
{
namedRegex: '^/another/(?<id>[^/]+?)(?:/)?$',
page: '/another/[id]',
regex: normalizeRegEx('^\\/another\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['id'],
},
{
namedRegex: '^/api/dynamic/(?<slug>[^/]+?)(?:/)?$',
page: '/api/dynamic/[slug]',
regex: normalizeRegEx('^\\/api\\/dynamic\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['slug'],
},
{
namedRegex: '^/blog/(?<post>[^/]+?)(?:/)?$',
page: '/blog/[post]',
regex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['post'],
},
],
})

View file

@ -3,15 +3,15 @@ import Link from 'next/link'
const Page = () => (
<div>
<h3>My blog</h3>
<Link href="/[post]" as="/post-1">
<Link href="/[name]" as="/post-1">
<a id="view-post-1">View post 1</a>
</Link>
<br />
<Link href="/[post]/comments" as="/post-1/comments">
<Link href="/[name]/comments" as="/post-1/comments">
<a id="view-post-1-comments">View post 1 comments</a>
</Link>
<br />
<Link href="/[post]/[comment]" as="/post-1/comment-1">
<Link href="/[name]/[comment]" as="/post-1/comment-1">
<a id="view-post-1-comment-1">View comment 1 on post 1</a>
</Link>
<br />
@ -19,7 +19,7 @@ const Page = () => (
<a id="view-nested-dynamic-cmnt">View comment 123 on blog post 321</a>
</Link>
<br />
<Link href="/[post]?fromHome=true" as="/post-1?fromHome=true">
<Link href="/[name]?fromHome=true" as="/post-1?fromHome=true">
<a id="view-post-1-with-query">View post 1 with query</a>
</Link>
<br />

View file

@ -532,78 +532,110 @@ function runTests(dev) {
redirects: [],
dataRoutes: [
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/all\\-ssg/(?<rest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/p1\\/p2\\/all\\-ssg\\/(.+?)\\.json$`
),
page: '/p1/p2/all-ssg/[...rest]',
routeKeys: ['rest'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/nested\\-all\\-ssg/(?<rest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)\\.json$`
),
page: '/p1/p2/nested-all-ssg/[...rest]',
routeKeys: ['rest'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/p1/p2/predefined\\-ssg/(?<rest>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)\\.json$`
),
page: '/p1/p2/predefined-ssg/[...rest]',
routeKeys: ['rest'],
},
],
dynamicRoutes: [
{
namedRegex: `^/blog/(?<name>[^/]+?)/comment/(?<id>[^/]+?)(?:/)?$`,
page: '/blog/[name]/comment/[id]',
regex: normalizeRegEx(
'^\\/blog\\/([^\\/]+?)\\/comment\\/([^\\/]+?)(?:\\/)?$'
),
routeKeys: ['name', 'id'],
},
{
namedRegex: `^/on\\-mount/(?<post>[^/]+?)(?:/)?$`,
page: '/on-mount/[post]',
regex: normalizeRegEx('^\\/on\\-mount\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['post'],
},
{
namedRegex: `^/p1/p2/all\\-ssg/(?<rest>.+?)(?:/)?$`,
page: '/p1/p2/all-ssg/[...rest]',
regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssg\\/(.+?)(?:\\/)?$'),
routeKeys: ['rest'],
},
{
namedRegex: `^/p1/p2/all\\-ssr/(?<rest>.+?)(?:/)?$`,
page: '/p1/p2/all-ssr/[...rest]',
regex: normalizeRegEx('^\\/p1\\/p2\\/all\\-ssr\\/(.+?)(?:\\/)?$'),
routeKeys: ['rest'],
},
{
namedRegex: `^/p1/p2/nested\\-all\\-ssg/(?<rest>.+?)(?:/)?$`,
page: '/p1/p2/nested-all-ssg/[...rest]',
regex: normalizeRegEx(
'^\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)(?:\\/)?$'
),
routeKeys: ['rest'],
},
{
namedRegex: `^/p1/p2/predefined\\-ssg/(?<rest>.+?)(?:/)?$`,
page: '/p1/p2/predefined-ssg/[...rest]',
regex: normalizeRegEx(
'^\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)(?:\\/)?$'
),
routeKeys: ['rest'],
},
{
namedRegex: `^/(?<name>[^/]+?)(?:/)?$`,
page: '/[name]',
regex: normalizeRegEx('^\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['name'],
},
{
namedRegex: `^/(?<name>[^/]+?)/comments(?:/)?$`,
page: '/[name]/comments',
regex: normalizeRegEx('^\\/([^\\/]+?)\\/comments(?:\\/)?$'),
routeKeys: ['name'],
},
{
namedRegex: `^/(?<name>[^/]+?)/on\\-mount\\-redir(?:/)?$`,
page: '/[name]/on-mount-redir',
regex: normalizeRegEx(
'^\\/([^\\/]+?)\\/on\\-mount\\-redir(?:\\/)?$'
),
routeKeys: ['name'],
},
{
namedRegex: `^/(?<name>[^/]+?)/(?<comment>[^/]+?)(?:/)?$`,
page: '/[name]/[comment]',
regex: normalizeRegEx('^\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'),
routeKeys: ['name', 'comment'],
},
],
})

View file

@ -47,24 +47,36 @@ const expectedManifestRoutes = () => [
page: '/blog',
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog\\/([^\\/]+?)\\.json$`
),
page: '/blog/[post]',
routeKeys: ['post'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)/(?<comment>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`
),
page: '/blog/[post]/[comment]',
routeKeys: ['post', 'comment'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/catchall/(?<path>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/catchall\\/(.+?)\\.json$`
),
page: '/catchall/[...path]',
routeKeys: ['path'],
},
{
dataRouteRegex: normalizeRegEx(
@ -103,12 +115,16 @@ const expectedManifestRoutes = () => [
page: '/something',
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/user/(?<user>[^/]+?)/profile\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/user\\/([^\\/]+?)\\/profile\\.json$`
),
page: '/user/[user]/profile',
routeKeys: ['user'],
},
]

View file

@ -771,36 +771,52 @@ const runTests = (dev = false, looseMode = false) => {
page: '/blog',
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/blog\\/([^\\/]+?)\\.json$`
),
page: '/blog/[post]',
routeKeys: ['post'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/blog/(?<post>[^/]+?)/(?<comment>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`
),
page: '/blog/[post]/[comment]',
routeKeys: ['post', 'comment'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/catchall/(?<slug>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/catchall\\/(.+?)\\.json$`
),
page: '/catchall/[...slug]',
routeKeys: ['slug'],
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/catchall\\-explicit/(?<slug>.+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/catchall\\-explicit\\/(.+?)\\.json$`
),
page: '/catchall-explicit/[...slug]',
routeKeys: ['slug'],
},
{
dataRouteRegex: normalizeRegEx(
@ -811,12 +827,16 @@ const runTests = (dev = false, looseMode = false) => {
page: '/default-revalidate',
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/non\\-json/(?<p>[^/]+?)\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/non\\-json\\/([^\\/]+?)\\.json$`
),
page: '/non-json/[p]',
routeKeys: ['p'],
},
{
dataRouteRegex: normalizeRegEx(
@ -825,12 +845,16 @@ const runTests = (dev = false, looseMode = false) => {
page: '/something',
},
{
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/user/(?<user>[^/]+?)/profile\\.json$`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/user\\/([^\\/]+?)\\/profile\\.json$`
),
page: '/user/[user]/profile',
routeKeys: ['user'],
},
])
})