experimental_createRouter,
createFixedResolver,
MatcherPatternPathStatic,
- MatcherPatternPathCustomParams,
+ MatcherPatternPathDynamic,
normalizeRouteRecord,
PARAM_PARSER_INT,
+ MatcherPatternQueryParam,
} from 'vue-router/experimental'
import type {
EXPERIMENTAL_RouteRecordNormalized_Matchable,
meta: {
fromGroup: 'r_group',
},
+
+ query: [
+ new MatcherPatternQueryParam(
+ 'group',
+ 'isGroup',
+ 'value',
+ PARAM_PARSER_INT,
+ undefined
+ ),
+ ],
})
const r_home = normalizeRouteRecord({
name: 'home',
path: new MatcherPatternPathStatic('/'),
- query: [PAGE_QUERY_PATTERN_MATCHER, QUERY_PATTERN_MATCHER],
+ query: [
+ new MatcherPatternQueryParam('pageArray', 'p', 'array', PARAM_PARSER_INT, [
+ 1,
+ ]),
+ new MatcherPatternQueryParam('pageBoth', 'p', 'both', PARAM_PARSER_INT, 1),
+ new MatcherPatternQueryParam('page', 'p', 'value', PARAM_PARSER_INT, 1),
+ QUERY_PATTERN_MATCHER,
+ ],
parent: r_group,
components: { default: PageHome },
})
name: 'profiles-detail',
components: { default: () => import('../pages/profiles/[userId].vue') },
parent: r_profiles_layout,
- path: new MatcherPatternPathCustomParams(
+ path: new MatcherPatternPathDynamic(
/^\/profiles\/([^/]+)$/i,
{
// this version handles all kind of params but in practice,
MatcherPattern,
MatcherPatternHash,
MatcherPatternPath,
- MatcherPatternQuery,
MatcherParamsFormatted,
MatcherQueryParams,
MatcherQueryParamsValue,
MatcherPatternPathDynamic_ParamOptions,
} from './route-resolver/matchers/matcher-pattern'
+export {
+ type MatcherPatternQuery,
+ MatcherPatternQueryParam,
+} from './route-resolver/matchers/matcher-pattern-query'
+
export {
PARAM_PARSER_INT,
type ParamParser,
--- /dev/null
+import { toValue } from 'vue'
+import {
+ EmptyParams,
+ MatcherParamsFormatted,
+ MatcherPattern,
+ MatcherQueryParams,
+} from './matcher-pattern'
+import { ParamParser } from './param-parsers'
+
+/**
+ * Handles the `query` part of a URL. It can transform a query object into an
+ * object of params and vice versa.
+ */
+
+export interface MatcherPatternQuery<
+ TParams extends MatcherParamsFormatted = MatcherParamsFormatted,
+> extends MatcherPattern<MatcherQueryParams, TParams> {}
+
+export class MatcherPatternQueryParam<T, ParamName extends string>
+ implements MatcherPatternQuery<Record<ParamName, T>>
+{
+ constructor(
+ private paramName: ParamName,
+ private queryKey: string,
+ private format: 'value' | 'array' | 'both',
+ private parser: ParamParser<T>,
+ // TODO: optional values
+ // private format: 'value' | 'array' | 'both' = 'both',
+ // private parser: ParamParser<T> = PATH_PARAM_DEFAULT_PARSER,
+ private defaultValue?: (() => T) | T
+ ) {}
+
+ match(query: MatcherQueryParams): Record<ParamName, T> {
+ const queryValue = query[this.queryKey]
+
+ let valueBeforeParse =
+ this.format === 'value'
+ ? Array.isArray(queryValue)
+ ? queryValue[0]
+ : queryValue
+ : this.format === 'array'
+ ? Array.isArray(queryValue)
+ ? queryValue
+ : [queryValue]
+ : queryValue
+
+ let value: T | undefined
+
+ // if we have an array, we need to try catch each value
+ if (Array.isArray(valueBeforeParse)) {
+ // @ts-expect-error: T is not connected to valueBeforeParse
+ value = []
+ for (const v of valueBeforeParse) {
+ if (v != null) {
+ try {
+ ;(value as unknown[]).push(
+ // for ts errors
+ this.parser.get!(v)
+ )
+ } catch (error) {
+ // we skip the invalid value unless there is no defaultValue
+ if (this.defaultValue == null) {
+ throw error
+ }
+ }
+ }
+ }
+
+ // if we have no values, we want to fall back to the default value
+ if ((value as unknown[]).length === 0) {
+ value = undefined
+ }
+ } else {
+ try {
+ // FIXME: fallback to default getter
+ value = this.parser.get!(valueBeforeParse)
+ } catch (error) {
+ if (this.defaultValue == null) {
+ throw error
+ }
+ }
+ }
+
+ return {
+ [this.paramName]: value ?? toValue(this.defaultValue),
+ // This is a TS limitation
+ } as Record<ParamName, T>
+ }
+
+ build(params: Record<ParamName, T>): MatcherQueryParams {
+ const paramValue = params[this.paramName]
+
+ if (paramValue == null) {
+ return {} as EmptyParams
+ }
+
+ return {
+ // FIXME: default setter
+ [this.queryKey]: this.parser.set!(paramValue),
+ }
+ }
+}
}
}
-/**
- * Handles the `query` part of a URL. It can transform a query object into an
- * object of params and vice versa.
- */
-export interface MatcherPatternQuery<
- TParams extends MatcherParamsFormatted = MatcherParamsFormatted,
-> extends MatcherPattern<MatcherQueryParams, TParams> {}
-
/**
* Handles the `hash` part of a URL. It can transform a hash string into an
* object of params and vice versa.
) => (value && Array.isArray(value) ? value.map(String) : String(value)) // TODO: `(value an null | undefined)` for types
export const PATH_PARAM_SINGLE_DEFAULT: ParamParser<string, string> = {}
-export const PATH_PARAM_DEFAULT_PARSER: ParamParser = {
+export const PATH_PARAM_DEFAULT_PARSER = {
get: PATH_PARAM_DEFAULT_GET,
set: PATH_PARAM_DEFAULT_SET,
-}
+} satisfies ParamParser
export type { ParamParser }
export const PARAM_INTEGER_SINGLE = {
get: (value: string) => {
const num = Number(value)
- if (Number.isInteger(num)) {
+ if (value && Number.isInteger(num)) {
return num
}
throw miss()
import { EmptyParams } from './matcher-pattern'
-import {
- MatcherPatternPath,
- MatcherPatternQuery,
- MatcherPatternHash,
-} from './matcher-pattern'
+import { MatcherPatternPath, MatcherPatternHash } from './matcher-pattern'
+import { MatcherPatternQuery } from './matcher-pattern-query'
import { miss } from './errors'
export const ANY_PATH_PATTERN_MATCHER: MatcherPatternPath<{
MatcherPatternHash,
MatcherQueryParams,
} from './matchers/matcher-pattern'
-import {
- MatcherPatternQuery,
- MatcherPatternPathStatic,
-} from './matchers/matcher-pattern'
+import { MatcherPatternPathStatic } from './matchers/matcher-pattern'
+import { MatcherPatternQuery } from './matchers/matcher-pattern-query'
import {
EMPTY_PATH_PATTERN_MATCHER,
USER_ID_PATH_PATTERN_MATCHER,
import { MatcherQueryParams } from './matchers/matcher-pattern'
import type {
MatcherPatternPath,
- MatcherPatternQuery,
MatcherPatternHash,
} from './matchers/matcher-pattern'
+import type { MatcherPatternQuery } from './matchers/matcher-pattern-query'
import { warn } from '../../warning'
export interface EXPERIMENTAL_ResolverRecord_Base {