Skip to content

Commit 879fae6

Browse files
committed
feat: allow multiple query matchers per route
1 parent 0027186 commit 879fae6

File tree

4 files changed

+51
-37
lines changed

4 files changed

+51
-37
lines changed

packages/experiments-playground/src/App.vue

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
<script setup lang="ts">
2-
import { ref } from 'vue'
2+
import { computed, ref } from 'vue'
33
import { useRoute, useRouter } from 'vue-router'
44
55
const route = useRoute()
66
const router = useRouter()
77
const url = ref('')
8+
9+
const queryPage = computed({
10+
get: () =>
11+
typeof route.params.page === 'number'
12+
? (route.params.page as number)
13+
: null,
14+
set: (value: number) => {
15+
// TODO: relative location
16+
router.push({
17+
...route,
18+
// @ts-expect-error: FIXME: wtf
19+
params: { ...route.params, page: value },
20+
})
21+
},
22+
})
823
</script>
924

1025
<template>
@@ -20,7 +35,7 @@ const url = ref('')
2035
|
2136
<RouterLink to="/profiles">Profiles list</RouterLink>
2237
</nav>
23-
<form @submit.prevent="router.push(url, route)">
38+
<form @submit.prevent="router.push(url)">
2439
<label for="path">Path:</label>
2540
<input
2641
id="path"
@@ -43,6 +58,10 @@ const url = ref('')
4358
<br />
4459
params: <code>{{ route.params }}</code>
4560
<br />
61+
<template v-if="queryPage != null">
62+
page: <input type="number" v-model.number="queryPage" />
63+
<br />
64+
</template>
4665
meta: <code>{{ route.meta }}</code>
4766
</p>
4867

packages/experiments-playground/src/router/index.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
MatcherPatternQuery,
1212
} from 'vue-router/experimental'
1313
import PageHome from '../pages/(home).vue'
14+
import type { EmptyParams } from 'vue-router/experimental'
1415

1516
// type ExtractMatcherQueryParams<T> =
1617
// T extends MatcherPatternQuery<infer P> ? P : never
@@ -34,14 +35,16 @@ const PAGE_QUERY_PATTERN_MATCHER: MatcherPatternQuery<{ page: number }> = {
3435
build: params => ({ page: String(params.page) }),
3536
}
3637

37-
const QUERY_PATTERN_MATCHER: MatcherPatternQuery<{ q: string }> = {
38+
const QUERY_PATTERN_MATCHER: MatcherPatternQuery<{ q?: string }> = {
3839
match: query => {
3940
return {
4041
q: typeof query.q === 'string' ? query.q : '',
4142
}
4243
},
43-
build: params => {
44-
return { q: params.q || '' }
44+
// NOTE: we need either to cast or to add an explicit return type annotation
45+
// because of the special meaning of {} in TypeScript.
46+
build: (params): { q?: string } => {
47+
return params.q ? { q: params.q } : ({} as EmptyParams)
4548
},
4649
}
4750

@@ -72,22 +75,6 @@ const QUERY_PATTERN_MATCHER: MatcherPatternQuery<{ q: string }> = {
7275
// QUERY_PATTERN_MATCHER
7376
// )
7477

75-
const QUERY_MATCHER_COMBINED: MatcherPatternQuery<{
76-
page: number
77-
q: string
78-
}> = {
79-
match: query => {
80-
return {
81-
...PAGE_QUERY_PATTERN_MATCHER.match(query),
82-
...QUERY_PATTERN_MATCHER.match(query),
83-
}
84-
},
85-
build: params => ({
86-
...PAGE_QUERY_PATTERN_MATCHER.build(params),
87-
...QUERY_PATTERN_MATCHER.build(params),
88-
}),
89-
}
90-
9178
const ANY_HASH_PATTERN_MATCHER: MatcherPatternHash<// hash could be named anything, in this case it creates a param named hash
9279
{ hash: string | null }> = {
9380
match: hash => ({ hash: hash ? hash.slice(1) : null }),
@@ -104,7 +91,7 @@ const r_group = normalizeRouteRecord({
10491
const r_home = normalizeRouteRecord({
10592
name: 'home',
10693
path: new MatcherPatternPathStatic('/'),
107-
query: QUERY_MATCHER_COMBINED,
94+
query: [PAGE_QUERY_PATTERN_MATCHER, QUERY_PATTERN_MATCHER],
10895
parent: r_group,
10996
components: { default: PageHome },
11097
})
@@ -124,7 +111,7 @@ const r_profiles_layout = normalizeRouteRecord({
124111
meta: {
125112
layout: 'profile',
126113
},
127-
query: PAGE_QUERY_PATTERN_MATCHER,
114+
query: [PAGE_QUERY_PATTERN_MATCHER],
128115
})
129116

130117
const r_profiles_list = normalizeRouteRecord({

packages/router/src/experimental/route-resolver/resolver-static.spec.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ describe('StaticResolver', () => {
117117
{
118118
name: 'any-path',
119119
path: ANY_PATH_PATTERN_MATCHER,
120-
query: PAGE_QUERY_PATTERN_MATCHER_LOCAL,
120+
query: [PAGE_QUERY_PATTERN_MATCHER_LOCAL],
121121
},
122122
])
123123

@@ -156,7 +156,7 @@ describe('StaticResolver', () => {
156156
{
157157
name: 'user-detail',
158158
path: USER_ID_PATH_PATTERN_MATCHER,
159-
query: PAGE_QUERY_PATTERN_MATCHER_LOCAL,
159+
query: [PAGE_QUERY_PATTERN_MATCHER_LOCAL],
160160
hash: ANY_HASH_PATTERN_MATCHER,
161161
},
162162
])
@@ -317,14 +317,16 @@ describe('StaticResolver', () => {
317317
{
318318
name: 'query',
319319
path: EMPTY_PATH_PATTERN_MATCHER,
320-
query: {
321-
match(q) {
322-
return { q }
323-
},
324-
build({ q }) {
325-
return { ...q }
326-
},
327-
} satisfies MatcherPatternQuery<{ q: MatcherQueryParams }>,
320+
query: [
321+
{
322+
match(q) {
323+
return { q }
324+
},
325+
build({ q }) {
326+
return { ...q }
327+
},
328+
} satisfies MatcherPatternQuery<{ q: MatcherQueryParams }>,
329+
],
328330
},
329331
])
330332
expect(resolver.resolve('/?%23%2F%3F=%23%2F%3F')).toMatchObject({

packages/router/src/experimental/route-resolver/resolver-static.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface EXPERIMENTAL_ResolverRecord_Base {
4141
/**
4242
* {@link MatcherPattern} for the query section of the URI.
4343
*/
44-
query?: MatcherPatternQuery
44+
query?: MatcherPatternQuery[]
4545

4646
/**
4747
* {@link MatcherPattern} for the hash section of the URI.
@@ -63,7 +63,9 @@ export interface EXPERIMENTAL_ResolverRecord_Group
6363
extends EXPERIMENTAL_ResolverRecord_Base {
6464
name?: undefined
6565
path?: undefined
66-
query?: undefined
66+
// Query is the only kind of matcher that is non-exclusive
67+
// all matched records get their queries merged
68+
// query?: undefined
6769
hash?: undefined
6870
}
6971

@@ -190,7 +192,9 @@ export function createStaticResolver<
190192
...currentLocation?.query,
191193
...normalizeQuery(to.query),
192194
},
193-
...matched.map(record => record.query?.build(params))
195+
...matched.flatMap(record =>
196+
record.query?.map(query => query.build(params))
197+
)
194198
)
195199

196200
return {
@@ -233,7 +237,9 @@ export function createStaticResolver<
233237
matched = buildMatched(record)
234238
const queryParams: MatcherQueryParams = Object.assign(
235239
{},
236-
...matched.map(record => record.query?.match(url.query))
240+
...matched.flatMap(record =>
241+
record.query?.map(query => query.match(url.query))
242+
)
237243
)
238244
// TODO: test performance
239245
// for (const record of matched) {

0 commit comments

Comments
 (0)