Skip to content

Commit

Permalink
fix: handle parent params
Browse files Browse the repository at this point in the history
Fix #20
  • Loading branch information
posva committed Jul 6, 2022
1 parent d41b538 commit ef078b0
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 7 deletions.
4 changes: 3 additions & 1 deletion playground/src/App.vue
Expand Up @@ -5,13 +5,15 @@ const route = useRoute()
if (route.name === '/deep/nesting/works/[[files]]+') {
route.params.files
}
const customRoute = useRoute('/deep/nesting/works/custom-path')
</script>

<template>
<header>
<div class="wrapper">
<nav>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/users/2">About</RouterLink>
<RouterLink :to="{ name: '/articles/[id]', params: { id: 2 } }"
>About</RouterLink
>
Expand Down
6 changes: 3 additions & 3 deletions playground/typed-router.d.ts
Expand Up @@ -36,9 +36,9 @@ declare module '@vue-router/routes' {
'the most rebel': RouteRecordInfo<'the most rebel', '/most-rebel', Record<never, never>, Record<never, never>>,
'/multiple-[a]-[b]-params': RouteRecordInfo<'/multiple-[a]-[b]-params', '/multiple-:a-:b-params', { a: ParamValue<true>, b: ParamValue<true> }, { a: ParamValue<false>, b: ParamValue<false> }>,
'/my-optional-[[slug]]': RouteRecordInfo<'/my-optional-[[slug]]', '/my-optional-:slug?', { slug?: ParamValueZeroOrOne<true> }, { slug?: ParamValueZeroOrOne<false> }>,
'/n-[[n]]/': RouteRecordInfo<'/n-[[n]]/', '/n-:n?/', Record<never, never>, Record<never, never>>,
'/n-[[n]]/[[more]]+/': RouteRecordInfo<'/n-[[n]]/[[more]]+/', '/n-:n?/:more*/', Record<never, never>, Record<never, never>>,
'/n-[[n]]/[[more]]+/[final]': RouteRecordInfo<'/n-[[n]]/[[more]]+/[final]', '/n-:n?/:more*/:final', { final: ParamValue<true> }, { final: ParamValue<false> }>,
'/n-[[n]]/': RouteRecordInfo<'/n-[[n]]/', '/n-:n?/', { n?: ParamValueZeroOrOne<true> }, { n?: ParamValueZeroOrOne<false> }>,
'/n-[[n]]/[[more]]+/': RouteRecordInfo<'/n-[[n]]/[[more]]+/', '/n-:n?/:more*/', { n?: ParamValueZeroOrOne<true>, more?: ParamValueZeroOrMore<true> }, { n?: ParamValueZeroOrOne<false>, more?: ParamValueZeroOrMore<false> }>,
'/n-[[n]]/[[more]]+/[final]': RouteRecordInfo<'/n-[[n]]/[[more]]+/[final]', '/n-:n?/:more*/:final', { n?: ParamValueZeroOrOne<true>, more?: ParamValueZeroOrMore<true>, final: ParamValue<true> }, { n?: ParamValueZeroOrOne<false>, more?: ParamValueZeroOrMore<false>, final: ParamValue<false> }>,
'/partial-[name]': RouteRecordInfo<'/partial-[name]', '/partial-:name', { name: ParamValue<true> }, { name: ParamValue<false> }>,
'/custom-path': RouteRecordInfo<'/custom-path', '/surprise-:id(\d+)', Record<never, never>, Record<never, never>>,
'/users/': RouteRecordInfo<'/users/', '/users/', Record<never, never>, Record<never, never>>,
Expand Down
16 changes: 16 additions & 0 deletions src/codegen/generateRouteMap.spec.ts
Expand Up @@ -47,6 +47,22 @@ describe('generateRouteNamedMap', () => {
`)
})

it('handles nested params in folders', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
tree.insert('n/[a]/index.vue') // normal
tree.insert('n/[a]/other.vue')
tree.insert('n/[a]/[b].vue')
tree.insert('n/[a]/[c]/other-[d].vue')
expect(formatExports(generateRouteNamedMap(tree))).toMatchInlineSnapshot(`
"export interface RouteNamedMap {
'/n/[a]/': RouteRecordInfo<'/n/[a]/', '/n/:a/', { a: ParamValue<true> }, { a: ParamValue<false> }>,
'/n/[a]/[b]': RouteRecordInfo<'/n/[a]/[b]', '/n/:a/:b', { a: ParamValue<true>, b: ParamValue<true> }, { a: ParamValue<false>, b: ParamValue<false> }>,
'/n/[a]/[c]/other-[d]': RouteRecordInfo<'/n/[a]/[c]/other-[d]', '/n/:a/:c/other-:d', { a: ParamValue<true>, c: ParamValue<true>, d: ParamValue<true> }, { a: ParamValue<false>, c: ParamValue<false>, d: ParamValue<false> }>,
'/n/[a]/other': RouteRecordInfo<'/n/[a]/other', '/n/:a/other', { a: ParamValue<true> }, { a: ParamValue<false> }>,
}"
`)
})

it('adds nested params', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
tree.insert('n/[a].vue') // normal
Expand Down
5 changes: 3 additions & 2 deletions src/codegen/generateRouteParams.ts
@@ -1,8 +1,9 @@
import { TreeLeaf } from '../core/tree'

export function generateRouteParams(node: TreeLeaf, isRaw: boolean): string {
return node.value.isParam()
? `{ ${node.value.params
const nodeParams = node.params
return node.params.length > 0
? `{ ${node.params
.map(
(param) =>
`${param.paramName}${param.optional ? '?' : ''}: ` +
Expand Down
80 changes: 80 additions & 0 deletions src/core/tree.spec.ts
Expand Up @@ -38,6 +38,86 @@ describe('Tree', () => {
expect(child.children.size).toBe(0)
})

it('creates params in nested files', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
const nestedId = tree.insert('nested/[id].vue')

expect(nestedId.value.isParam()).toBe(true)
expect(nestedId.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'id',
repeatable: false,
}),
])

const nestedAId = tree.insert('nested/a/[id].vue')
expect(nestedAId.value.isParam()).toBe(true)
expect(nestedAId.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'id',
repeatable: false,
}),
])
})

it('creates params in nested folders', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)

let node = tree.insert('nested/[id]/index.vue')
const id = tree.children.get('nested')!.children.get('[id]')!
expect(id.value.isParam()).toBe(true)
expect(id.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'id',
repeatable: false,
}),
])

expect(node.value.isParam()).toBe(false)
expect(node.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'id',
repeatable: false,
}),
])

node = tree.insert('nested/[a]/other.vue')
expect(node.value.isParam()).toBe(false)
expect(node.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'a',
repeatable: false,
}),
])

node = tree.insert('nested/a/[id]/index.vue')
expect(node.value.isParam()).toBe(false)
expect(node.params).toEqual([
expect.objectContaining({
isSplat: false,
modifier: '',
optional: false,
paramName: 'id',
repeatable: false,
}),
])
})

it('handles named views', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
tree.insert('index.vue')
Expand Down
15 changes: 14 additions & 1 deletion src/core/tree.ts
@@ -1,5 +1,5 @@
import type { ResolvedOptions } from '../options'
import { createTreeLeafValue } from './treeLeafValue'
import { createTreeLeafValue, TreeRouteParam } from './treeLeafValue'
import type { TreeLeafValue } from './treeLeafValue'
import { trimExtension } from './utils'
import { CustomRouteBlock } from './customBlock'
Expand Down Expand Up @@ -115,6 +115,19 @@ export class TreeLeaf {
: ''
}

get params(): TreeRouteParam[] {
const params = this.value.isParam() ? [...this.value.params] : []
let node = this.parent
while (node) {
if (node.value.isParam()) {
params.unshift(...node.value.params)
}
node = node.parent
}

return params
}

isRoot() {
return this.value.path === '/' && !this.value.filePaths.size
}
Expand Down

0 comments on commit ef078b0

Please sign in to comment.