feat(pages)!: explicitly allow optional params with `[[` (#4537)

This commit is contained in:
Daniel Roe 2022-04-26 17:10:05 +01:00 committed by GitHub
parent d2632e8d7c
commit 2bfd749c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 22 deletions

View File

@ -95,6 +95,8 @@ Here are some examples to illustrate what a page with a single root element look
If you place anything within square brackets, it will be turned into a [dynamic route](https://router.vuejs.org/guide/essentials/dynamic-matching.html) parameter. You can mix and match multiple parameters and even non-dynamic text within a file name or directory.
If you want a parameter to be _optional_, you must enclose it in double square brackets - for example, `~/pages/[[slug]]/index.vue` or `~/pages/[[slug]].vue` will match both `/` and `/test`.
### Example
```bash

View File

@ -10,12 +10,14 @@ enum SegmentParserState {
initial,
static,
dynamic,
optional,
catchall,
}
enum SegmentTokenType {
static,
dynamic,
optional,
catchall,
}
@ -67,7 +69,6 @@ export function generateRoutesFromFiles (files: string[], pagesDir: string): Nux
const tokens = parseSegment(segment)
const segmentName = tokens.map(({ value }) => value).join('')
const isSingleSegment = segments.length === 1
const isLastSegment = i === segments.length - 1
// ex: parent/[slug].vue -> parent-slug
route.name += (route.name && '-') + segmentName
@ -83,9 +84,6 @@ export function generateRoutesFromFiles (files: string[], pagesDir: string): Nux
route.path += '/'
} else if (segmentName !== 'index') {
route.path += getRoutePath(tokens)
if (isLastSegment && tokens.length === 1 && tokens[0].type === SegmentTokenType.dynamic) {
route.path += '?'
}
}
}
@ -99,11 +97,13 @@ function getRoutePath (tokens: SegmentToken[]): string {
return tokens.reduce((path, token) => {
return (
path +
(token.type === SegmentTokenType.dynamic
? `:${token.value}`
: token.type === SegmentTokenType.catchall
? `:${token.value}(.*)*`
: encodePath(token.value))
(token.type === SegmentTokenType.optional
? `:${token.value}?`
: token.type === SegmentTokenType.dynamic
? `:${token.value}`
: token.type === SegmentTokenType.catchall
? `:${token.value}(.*)*`
: encodePath(token.value))
)
}, '/')
}
@ -131,7 +131,9 @@ function parseSegment (segment: string) {
? SegmentTokenType.static
: state === SegmentParserState.dynamic
? SegmentTokenType.dynamic
: SegmentTokenType.catchall,
: state === SegmentParserState.optional
? SegmentTokenType.optional
: SegmentTokenType.catchall,
value: buffer
})
@ -163,11 +165,15 @@ function parseSegment (segment: string) {
case SegmentParserState.catchall:
case SegmentParserState.dynamic:
case SegmentParserState.optional:
if (buffer === '...') {
buffer = ''
state = SegmentParserState.catchall
}
if (c === ']') {
if (c === '[' && state === SegmentParserState.dynamic) {
state = SegmentParserState.optional
}
if (c === ']' && (state !== SegmentParserState.optional || buffer[buffer.length - 1] === ']')) {
if (!buffer) {
throw new Error('Empty param')
} else {

View File

@ -97,26 +97,48 @@ describe('pages:generateRoutesFromFiles', () => {
description: 'should generate correct dynamic routes',
files: [
`${pagesDir}/[slug].vue`,
`${pagesDir}/[[foo]]`,
`${pagesDir}/[[foo]]/index.vue`,
`${pagesDir}/[bar]/index.vue`,
`${pagesDir}/sub/[slug].vue`,
`${pagesDir}/[sub]/route-[slug].vue`
`${pagesDir}/[[sub]]/route-[slug].vue`
],
output: [
{
name: 'slug',
path: '/:slug?',
path: '/:slug',
file: `${pagesDir}/[slug].vue`,
children: []
},
{
children: [
{
name: 'foo',
path: '',
file: `${pagesDir}/[[foo]]/index.vue`,
children: []
}
],
file: 'pages/[[foo]]',
path: '/:foo?'
},
{
children: [],
name: 'bar',
file: 'pages/[bar]/index.vue',
path: '/:bar'
},
{
name: 'sub-slug',
path: '/sub/:slug?',
path: '/sub/:slug',
file: `${pagesDir}/sub/[slug].vue`,
children: []
},
{
name: 'sub-route-slug',
path: '/:sub/route-:slug',
file: `${pagesDir}/[sub]/route-[slug].vue`,
path: '/:sub?/route-:slug',
file: `${pagesDir}/[[sub]]/route-[slug].vue`,
children: []
}
]
@ -150,32 +172,32 @@ describe('pages:generateRoutesFromFiles', () => {
files: [
`${pagesDir}/[a1_1a].vue`,
`${pagesDir}/[b2.2b].vue`,
`${pagesDir}/[c3@3c].vue`,
`${pagesDir}/[d4-4d].vue`
`${pagesDir}/[[c3@3c]].vue`,
`${pagesDir}/[[d4-4d]].vue`
],
output: [
{
name: 'a1_1a',
path: '/:a1_1a?',
path: '/:a1_1a',
file: `${pagesDir}/[a1_1a].vue`,
children: []
},
{
name: 'b2.2b',
path: '/:b2.2b?',
path: '/:b2.2b',
file: `${pagesDir}/[b2.2b].vue`,
children: []
},
{
name: 'c33c',
path: '/:c33c?',
file: `${pagesDir}/[c3@3c].vue`,
file: `${pagesDir}/[[c3@3c]].vue`,
children: []
},
{
name: 'd44d',
path: '/:d44d?',
file: `${pagesDir}/[d4-4d].vue`,
file: `${pagesDir}/[[d4-4d]].vue`,
children: []
}
]