fix(nuxt): use more performant router catchall pattern (#31450)

This commit is contained in:
Daniel Roe 2025-03-19 15:05:03 +00:00 committed by GitHub
parent e315a8465f
commit 726c029b33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 42 additions and 4 deletions

2
.gitignore vendored
View File

@ -80,3 +80,5 @@ eslint-typegen.d.ts
.eslintcache .eslintcache
test-results/ test-results/
playwright-report playwright-report
temp

View File

@ -137,7 +137,8 @@ export function generateRoutesFromFiles (files: ScannedFile[], options: Generate
route.name += (route.name && '/') + segmentName route.name += (route.name && '/') + segmentName
// ex: parent.vue + parent/child.vue // ex: parent.vue + parent/child.vue
const path = withLeadingSlash(joinURL(route.path, getRoutePath(tokens).replace(INDEX_PAGE_RE, '/'))) const routePath = getRoutePath(tokens, segments[i + 1] !== undefined)
const path = withLeadingSlash(joinURL(route.path, routePath.replace(INDEX_PAGE_RE, '/')))
const child = parent.find(parentRoute => parentRoute.name === route.name && parentRoute.path === path) const child = parent.find(parentRoute => parentRoute.name === route.name && parentRoute.path === path)
if (child && child.children) { if (child && child.children) {
@ -146,7 +147,7 @@ export function generateRoutesFromFiles (files: ScannedFile[], options: Generate
} else if (segmentName === 'index' && !route.path) { } else if (segmentName === 'index' && !route.path) {
route.path += '/' route.path += '/'
} else if (segmentName !== 'index') { } else if (segmentName !== 'index') {
route.path += getRoutePath(tokens) route.path += routePath
} }
} }
@ -319,7 +320,7 @@ export function getRouteMeta (contents: string, absolutePath: string, extraExtra
} }
const COLON_RE = /:/g const COLON_RE = /:/g
function getRoutePath (tokens: SegmentToken[]): string { function getRoutePath (tokens: SegmentToken[], hasSucceedingSegment = false): string {
return tokens.reduce((path, token) => { return tokens.reduce((path, token) => {
return ( return (
path + path +
@ -328,7 +329,7 @@ function getRoutePath (tokens: SegmentToken[]): string {
: token.type === SegmentTokenType.dynamic : token.type === SegmentTokenType.dynamic
? `:${token.value}()` ? `:${token.value}()`
: token.type === SegmentTokenType.catchall : token.type === SegmentTokenType.catchall
? `:${token.value}(.*)*` ? hasSucceedingSegment ? `:${token.value}([^/]*)*` : `:${token.value}(.*)*`
: token.type === SegmentTokenType.group : token.type === SegmentTokenType.group
? '' ? ''
: encodePath(token.value).replace(COLON_RE, '\\:')) : encodePath(token.value).replace(COLON_RE, '\\:'))

View File

@ -560,4 +560,15 @@
"redirect": "mockMeta?.redirect", "redirect": "mockMeta?.redirect",
}, },
], ],
"should use more performant regexp when catchall is used in middle of path": [
{
"alias": "mockMeta?.alias || []",
"component": "() => import("pages/[...id]/suffix.vue")",
"meta": "mockMeta || {}",
"name": "mockMeta?.name ?? "id-suffix"",
"path": "mockMeta?.path ?? "/:id([^/]*)*/suffix"",
"props": "mockMeta?.props ?? false",
"redirect": "mockMeta?.redirect",
},
],
} }

View File

@ -351,4 +351,11 @@
"redirect": "mockMeta?.redirect", "redirect": "mockMeta?.redirect",
}, },
], ],
"should use more performant regexp when catchall is used in middle of path": [
{
"component": "() => import("pages/[...id]/suffix.vue")",
"name": ""id-suffix"",
"path": ""/:id([^/]*)*/suffix"",
},
],
} }

View File

@ -574,6 +574,23 @@ describe('pages:generateRoutesFromFiles', () => {
}, },
], ],
}, },
{
description: 'should use more performant regexp when catchall is used in middle of path',
files: [
{
path: `${pagesDir}/[...id]/suffix.vue`,
},
],
output: [
{
name: 'id-suffix',
meta: undefined,
path: '/:id([^/]*)*/suffix',
file: `${pagesDir}/[...id]/suffix.vue`,
children: [],
},
],
},
{ {
description: 'should merge route.meta with meta from file', description: 'should merge route.meta with meta from file',
files: [ files: [