Add namedRegex and routeKeys to routes manifest (#12250)
This commit is contained in:
parent
af33ebc35d
commit
52686690a9
7 changed files with 154 additions and 24 deletions
|
@ -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 => ({
|
||||
page,
|
||||
regex: getRouteRegex(page).re.source,
|
||||
})),
|
||||
dynamicRoutes: getSortedRoutes(dynamicRoutes).map(page => {
|
||||
const routeRegex = getRouteRegex(page)
|
||||
return {
|
||||
page,
|
||||
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`
|
||||
)
|
||||
|
||||
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$'
|
||||
)
|
||||
namedDataRouteRegex = routeRegex.namedRegex!.replace(
|
||||
/\(\?:\/\)\?\$$/,
|
||||
'\\.json$'
|
||||
)
|
||||
routeKeys = Object.keys(routeRegex.groups)
|
||||
} else {
|
||||
dataRouteRegex = new RegExp(
|
||||
`^${path.posix.join(
|
||||
'/_next/data',
|
||||
escapeStringRegexp(buildId),
|
||||
`${pagePath}.json`
|
||||
)}$`
|
||||
).source
|
||||
}
|
||||
|
||||
return {
|
||||
page,
|
||||
dataRouteRegex: isDynamicRoute(page)
|
||||
? getRouteRegex(dataRoute.replace(/\.json$/, '')).re.source.replace(
|
||||
/\(\?:\\\/\)\?\$$/,
|
||||
'\\.json$'
|
||||
)
|
||||
: new RegExp(
|
||||
`^${path.posix.join(
|
||||
'/_next/data',
|
||||
escapeStringRegexp(buildId),
|
||||
`${pagePath}.json`
|
||||
)}$`
|
||||
).source,
|
||||
routeKeys,
|
||||
dataRouteRegex,
|
||||
namedDataRouteRegex,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -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}(?:/)?$`,
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue