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 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 ### Example
```bash ```bash

View File

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

View File

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