Skip to content

Commit

Permalink
feat: handle children
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Jan 9, 2025
1 parent 60564fe commit db5a85c
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 82 deletions.
10 changes: 0 additions & 10 deletions packages/router/__tests__/matcher/pathRanking.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,9 @@ describe('Path ranking', () => {
return comparePathParserScore(
{
score: a,
re: /a/,
// @ts-expect-error
stringify: v => v,
// @ts-expect-error
parse: v => v,
keys: [],
},
{
score: b,
re: /a/,
stringify: v => v,
parse: v => v,
keys: [],
}
)
}
Expand Down
44 changes: 25 additions & 19 deletions packages/router/src/experimental/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import {
type RouterHistory,
} from '../history/common'
import type { PathParserOptions } from '../matcher'
import type {
NEW_LocationResolved,
NEW_MatcherRecord,
NEW_MatcherRecordRaw,
NEW_RouterResolver,
import {
type NEW_MatcherRecordBase,
type NEW_LocationResolved,
type NEW_MatcherRecord,
type NEW_MatcherRecordRaw,
type NEW_RouterResolver,
} from '../new-route-resolver/resolver'
import {
parseQuery as originalParseQuery,
Expand Down Expand Up @@ -194,7 +195,7 @@ export interface EXPERIMENTAL_RouterOptions<
* Matcher to use to resolve routes.
* @experimental
*/
matcher: NEW_RouterResolver<NEW_MatcherRecordRaw, TMatcherRecord>
resolver: NEW_RouterResolver<NEW_MatcherRecordRaw, TMatcherRecord>
}

/**
Expand Down Expand Up @@ -411,14 +412,18 @@ export interface EXPERIMENTAL_RouteRecordRaw extends NEW_MatcherRecordRaw {
component?: unknown

redirect?: unknown
score: Array<number[]>
}

// TODO: is it worth to have 2 types for the undefined values?
export interface EXPERIMENTAL_RouteRecordNormalized extends NEW_MatcherRecord {
export interface EXPERIMENTAL_RouteRecordNormalized
extends NEW_MatcherRecordBase<EXPERIMENTAL_RouteRecordNormalized> {
/**
* Arbitrary data attached to the record.
*/
meta: RouteMeta
group?: boolean
score: Array<number[]>
}

function normalizeRouteRecord(
Expand All @@ -429,6 +434,7 @@ function normalizeRouteRecord(
name: __DEV__ ? Symbol('anonymous route record') : Symbol(),
meta: {},
...record,
children: (record.children || []).map(normalizeRouteRecord),
}
}

Expand All @@ -439,7 +445,7 @@ export function experimental_createRouter(
EXPERIMENTAL_RouteRecordNormalized
> {
const {
matcher,
resolver,
parseQuery = originalParseQuery,
stringifyQuery = originalStringifyQuery,
history: routerHistory,
Expand Down Expand Up @@ -470,11 +476,11 @@ export function experimental_createRouter(
| EXPERIMENTAL_RouteRecordRaw,
route?: EXPERIMENTAL_RouteRecordRaw
) {
let parent: Parameters<(typeof matcher)['addMatcher']>[1] | undefined
let parent: Parameters<(typeof resolver)['addMatcher']>[1] | undefined
let rawRecord: EXPERIMENTAL_RouteRecordRaw

if (isRouteName(parentOrRoute)) {
parent = matcher.getMatcher(parentOrRoute)
parent = resolver.getMatcher(parentOrRoute)
if (__DEV__ && !parent) {
warn(
`Parent route "${String(
Expand All @@ -488,31 +494,31 @@ export function experimental_createRouter(
rawRecord = parentOrRoute
}

const addedRecord = matcher.addMatcher(
const addedRecord = resolver.addMatcher(
normalizeRouteRecord(rawRecord),
parent
)

return () => {
matcher.removeMatcher(addedRecord)
resolver.removeMatcher(addedRecord)
}
}

function removeRoute(name: NonNullable<RouteRecordNameGeneric>) {
const recordMatcher = matcher.getMatcher(name)
const recordMatcher = resolver.getMatcher(name)
if (recordMatcher) {
matcher.removeMatcher(recordMatcher)
resolver.removeMatcher(recordMatcher)
} else if (__DEV__) {
warn(`Cannot remove non-existent route "${String(name)}"`)
}
}

function getRoutes() {
return matcher.getMatchers()
return resolver.getMatchers()
}

function hasRoute(name: NonNullable<RouteRecordNameGeneric>): boolean {
return !!matcher.getMatcher(name)
return !!resolver.getMatcher(name)
}

function locationAsObject(
Expand Down Expand Up @@ -567,7 +573,7 @@ export function experimental_createRouter(
// rawLocation.params = targetParams
// }

const matchedRoute = matcher.resolve(
const matchedRoute = resolver.resolve(
// incompatible types
rawLocation as any,
// incompatible `matched` requires casting
Expand Down Expand Up @@ -1226,7 +1232,7 @@ export function experimental_createRouter(

addRoute,
removeRoute,
clearRoutes: matcher.clearMatchers,
clearRoutes: resolver.clearMatchers,
hasRoute,
getRoutes,
resolve,
Expand Down Expand Up @@ -1307,7 +1313,7 @@ export function experimental_createRouter(
// TODO: this probably needs to be updated so it can be used by vue-termui
if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && isBrowser) {
// @ts-expect-error: FIXME: refactor with new types once it's possible
addDevtools(app, router, matcher)
addDevtools(app, router, resolver)
}
},
}
Expand Down
5 changes: 4 additions & 1 deletion packages/router/src/matcher/pathParserRanker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,10 @@ function compareScoreArray(a: number[], b: number[]): number {
* @param b - second PathParser
* @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
*/
export function comparePathParserScore(a: PathParser, b: PathParser): number {
export function comparePathParserScore(
a: Pick<PathParser, 'score'>,
b: Pick<PathParser, 'score'>
): number {
let i = 0
const aScore = a.score
const bScore = b.score
Expand Down
63 changes: 42 additions & 21 deletions packages/router/src/new-route-resolver/matcher-resolve.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,25 @@ function isMatchable(record: RouteRecordRaw): boolean {
)
}

function joinPaths(a: string | undefined, b: string) {
if (a?.endsWith('/')) {
return a + b
}
return a + '/' + b
}

function compileRouteRecord(
record: RouteRecordRaw,
parentRecord?: RouteRecordRaw
): NEW_MatcherRecordRaw {
// we adapt the path to ensure they are absolute
// TODO: aliases? they could be handled directly in the path matcher
if (!parentRecord && !record.path.startsWith('/')) {
throw new Error(`Record without parent must have an absolute path`)
}
const path = record.path.startsWith('/')
? record.path
: (parentRecord?.path || '') + record.path
: joinPaths(parentRecord?.path, record.path)
record.path = path
const parser = tokensToParser(
tokenizePath(record.path),
Expand All @@ -62,10 +72,12 @@ function compileRouteRecord(
return {
group: !isMatchable(record),
name: record.name,
score: parser.score,

path: {
match(value) {
const params = parser.parse(value)
// console.log('🌟', parser.re, value, params)
if (params) {
return params
}
Expand Down Expand Up @@ -181,20 +193,21 @@ describe('RouterMatcher.resolve', () => {
: matcher.resolve(
// FIXME: is this a ts bug?
// @ts-expect-error
typeof fromLocation === 'string'
? { path: fromLocation }
: fromLocation
fromLocation
)

// console.log(matcher.getMatchers())
// console.log({ toLocation, resolved, expectedLocation, resolvedFrom })

const result = matcher.resolve(
// FIXME: should work now
// @ts-expect-error
typeof toLocation === 'string' ? { path: toLocation } : toLocation,
toLocation,
resolvedFrom === START_LOCATION ? undefined : resolvedFrom
)

// console.log(result)

if (
expectedLocation.name === undefined ||
expectedLocation.name !== NO_MATCH_LOCATION.name
Expand Down Expand Up @@ -479,7 +492,7 @@ describe('RouterMatcher.resolve', () => {
// TODO: not sure where this warning should appear now
it.todo('warns if a path isn not absolute', () => {
const matcher = createCompiledMatcher([
{ path: new MatcherPatternPathStatic('/') },
{ path: new MatcherPatternPathStatic('/'), score: [[80]] },
])
matcher.resolve({ path: 'two' }, matcher.resolve({ path: '/' }))
expect('received "two"').toHaveBeenWarned()
Expand Down Expand Up @@ -1169,34 +1182,42 @@ describe('RouterMatcher.resolve', () => {
})
})

describe.skip('children', () => {
const ChildA = { path: 'a', name: 'child-a', components }
const ChildB = { path: 'b', name: 'child-b', components }
const ChildC = { path: 'c', name: 'child-c', components }
const ChildD = { path: '/absolute', name: 'absolute', components }
const ChildWithParam = { path: ':p', name: 'child-params', components }
const NestedChildWithParam = {
describe('children', () => {
const ChildA: RouteRecordRaw = { path: 'a', name: 'child-a', components }
const ChildB: RouteRecordRaw = { path: 'b', name: 'child-b', components }
const ChildC: RouteRecordRaw = { path: 'c', name: 'child-c', components }
const ChildD: RouteRecordRaw = {
path: '/absolute',
name: 'absolute',
components,
}
const ChildWithParam: RouteRecordRaw = {
path: ':p',
name: 'child-params',
components,
}
const NestedChildWithParam: RouteRecordRaw = {
...ChildWithParam,
name: 'nested-child-params',
}
const NestedChildA = { ...ChildA, name: 'nested-child-a' }
const NestedChildB = { ...ChildB, name: 'nested-child-b' }
const NestedChildC = { ...ChildC, name: 'nested-child-c' }
const Nested = {
const NestedChildA: RouteRecordRaw = { ...ChildA, name: 'nested-child-a' }
const NestedChildB: RouteRecordRaw = { ...ChildB, name: 'nested-child-b' }
const NestedChildC: RouteRecordRaw = { ...ChildC, name: 'nested-child-c' }
const Nested: RouteRecordRaw = {
path: 'nested',
name: 'nested',
components,
children: [NestedChildA, NestedChildB, NestedChildC],
}
const NestedWithParam = {
const NestedWithParam: RouteRecordRaw = {
path: 'nested/:n',
name: 'nested',
components,
children: [NestedChildWithParam],
}

it('resolves children', () => {
const Foo = {
const Foo: RouteRecordRaw = {
path: '/foo',
name: 'Foo',
components,
Expand All @@ -1216,8 +1237,8 @@ describe('RouterMatcher.resolve', () => {
})

it('resolves children with empty paths', () => {
const Nested = { path: '', name: 'nested', components }
const Foo = {
const Nested: RouteRecordRaw = { path: '', name: 'nested', components }
const Foo: RouteRecordRaw = {
path: '/foo',
name: 'Foo',
components,
Expand Down
14 changes: 14 additions & 0 deletions packages/router/src/new-route-resolver/matchers/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,23 @@ export const ANY_HASH_PATTERN_MATCHER: MatcherPatternParams_Base<
export const EMPTY_PATH_ROUTE = {
name: 'no params',
path: EMPTY_PATH_PATTERN_MATCHER,
score: [[80]],
children: [],
parent: undefined,
} satisfies NEW_MatcherRecord

export const ANY_PATH_ROUTE = {
name: 'any path',
path: ANY_PATH_PATTERN_MATCHER,
score: [[-10]],
children: [],
parent: undefined,
} satisfies NEW_MatcherRecord

export const USER_ID_ROUTE = {
name: 'user-id',
path: USER_ID_PATH_PATTERN_MATCHER,
score: [[80], [70]],
children: [],
parent: undefined,
} satisfies NEW_MatcherRecord
Loading

0 comments on commit db5a85c

Please sign in to comment.