From: Eduardo San Martin Morote Date: Fri, 6 Dec 2024 21:01:44 +0000 (+0100) Subject: refactor: renames and minor changes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d46275810fb4d10677b75fee1e11cbbc7d1e7ffb;p=thirdparty%2Fvuejs%2Frouter.git refactor: renames and minor changes --- diff --git a/packages/router/src/new-route-resolver/matcher-location.ts b/packages/router/src/new-route-resolver/matcher-location.ts index 1bfcd9a1..c205a456 100644 --- a/packages/router/src/new-route-resolver/matcher-location.ts +++ b/packages/router/src/new-route-resolver/matcher-location.ts @@ -6,7 +6,7 @@ import type { MatcherName } from './matcher' */ export type MatcherParamsFormatted = Record -export interface MatcherLocationAsName { +export interface MatcherLocationAsNamed { name: MatcherName params: MatcherParamsFormatted query?: LocationQueryRaw diff --git a/packages/router/src/new-route-resolver/matcher-pattern.ts b/packages/router/src/new-route-resolver/matcher-pattern.ts index 3b2bbdbf..049109e1 100644 --- a/packages/router/src/new-route-resolver/matcher-pattern.ts +++ b/packages/router/src/new-route-resolver/matcher-pattern.ts @@ -47,11 +47,11 @@ export interface MatcherPattern { * query: { used: String }, // we require a `used` query param * }) * // /?used=2 - * pattern.parseLocation({ path: '/', query: { used: '' }, hash: '' }) // null + * pattern.parseLocation({ path: '/', query: { used: '' }, hash: '' }) // null becauso no /foo * // /foo?used=2¬Used¬Used=2#hello * pattern.parseLocation({ path: '/foo', query: { used: '2', notUsed: [null, '2']}, hash: '#hello' }) - * // { used: '2' } // we extract the required params - * // /foo?used=2#hello + * // [{}, { used: '2' }, {}]// we extract the required params + * // /foo?other=2#hello * pattern.parseLocation({ path: '/foo', query: {}, hash: '#hello' }) * // null // the query param is missing * ``` @@ -109,6 +109,7 @@ export interface PatternPathParamOptions export interface PatternQueryParamOptions extends PatternParamOptions_Base { + // FIXME: can be removed? seems to be the same as above get: (value: MatcherQueryParamsValue) => T set?: (value: T) => MatcherQueryParamsValue } @@ -153,7 +154,7 @@ export class MatcherPatternImpl implements MatcherPattern { query: MatcherQueryParams hash: string }) { - // TODO: is this performant? bench compare to a check with `null + // TODO: is this performant? bench compare to a check with `null` try { return [ this.path.match(location.path), diff --git a/packages/router/src/new-route-resolver/matcher.test-d.ts b/packages/router/src/new-route-resolver/matcher.test-d.ts index 412cb071..bb45c512 100644 --- a/packages/router/src/new-route-resolver/matcher.test-d.ts +++ b/packages/router/src/new-route-resolver/matcher.test-d.ts @@ -1,16 +1,42 @@ -import { describe, it } from 'vitest' +import { describe, expectTypeOf, it } from 'vitest' import { NEW_LocationResolved, createCompiledMatcher } from './matcher' describe('Matcher', () => { - it('resolves locations', () => { - const matcher = createCompiledMatcher() - matcher.resolve('/foo') - // @ts-expect-error: needs currentLocation - matcher.resolve('foo') - matcher.resolve('foo', {} as NEW_LocationResolved) - matcher.resolve({ name: 'foo', params: {} }) - // @ts-expect-error: needs currentLocation - matcher.resolve({ params: { id: 1 } }) - matcher.resolve({ params: { id: 1 } }, {} as NEW_LocationResolved) + const matcher = createCompiledMatcher() + + describe('matcher.resolve()', () => { + it('resolves absolute string locations', () => { + expectTypeOf( + matcher.resolve('/foo') + ).toEqualTypeOf() + }) + + it('fails on non absolute location without a currentLocation', () => { + // @ts-expect-error: needs currentLocation + matcher.resolve('foo') + }) + + it('resolves relative locations', () => { + expectTypeOf( + matcher.resolve('foo', {} as NEW_LocationResolved) + ).toEqualTypeOf() + }) + + it('resolved named locations', () => { + expectTypeOf( + matcher.resolve({ name: 'foo', params: {} }) + ).toEqualTypeOf() + }) + + it('fails on object relative location without a currentLocation', () => { + // @ts-expect-error: needs currentLocation + matcher.resolve({ params: { id: 1 } }) + }) + + it('resolves object relative locations with a currentLocation', () => { + expectTypeOf( + matcher.resolve({ params: { id: 1 } }, {} as NEW_LocationResolved) + ).toEqualTypeOf() + }) }) }) diff --git a/packages/router/src/new-route-resolver/matcher.ts b/packages/router/src/new-route-resolver/matcher.ts index 4aa742e9..31b1d031 100644 --- a/packages/router/src/new-route-resolver/matcher.ts +++ b/packages/router/src/new-route-resolver/matcher.ts @@ -13,7 +13,7 @@ import { } from '../encoding' import { parseURL, stringifyURL } from '../location' import type { - MatcherLocationAsName, + MatcherLocationAsNamed, MatcherLocationAsRelative, MatcherParamsFormatted, } from './matcher-location' @@ -31,7 +31,7 @@ export interface RouteResolver { /** * Resolves a string location relative to another location. A relative location can be `./same-folder`, - * `../parent-folder`, or even `same-folder`. + * `../parent-folder`, `same-folder`, or even `?page=2`. */ resolve( relativeLocation: string, @@ -41,7 +41,7 @@ export interface RouteResolver { /** * Resolves a location by its name. Any required params or query must be passed in the `options` argument. */ - resolve(location: MatcherLocationAsName): NEW_LocationResolved + resolve(location: MatcherLocationAsNamed): NEW_LocationResolved /** * Resolves a location by its path. Any required query must be passed. @@ -67,7 +67,7 @@ export interface RouteResolver { type MatcherResolveArgs = | [absoluteLocation: `/${string}`] | [relativeLocation: string, currentLocation: NEW_LocationResolved] - | [location: MatcherLocationAsName] + | [location: MatcherLocationAsNamed] | [ relativeLocation: MatcherLocationAsRelative, currentLocation: NEW_LocationResolved @@ -108,7 +108,11 @@ export type MatcherPathParams = Record export type MatcherQueryParamsValue = string | null | Array export type MatcherQueryParams = Record -export function applyToParams( +/** + * Apply a function to all properties in an object. It's used to encode/decode params and queries. + * @internal + */ +export function applyFnToObject( fn: (v: string | number | null | undefined) => R, params: MatcherPathParams | LocationQuery | undefined ): Record { @@ -195,7 +199,7 @@ function transformObject( } export const NO_MATCH_LOCATION = { - name: Symbol('no-match'), + name: __DEV__ ? Symbol('no-match') : Symbol(), params: {}, matched: [], } satisfies Omit @@ -215,8 +219,9 @@ export function createCompiledMatcher(): RouteResolver { function resolve(...args: MatcherResolveArgs): NEW_LocationResolved { const [location, currentLocation] = args + + // string location, e.g. '/foo', '../bar', 'baz', '?page=1' if (typeof location === 'string') { - // string location, e.g. '/foo', '../bar', 'baz' const url = parseURL(parseQuery, location, currentLocation?.path) let matcher: MatcherPattern | undefined @@ -257,6 +262,21 @@ export function createCompiledMatcher(): RouteResolver { } } else { // relative location or by name + if (__DEV__ && location.name == null && currentLocation == null) { + console.warn( + `Cannot resolve an unnamed relative location without a current location. This will throw in production.`, + location + ) + return { + ...NO_MATCH_LOCATION, + fullPath: '/', + path: '/', + query: {}, + hash: '', + } + } + + // either one of them must be defined and is catched by the dev only warn above const name = location.name ?? currentLocation!.name const matcher = matchers.get(name) if (!matcher) { @@ -264,7 +284,8 @@ export function createCompiledMatcher(): RouteResolver { } // unencoded params in a formatted form that the user came up with - const params = location.params ?? currentLocation!.params + const params: MatcherParamsFormatted = + location.params ?? currentLocation!.params const mixedUnencodedParams = matcher.matchParams(params) if (!mixedUnencodedParams) {