diff --git a/packages/router/src/new-route-resolver/matcher-location.ts b/packages/router/src/new-route-resolver/matcher-location.ts index 3744e8cec..b9ca1ab0c 100644 --- a/packages/router/src/new-route-resolver/matcher-location.ts +++ b/packages/router/src/new-route-resolver/matcher-location.ts @@ -19,25 +19,41 @@ export interface MatcherLocationAsNamed { hash?: string /** - * A path is ignored if `name` is provided. + * @deprecated This is ignored when `name` is provided */ path?: undefined } -export interface MatcherLocationAsPath { +export interface MatcherLocationAsPathRelative { path: string query?: LocationQueryRaw hash?: string + /** + * @deprecated This is ignored when `path` is provided + */ name?: undefined + /** + * @deprecated This is ignored when `path` (instead of `name`) is provided + */ params?: undefined } +export interface MatcherLocationAsPathAbsolute + extends MatcherLocationAsPathRelative { + path: `/${string}` +} export interface MatcherLocationAsRelative { params?: MatcherParamsFormatted query?: LocationQueryRaw hash?: string + /** + * @deprecated This location is relative to the next parameter. This `name` will be ignored. + */ name?: undefined + /** + * @deprecated This location is relative to the next parameter. This `path` will be ignored. + */ path?: undefined } diff --git a/packages/router/src/new-route-resolver/matcher.spec.ts b/packages/router/src/new-route-resolver/matcher.spec.ts index 15ca09f83..c15561f53 100644 --- a/packages/router/src/new-route-resolver/matcher.spec.ts +++ b/packages/router/src/new-route-resolver/matcher.spec.ts @@ -209,6 +209,20 @@ describe('Matcher', () => { }) }) + describe('absolute locations as objects', () => { + it('resolves an object location', () => { + const matcher = createCompiledMatcher() + matcher.addRoute(EMPTY_PATH_ROUTE) + expect(matcher.resolve({ path: '/' })).toMatchObject({ + fullPath: '/', + path: '/', + params: {}, + query: {}, + hash: '', + }) + }) + }) + describe('named locations', () => { it('resolves named locations with no params', () => { const matcher = createCompiledMatcher() 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 c50731a1e..a60874518 100644 --- a/packages/router/src/new-route-resolver/matcher.test-d.ts +++ b/packages/router/src/new-route-resolver/matcher.test-d.ts @@ -39,4 +39,16 @@ describe('Matcher', () => { ).toEqualTypeOf() }) }) + + it('does not allow a name + path', () => { + matcher.resolve({ + // ...({} as NEW_LocationResolved), + name: 'foo', + params: {}, + // @ts-expect-error: name + path + path: '/e', + }) + // @ts-expect-error: name + currentLocation + matcher.resolve({ name: 'a', params: {} }, {} as NEW_LocationResolved) + }) }) diff --git a/packages/router/src/new-route-resolver/matcher.ts b/packages/router/src/new-route-resolver/matcher.ts index 26805ddaf..f9fa1c6f8 100644 --- a/packages/router/src/new-route-resolver/matcher.ts +++ b/packages/router/src/new-route-resolver/matcher.ts @@ -15,6 +15,8 @@ import { encodeQueryValue as _encodeQueryValue } from '../encoding' import { parseURL, stringifyURL } from '../location' import type { MatcherLocationAsNamed, + MatcherLocationAsPathAbsolute, + MatcherLocationAsPathRelative, MatcherLocationAsRelative, MatcherParamsFormatted, } from './matcher-location' @@ -48,10 +50,16 @@ export interface RouteResolver { resolve(location: MatcherLocationAsNamed): NEW_LocationResolved /** - * Resolves a location by its path. Any required query must be passed. + * Resolves a location by its absolute path (starts with `/`). Any required query must be passed. * @param location - The location to resolve. */ - // resolve(location: MatcherLocationAsPath): NEW_MatcherLocationResolved + resolve(location: MatcherLocationAsPathAbsolute): NEW_LocationResolved + + resolve( + location: MatcherLocationAsPathRelative, + currentLocation: NEW_LocationResolved + ): NEW_LocationResolved + // NOTE: in practice, this overload can cause bugs. It's better to use named locations /** @@ -66,11 +74,28 @@ export interface RouteResolver { addRoute(matcher: Matcher, parent?: MatcherNormalized): MatcherNormalized removeRoute(matcher: MatcherNormalized): void clearRoutes(): void + + /** + * Get a list of all matchers. + * Previously named `getRoutes()` + */ + getMatchers(): MatcherNormalized[] + + /** + * Get a matcher by its name. + * Previously named `getRecordMatcher()` + */ + getMatcher(name: MatcherName): MatcherNormalized | undefined } type MatcherResolveArgs = | [absoluteLocation: `/${string}`] | [relativeLocation: string, currentLocation: NEW_LocationResolved] + | [absoluteLocation: MatcherLocationAsPathAbsolute] + | [ + relativeLocation: MatcherLocationAsPathRelative, + currentLocation: NEW_LocationResolved + ] | [location: MatcherLocationAsNamed] | [ relativeLocation: MatcherLocationAsRelative, @@ -224,6 +249,7 @@ export function createCompiledMatcher(): RouteResolver< MatcherRecordRaw, MatcherPattern > { + // TODO: we also need an array that has the correct order const matchers = new Map() // TODO: allow custom encode/decode functions @@ -241,6 +267,7 @@ export function createCompiledMatcher(): RouteResolver< // string location, e.g. '/foo', '../bar', 'baz', '?page=1' if (typeof location === 'string') { + // parseURL handles relative paths const url = parseURL(parseQuery, location, currentLocation?.path) let matcher: MatcherPattern | undefined @@ -266,7 +293,6 @@ export function createCompiledMatcher(): RouteResolver< // } parsedParams = { ...pathParams, ...queryParams, ...hashParams } - // console.log('parsedParams', parsedParams) if (parsedParams) break } catch (e) { @@ -296,6 +322,7 @@ export function createCompiledMatcher(): RouteResolver< hash: url.hash, matched, } + // TODO: handle object location { path, query, hash } } else { // relative location or by name if (__DEV__ && location.name == null && currentLocation == null) { @@ -368,11 +395,21 @@ export function createCompiledMatcher(): RouteResolver< matchers.clear() } + function getMatchers() { + return Array.from(matchers.values()) + } + + function getMatcher(name: MatcherName) { + return matchers.get(name) + } + return { resolve, addRoute, removeRoute, clearRoutes, + getMatcher, + getMatchers, } }