mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 21:55:11 +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 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
|
||||||
|
@ -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 {
|
||||||
|
@ -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: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user