require('./helper')
const expect = require('expect')
const { createRouteMatcher } = require('../src/matcher')
-const { START_LOCATION_NORMALIZED } = require('../src/types')
-const { normalizeRouteRecord } = require('./utils')
/** @type {RouteComponent} */
const component = null
/** @typedef {import('../src/types').MatcherLocation} MatcherLocation */
/** @typedef {import('../src/types').MatcherLocationRedirect} MatcherLocationRedirect */
/** @typedef {import('../src/types').MatcherLocationNormalized} MatcherLocationNormalized */
+/** @typedef {import('../src/matcher').RouteMatcher} RouteMatcher */
/** @typedef {import('path-to-regexp').RegExpOptions} RegExpOptions */
function stringifyOptions(options) {
return [pathOrCombined[0], { ...options, ...pathOrCombined[1] }]
return [pathOrCombined, options]
})
+
+ /** @type {Array<RouteMatcher & { _options: RegExpOptions }>} */
const matchers = normalizedPaths
.slice()
// Because sorting order is conserved, allows to mismatch order on
// routes with the same ranking
.reverse()
- .map(([path, options]) =>
- createRouteMatcher(
+ .map(([path, options]) => ({
+ ...createRouteMatcher(
{
// @ts-ignore types are correct
path,
},
null,
options
- )
- )
+ ),
+ // add original options
+ _options: options,
+ }))
.sort((a, b) => b.score - a.score)
expect(matchers.map(matcher => matcher.record.path)).toEqual(
} catch (e) {
throw new Error(
`Record "${a.record.path}"${stringifyOptions(
- normalizedPaths[i - 1][1]
+ matchers[i - 1]._options
)} and "${b.record.path}"${stringifyOptions(
- normalizedPaths[i][1]
+ matchers[i]._options
)} have the same score: ${
a.score
}. Avoid putting routes with the same score on the same test`
])
})
+ it('ending slashes less than params', () => {
+ checkPathOrder([
+ ['/a/:b/', { strict: true }],
+ ['/a/b', { strict: false }],
+ ['/a/:b', { strict: true }],
+ ])
+ })
+
it('prioritizes ending slashes', () => {
checkPathOrder([
// no strict
'/a',
])
})
+
+ it('prioritizes case sensitive', () => {
+ checkPathOrder([
+ ['/a/', { sensitive: true }],
+ '/a/', // explicit ending slash
+ ['/a', { sensitive: true }],
+ '/a', // also matches /A
+ ])
+ })
})
type NormalizedRouteRecord = Exclude<RouteRecord, { component: any }> // normalize component/components into components
-interface RouteMatcher {
+export interface RouteMatcher {
re: RegExp
resolve: (params?: RouteParams) => string
record: NormalizedRouteRecord
SubWildcard = 1, // Wildcard as a subsegment
Repeatable = -0.5, // /:w+ or /:w*
Strict = 0.5, // when options strict: true is passed, as the regex omits \/?
+ CaseSensitive = 0.25, // when options strict: true is passed, as the regex omits \/?
Optional = -4, // /:w? or /:w*
SubOptional = -0.1, // optional inside a subsegment /a-:w? or /a-:w*
Root = 1, // just /
// to compute the score of routes
const resolve = pathToRegexp.tokensToFunction([...tokens])
- let score = options.strict ? PathScore.Strict : 0
+ let score =
+ (options.strict ? PathScore.Strict : 0) +
+ (options.sensitive ? PathScore.CaseSensitive : 0)
// console.log(tokens)
// console.log('--- GROUPING ---')
// special case for root path
if (tokens.length === 1 && tokens[0] === '/') {
- score = 5
+ score = PathScore.Segment + PathScore.Root
} else {
// allows us to group tokens into one single segment
// it will point to the first token of the current group
for (const childRecord of record.children) {
this.addRouteRecord(childRecord, matcher)
}
+ // TODO: the parent is special, we should match their children. They
+ // reference to the parent so we can render the parent
+ //
+ // matcher.score = -10
}
this.insertMatcher(matcher)