mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat(pages)!: explicitly allow optional params with [[
(#4537)
This commit is contained in:
parent
d2632e8d7c
commit
2bfd749c24
@ -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
|
||||
|
@ -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,7 +97,9 @@ function getRoutePath (tokens: SegmentToken[]): string {
|
||||
return tokens.reduce((path, token) => {
|
||||
return (
|
||||
path +
|
||||
(token.type === SegmentTokenType.dynamic
|
||||
(token.type === SegmentTokenType.optional
|
||||
? `:${token.value}?`
|
||||
: token.type === SegmentTokenType.dynamic
|
||||
? `:${token.value}`
|
||||
: token.type === SegmentTokenType.catchall
|
||||
? `:${token.value}(.*)*`
|
||||
@ -131,6 +131,8 @@ function parseSegment (segment: string) {
|
||||
? SegmentTokenType.static
|
||||
: state === SegmentParserState.dynamic
|
||||
? SegmentTokenType.dynamic
|
||||
: 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 {
|
||||
|
@ -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: []
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user