From: Eduardo San Martin Morote Date: Fri, 8 Aug 2025 08:30:34 +0000 (+0200) Subject: refactor: add encoding within matcher X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f846fadf60700520c725d010cd12abec18d6edc7;p=thirdparty%2Fvuejs%2Frouter.git refactor: add encoding within matcher --- diff --git a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.spec.ts b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.spec.ts index 0cc13cc5..8fd542fa 100644 --- a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.spec.ts +++ b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.spec.ts @@ -112,12 +112,7 @@ describe('MatcherPatternPathCustom', () => { // all defaults teamId: {}, }, - ({ teamId }) => { - if (typeof teamId !== 'string') { - throw invalid('teamId must be a string') - } - return pathEncoded`/teams/${teamId}/b` - } + ['teams', 0, 'b'] ) expect(pattern.match('/teams/123/b')).toEqual({ @@ -138,12 +133,7 @@ describe('MatcherPatternPathCustom', () => { { teamId: {}, }, - ({ teamId }) => { - if (typeof teamId !== 'string') { - throw invalid('teamId must be a string') - } - return pathEncoded`/teams/${teamId}` - } + ['teams', 0] ) expect(pattern.match('/teams/a%20b')).toEqual({ teamId: 'a b' }) expect(pattern.build({ teamId: 'a b' })).toBe('/teams/a%20b') @@ -155,12 +145,7 @@ describe('MatcherPatternPathCustom', () => { { teamId: { optional: true }, }, - ({ teamId }) => { - if (teamId != null && typeof teamId !== 'string') { - throw invalid('teamId must be a string') - } - return teamId ? pathEncoded`/teams/${teamId}/b` : '/teams/b' - } + ['teams', 0, 'b'] ) expect(pattern.match('/teams/b')).toEqual({ teamId: null }) @@ -177,12 +162,7 @@ describe('MatcherPatternPathCustom', () => { { teamId: { repeat: true }, }, - ({ teamId }) => { - if (!Array.isArray(teamId)) { - throw invalid('teamId must be an array') - } - return '/teams/' + teamId.join('/') + '/b' - } + ['teams', 0, 'b'] ) expect(pattern.match('/teams/123/b')).toEqual({ teamId: ['123'] }) @@ -201,15 +181,7 @@ describe('MatcherPatternPathCustom', () => { { teamId: { repeat: true, optional: true }, }, - ({ teamId }) => { - if (!Array.isArray(teamId)) { - throw invalid('teamId must be an array') - } - const joined = teamId.join('/') - return teamId - ? '/teams' + (joined ? '/' + joined : '') + '/b' - : '/teams/b' - } + ['teams', 0, 'b'] ) expect(pattern.match('/teams/123/b')).toEqual({ teamId: ['123'] }) diff --git a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts index d239ac7f..b015241f 100644 --- a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts +++ b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts @@ -1,3 +1,4 @@ +import { encodeParam } from '../../../encoding' import { warn } from '../../../warning' import { decode, MatcherQueryParams } from '../resolver-abstract' import { miss } from './errors' @@ -198,6 +199,7 @@ interface MatcherPatternPathCustomParamOptions< TOut = string | string[] | null, > { repeat?: boolean + // TODO: not needed because in the regexp, the value is undefined if the group is optional and not given optional?: boolean parser?: Param_GetSet } @@ -237,23 +239,20 @@ export const PARAM_NUMBER_REPEATABLE_OPTIONAL = { } satisfies Param_GetSet export class MatcherPatternPathCustomParams implements MatcherPatternPath { - // private paramsKeys: string[] + private paramsKeys: string[] constructor( - // TODO: make this work with named groups and simplify `params` to be an array of the repeat flag readonly re: RegExp, readonly params: Record< string, - // @ts-expect-error: adapt with generic class MatcherPatternPathCustomParamOptions >, - readonly build: (params: MatcherParamsFormatted) => string // A better version could be using all the parts to join them // .e.g ['users', 0, 'profile', 1] -> /users/123/profile/456 // numbers are indexes of the params in the params object keys - // readonly pathParts: Array + readonly pathParts: Array ) { - // this.paramsKeys = Object.keys(this.params) + this.paramsKeys = Object.keys(this.params) } match(path: string): MatcherParamsFormatted { @@ -261,30 +260,27 @@ export class MatcherPatternPathCustomParams implements MatcherPatternPath { if (!match) { throw miss() } + // NOTE: if we have params, we assume named groups const params = {} as MatcherParamsFormatted let i = 1 // index in match array for (const paramName in this.params) { - const currentParam = this.params[paramName] - // an optional group in the regexp will return undefined - const currentMatch = (match[i++] as string | undefined) ?? null - if (__DEV__ && !currentParam.optional && !currentMatch) { - warn( - `Unexpected undefined value for param "${paramName}". Regexp: ${String(this.re)}. path: "${path}". This is likely a bug.` - ) - throw miss() - } + const paramOptions = this.params[paramName] + const currentMatch = (match[i] as string | undefined) ?? null - const value = currentParam.repeat + const value = paramOptions.repeat ? (currentMatch?.split('/') || []).map( // using just decode makes the type inference fail v => decode(v) ) : decode(currentMatch) - params[paramName] = (currentParam.parser?.get || (v => v))(value) + params[paramName] = (paramOptions.parser?.get || (v => v))(value) } - if (__DEV__ && i !== match.length) { + if ( + __DEV__ && + Object.keys(params).length !== Object.keys(this.params).length + ) { warn( `Regexp matched ${match.length} params, but ${i} params are defined. Found when matching "${path}" against ${String(this.re)}` ) @@ -292,6 +288,21 @@ export class MatcherPatternPathCustomParams implements MatcherPatternPath { return params } + + build(params: MatcherParamsFormatted): string { + return this.pathParts.reduce((acc, part) => { + if (typeof part === 'string') { + return acc + '/' + part + } + const paramName = this.paramsKeys[part] + const paramOptions = this.params[paramName] + const value = (paramOptions.parser?.set || (v => v))(params[paramName]) + const encodedValue = Array.isArray(value) + ? value.map(encodeParam).join('/') + : encodeParam(value) + return encodedValue ? acc + '/' + encodedValue : acc + }, '') + } } /**