From: Eduardo San Martin Morote Date: Wed, 17 Jul 2019 14:43:53 +0000 (+0200) Subject: feat(matcher): allow case sensitive routes X-Git-Tag: v4.0.0-alpha.0~295 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=123203444b9b7d036c44fdc41e437f55ae7f9cd4;p=thirdparty%2Fvuejs%2Frouter.git feat(matcher): allow case sensitive routes --- diff --git a/__tests__/matcher-ranking.spec.js b/__tests__/matcher-ranking.spec.js index 77483e2d..6b94bf4f 100644 --- a/__tests__/matcher-ranking.spec.js +++ b/__tests__/matcher-ranking.spec.js @@ -2,8 +2,6 @@ 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 @@ -14,6 +12,7 @@ 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) { @@ -32,13 +31,15 @@ describe('createRouteMatcher', () => { return [pathOrCombined[0], { ...options, ...pathOrCombined[1] }] return [pathOrCombined, options] }) + + /** @type {Array} */ 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, @@ -46,8 +47,10 @@ describe('createRouteMatcher', () => { }, null, options - ) - ) + ), + // add original options + _options: options, + })) .sort((a, b) => b.score - a.score) expect(matchers.map(matcher => matcher.record.path)).toEqual( @@ -63,9 +66,9 @@ describe('createRouteMatcher', () => { } 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` @@ -136,6 +139,14 @@ describe('createRouteMatcher', () => { ]) }) + 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 @@ -156,4 +167,13 @@ describe('createRouteMatcher', () => { '/a', ]) }) + + it('prioritizes case sensitive', () => { + checkPathOrder([ + ['/a/', { sensitive: true }], + '/a/', // explicit ending slash + ['/a', { sensitive: true }], + '/a', // also matches /A + ]) + }) }) diff --git a/src/matcher.ts b/src/matcher.ts index 61734381..f6d34fd3 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -12,7 +12,7 @@ import { NoRouteMatchError, InvalidRouteMatch } from './errors' type NormalizedRouteRecord = Exclude // normalize component/components into components -interface RouteMatcher { +export interface RouteMatcher { re: RegExp resolve: (params?: RouteParams) => string record: NormalizedRouteRecord @@ -57,6 +57,7 @@ enum PathScore { 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 / @@ -78,14 +79,16 @@ export function createRouteMatcher( // 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 @@ -293,6 +296,10 @@ export class RouterMatcher { 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)