Skip to content

Commit 8bcf9de

Browse files
committed
feat: MatcherPatternQueryParam
1 parent 7432d65 commit 8bcf9de

File tree

9 files changed

+136
-25
lines changed

9 files changed

+136
-25
lines changed

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import {
33
experimental_createRouter,
44
createFixedResolver,
55
MatcherPatternPathStatic,
6-
MatcherPatternPathCustomParams,
6+
MatcherPatternPathDynamic,
77
normalizeRouteRecord,
88
PARAM_PARSER_INT,
9+
MatcherPatternQueryParam,
910
} from 'vue-router/experimental'
1011
import type {
1112
EXPERIMENTAL_RouteRecordNormalized_Matchable,
@@ -49,12 +50,29 @@ const r_group = normalizeRouteRecord({
4950
meta: {
5051
fromGroup: 'r_group',
5152
},
53+
54+
query: [
55+
new MatcherPatternQueryParam(
56+
'group',
57+
'isGroup',
58+
'value',
59+
PARAM_PARSER_INT,
60+
undefined
61+
),
62+
],
5263
})
5364

5465
const r_home = normalizeRouteRecord({
5566
name: 'home',
5667
path: new MatcherPatternPathStatic('/'),
57-
query: [PAGE_QUERY_PATTERN_MATCHER, QUERY_PATTERN_MATCHER],
68+
query: [
69+
new MatcherPatternQueryParam('pageArray', 'p', 'array', PARAM_PARSER_INT, [
70+
1,
71+
]),
72+
new MatcherPatternQueryParam('pageBoth', 'p', 'both', PARAM_PARSER_INT, 1),
73+
new MatcherPatternQueryParam('page', 'p', 'value', PARAM_PARSER_INT, 1),
74+
QUERY_PATTERN_MATCHER,
75+
],
5876
parent: r_group,
5977
components: { default: PageHome },
6078
})
@@ -101,7 +119,7 @@ const r_profiles_detail = normalizeRouteRecord({
101119
name: 'profiles-detail',
102120
components: { default: () => import('../pages/profiles/[userId].vue') },
103121
parent: r_profiles_layout,
104-
path: new MatcherPatternPathCustomParams(
122+
path: new MatcherPatternPathDynamic(
105123
/^\/profiles\/([^/]+)$/i,
106124
{
107125
// this version handles all kind of params but in practice,

packages/router/src/experimental/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ export type {
2424
MatcherPattern,
2525
MatcherPatternHash,
2626
MatcherPatternPath,
27-
MatcherPatternQuery,
2827
MatcherParamsFormatted,
2928
MatcherQueryParams,
3029
MatcherQueryParamsValue,
3130
MatcherPatternPathDynamic_ParamOptions,
3231
} from './route-resolver/matchers/matcher-pattern'
3332

33+
export {
34+
type MatcherPatternQuery,
35+
MatcherPatternQueryParam,
36+
} from './route-resolver/matchers/matcher-pattern-query'
37+
3438
export {
3539
PARAM_PARSER_INT,
3640
type ParamParser,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { toValue } from 'vue'
2+
import {
3+
EmptyParams,
4+
MatcherParamsFormatted,
5+
MatcherPattern,
6+
MatcherQueryParams,
7+
} from './matcher-pattern'
8+
import { ParamParser } from './param-parsers'
9+
10+
/**
11+
* Handles the `query` part of a URL. It can transform a query object into an
12+
* object of params and vice versa.
13+
*/
14+
15+
export interface MatcherPatternQuery<
16+
TParams extends MatcherParamsFormatted = MatcherParamsFormatted,
17+
> extends MatcherPattern<MatcherQueryParams, TParams> {}
18+
19+
export class MatcherPatternQueryParam<T, ParamName extends string>
20+
implements MatcherPatternQuery<Record<ParamName, T>>
21+
{
22+
constructor(
23+
private paramName: ParamName,
24+
private queryKey: string,
25+
private format: 'value' | 'array' | 'both',
26+
private parser: ParamParser<T>,
27+
// TODO: optional values
28+
// private format: 'value' | 'array' | 'both' = 'both',
29+
// private parser: ParamParser<T> = PATH_PARAM_DEFAULT_PARSER,
30+
private defaultValue?: (() => T) | T
31+
) {}
32+
33+
match(query: MatcherQueryParams): Record<ParamName, T> {
34+
const queryValue = query[this.queryKey]
35+
36+
let valueBeforeParse =
37+
this.format === 'value'
38+
? Array.isArray(queryValue)
39+
? queryValue[0]
40+
: queryValue
41+
: this.format === 'array'
42+
? Array.isArray(queryValue)
43+
? queryValue
44+
: [queryValue]
45+
: queryValue
46+
47+
let value: T | undefined
48+
49+
// if we have an array, we need to try catch each value
50+
if (Array.isArray(valueBeforeParse)) {
51+
// @ts-expect-error: T is not connected to valueBeforeParse
52+
value = []
53+
for (const v of valueBeforeParse) {
54+
if (v != null) {
55+
try {
56+
;(value as unknown[]).push(
57+
// for ts errors
58+
this.parser.get!(v)
59+
)
60+
} catch (error) {
61+
// we skip the invalid value unless there is no defaultValue
62+
if (this.defaultValue == null) {
63+
throw error
64+
}
65+
}
66+
}
67+
}
68+
69+
// if we have no values, we want to fall back to the default value
70+
if ((value as unknown[]).length === 0) {
71+
value = undefined
72+
}
73+
} else {
74+
try {
75+
// FIXME: fallback to default getter
76+
value = this.parser.get!(valueBeforeParse)
77+
} catch (error) {
78+
if (this.defaultValue == null) {
79+
throw error
80+
}
81+
}
82+
}
83+
84+
return {
85+
[this.paramName]: value ?? toValue(this.defaultValue),
86+
// This is a TS limitation
87+
} as Record<ParamName, T>
88+
}
89+
90+
build(params: Record<ParamName, T>): MatcherQueryParams {
91+
const paramValue = params[this.paramName]
92+
93+
if (paramValue == null) {
94+
return {} as EmptyParams
95+
}
96+
97+
return {
98+
// FIXME: default setter
99+
[this.queryKey]: this.parser.set!(paramValue),
100+
}
101+
}
102+
}

packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,6 @@ export class MatcherPatternPathDynamic<
239239
}
240240
}
241241

242-
/**
243-
* Handles the `query` part of a URL. It can transform a query object into an
244-
* object of params and vice versa.
245-
*/
246-
export interface MatcherPatternQuery<
247-
TParams extends MatcherParamsFormatted = MatcherParamsFormatted,
248-
> extends MatcherPattern<MatcherQueryParams, TParams> {}
249-
250242
/**
251243
* Handles the `hash` part of a URL. It can transform a hash string into an
252244
* object of params and vice versa.

packages/router/src/experimental/route-resolver/matchers/param-parsers/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ export const PATH_PARAM_DEFAULT_SET = (
2121
) => (value && Array.isArray(value) ? value.map(String) : String(value)) // TODO: `(value an null | undefined)` for types
2222

2323
export const PATH_PARAM_SINGLE_DEFAULT: ParamParser<string, string> = {}
24-
export const PATH_PARAM_DEFAULT_PARSER: ParamParser = {
24+
export const PATH_PARAM_DEFAULT_PARSER = {
2525
get: PATH_PARAM_DEFAULT_GET,
2626
set: PATH_PARAM_DEFAULT_SET,
27-
}
27+
} satisfies ParamParser
2828

2929
export type { ParamParser }
3030

packages/router/src/experimental/route-resolver/matchers/param-parsers/numbers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ParamParser } from './types'
44
export const PARAM_INTEGER_SINGLE = {
55
get: (value: string) => {
66
const num = Number(value)
7-
if (Number.isInteger(num)) {
7+
if (value && Number.isInteger(num)) {
88
return num
99
}
1010
throw miss()

packages/router/src/experimental/route-resolver/matchers/test-utils.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { EmptyParams } from './matcher-pattern'
2-
import {
3-
MatcherPatternPath,
4-
MatcherPatternQuery,
5-
MatcherPatternHash,
6-
} from './matcher-pattern'
2+
import { MatcherPatternPath, MatcherPatternHash } from './matcher-pattern'
3+
import { MatcherPatternQuery } from './matcher-pattern-query'
74
import { miss } from './errors'
85

96
export const ANY_PATH_PATTERN_MATCHER: MatcherPatternPath<{

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import {
66
MatcherPatternHash,
77
MatcherQueryParams,
88
} from './matchers/matcher-pattern'
9-
import {
10-
MatcherPatternQuery,
11-
MatcherPatternPathStatic,
12-
} from './matchers/matcher-pattern'
9+
import { MatcherPatternPathStatic } from './matchers/matcher-pattern'
10+
import { MatcherPatternQuery } from './matchers/matcher-pattern-query'
1311
import {
1412
EMPTY_PATH_PATTERN_MATCHER,
1513
USER_ID_PATH_PATTERN_MATCHER,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import {
1919
import { MatcherQueryParams } from './matchers/matcher-pattern'
2020
import type {
2121
MatcherPatternPath,
22-
MatcherPatternQuery,
2322
MatcherPatternHash,
2423
} from './matchers/matcher-pattern'
24+
import type { MatcherPatternQuery } from './matchers/matcher-pattern-query'
2525
import { warn } from '../../warning'
2626

2727
export interface EXPERIMENTAL_ResolverRecord_Base {

0 commit comments

Comments
 (0)