Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/content/2.guide/3.directory-structure/10.pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 17 additions & 11 deletions packages/nuxt/src/pages/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ enum SegmentParserState {
initial,
static,
dynamic,
optional,
catchall,
}

enum SegmentTokenType {
static,
dynamic,
optional,
catchall,
}

Expand Down Expand Up @@ -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
Expand All @@ -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 += '?'
}
}
}

Expand All @@ -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))
)
}, '/')
}
Expand Down Expand Up @@ -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
})

Expand Down Expand Up @@ -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 {
Expand Down
44 changes: 33 additions & 11 deletions packages/nuxt/test/pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: []
}
]
Expand Down Expand Up @@ -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: []
}
]
Expand Down