Skip to content

Commit

Permalink
refactor: renames and minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Dec 6, 2024
1 parent b57cb21 commit d462758
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 24 deletions.
2 changes: 1 addition & 1 deletion packages/router/src/new-route-resolver/matcher-location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { MatcherName } from './matcher'
*/
export type MatcherParamsFormatted = Record<string, unknown>

export interface MatcherLocationAsName {
export interface MatcherLocationAsNamed {
name: MatcherName
params: MatcherParamsFormatted
query?: LocationQueryRaw
Expand Down
9 changes: 5 additions & 4 deletions packages/router/src/new-route-resolver/matcher-pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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&notUsed&notUsed=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
* ```
Expand Down Expand Up @@ -109,6 +109,7 @@ export interface PatternPathParamOptions<T = unknown>

export interface PatternQueryParamOptions<T = unknown>
extends PatternParamOptions_Base<T> {
// FIXME: can be removed? seems to be the same as above
get: (value: MatcherQueryParamsValue) => T
set?: (value: T) => MatcherQueryParamsValue
}
Expand Down Expand Up @@ -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),
Expand Down
48 changes: 37 additions & 11 deletions packages/router/src/new-route-resolver/matcher.test-d.ts
Original file line number Diff line number Diff line change
@@ -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<NEW_LocationResolved>()
})

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<NEW_LocationResolved>()
})

it('resolved named locations', () => {
expectTypeOf(
matcher.resolve({ name: 'foo', params: {} })
).toEqualTypeOf<NEW_LocationResolved>()
})

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<NEW_LocationResolved>()
})
})
})
37 changes: 29 additions & 8 deletions packages/router/src/new-route-resolver/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../encoding'
import { parseURL, stringifyURL } from '../location'
import type {
MatcherLocationAsName,
MatcherLocationAsNamed,
MatcherLocationAsRelative,
MatcherParamsFormatted,
} from './matcher-location'
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -108,7 +108,11 @@ export type MatcherPathParams = Record<string, MatcherPathParamsValue>
export type MatcherQueryParamsValue = string | null | Array<string | null>
export type MatcherQueryParams = Record<string, MatcherQueryParamsValue>

export function applyToParams<R>(
/**
* Apply a function to all properties in an object. It's used to encode/decode params and queries.
* @internal
*/
export function applyFnToObject<R>(
fn: (v: string | number | null | undefined) => R,
params: MatcherPathParams | LocationQuery | undefined
): Record<string, R | R[]> {
Expand Down Expand Up @@ -195,7 +199,7 @@ function transformObject<T>(
}

export const NO_MATCH_LOCATION = {
name: Symbol('no-match'),
name: __DEV__ ? Symbol('no-match') : Symbol(),
params: {},
matched: [],
} satisfies Omit<NEW_LocationResolved, 'path' | 'hash' | 'query' | 'fullPath'>
Expand All @@ -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
Expand Down Expand Up @@ -257,14 +262,30 @@ 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) {
throw new Error(`Matcher "${String(location.name)}" not found`)
}

// 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) {
Expand Down

0 comments on commit d462758

Please sign in to comment.