Skip to content

Commit 52f4a9a

Browse files
smitt04yyx990803
authored andcommitted
Case Sensitive Option for Routes #1214 (#1215)
* added caseSensitive and pathToRegexpOptions to RouteConfig and pathToRegexpOptions to RouteRecord. Used to support senstive matches and pass along more options to pathToRegexp. #1214 * changed test for /FooBar to redirect to /FOOBAR * fixed tests and added 2 tests for case sensitive redirects * added some route-matching tests * Update create-route-map.js * Update route-matching.js * Update code to use caseSensitive option * Add unit test for caseSensitive * Add test for pathToRegexpOptions in route * Fix flow typings * Add ts types * Remove old test
1 parent 7646663 commit 52f4a9a

File tree

7 files changed

+123
-7
lines changed

7 files changed

+123
-7
lines changed

examples/redirect/app.js

+16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const Foo = { template: '<div>foo</div>' }
99
const Bar = { template: '<div>bar</div>' }
1010
const Baz = { template: '<div>baz</div>' }
1111
const WithParams = { template: '<div>{{ $route.params.id }}</div>' }
12+
const Foobar = { template: '<div>foobar</div>' }
13+
const FooBar = { template: '<div>FooBar</div>' }
1214

1315
const router = new VueRouter({
1416
mode: 'history',
@@ -50,6 +52,12 @@ const router = new VueRouter({
5052
// redirect with params
5153
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' },
5254

55+
// redirect with caseSensitive
56+
{ path: '/foobar', component: Foobar, caseSensitive: true },
57+
58+
// redirect with pathToRegexpOptions
59+
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }},
60+
5361
// catch all redirect
5462
{ path: '*', redirect: '/' }
5563
]
@@ -97,6 +105,14 @@ new Vue({
97105
/redirect-with-params/123 (redirects to /with-params/123)
98106
</router-link></li>
99107
108+
<li><router-link to="/foobar">
109+
/foobar
110+
</router-link></li>
111+
112+
<li><router-link to="/FooBar">
113+
/FooBar
114+
</router-link></li>
115+
100116
<li><router-link to="/not-found">
101117
/not-found (redirects to /)
102118
</router-link></li>

flow/declarations.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ declare class RouteRegExp extends RegExp {
44
keys: Array<{ name: string, optional: boolean }>;
55
}
66

7+
declare type PathToRegexpOptions = {
8+
sensitive?: boolean,
9+
strict?: boolean,
10+
end?: boolean
11+
}
12+
713
declare module 'path-to-regexp' {
814
declare var exports: {
9-
(path: string, keys?: Array<?{ name: string }>): RouteRegExp;
15+
(path: string, keys?: Array<?{ name: string }>, options?: PathToRegexpOptions): RouteRegExp;
1016
compile: (path: string) => (params: Object) => string;
1117
}
1218
}
@@ -48,6 +54,8 @@ declare type RouteConfig = {
4854
beforeEnter?: NavigationGuard;
4955
meta?: any;
5056
props?: boolean | Object | Function;
57+
caseSensitive?: boolean;
58+
pathToRegexpOptions?: PathToRegexpOptions;
5159
}
5260

5361
declare type RouteRecord = {

src/create-route-map.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,15 @@ function addRouteRecord (
5858
}
5959

6060
const normalizedPath = normalizePath(path, parent)
61+
const pathToRegexpOptions: PathToRegexpOptions = route.pathToRegexpOptions || {}
62+
63+
if (typeof route.caseSensitive === 'boolean') {
64+
pathToRegexpOptions.sensitive = route.caseSensitive
65+
}
66+
6167
const record: RouteRecord = {
6268
path: normalizedPath,
63-
regex: compileRouteRegex(normalizedPath),
69+
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
6470
components: route.components || { default: route.component },
6571
instances: {},
6672
name,
@@ -132,8 +138,8 @@ function addRouteRecord (
132138
}
133139
}
134140

135-
function compileRouteRegex (path: string): RouteRegExp {
136-
const regex = Regexp(path)
141+
function compileRouteRegex (path: string, pathToRegexpOptions: PathToRegexpOptions): RouteRegExp {
142+
const regex = Regexp(path, [], pathToRegexpOptions)
137143
if (process.env.NODE_ENV !== 'production') {
138144
const keys: any = {}
139145
regex.keys.forEach(key => {

test/e2e/specs/redirect.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module.exports = {
33
browser
44
.url('http://localhost:8080/redirect/')
55
.waitForElementVisible('#app', 1000)
6-
.assert.count('li a', 10)
6+
.assert.count('li a', 12)
77
// assert correct href with base
88
.assert.attributeContains('li:nth-child(1) a', 'href', '/redirect/relative-redirect')
99
.assert.attributeContains('li:nth-child(2) a', 'href', '/redirect/relative-redirect?foo=bar')
@@ -14,7 +14,9 @@ module.exports = {
1414
.assert.attributeContains('li:nth-child(7) a', 'href', '/redirect/dynamic-redirect#baz')
1515
.assert.attributeContains('li:nth-child(8) a', 'href', '/redirect/named-redirect')
1616
.assert.attributeContains('li:nth-child(9) a', 'href', '/redirect/redirect-with-params/123')
17-
.assert.attributeContains('li:nth-child(10) a', 'href', '/not-found')
17+
.assert.attributeContains('li:nth-child(10) a', 'href', '/redirect/foobar')
18+
.assert.attributeContains('li:nth-child(11) a', 'href', '/redirect/FooBar')
19+
.assert.attributeContains('li:nth-child(12) a', 'href', '/not-found')
1820

1921
.assert.containsText('.view', 'default')
2022

@@ -55,6 +57,14 @@ module.exports = {
5557
.assert.containsText('.view', '123')
5658

5759
.click('li:nth-child(10) a')
60+
.assert.urlEquals('http://localhost:8080/redirect/foobar')
61+
.assert.containsText('.view', 'foobar')
62+
63+
.click('li:nth-child(11) a')
64+
.assert.urlEquals('http://localhost:8080/redirect/FooBar')
65+
.assert.containsText('.view', 'FooBar')
66+
67+
.click('li:nth-child(12) a')
5868
.assert.urlEquals('http://localhost:8080/redirect/')
5969
.assert.containsText('.view', 'default')
6070

@@ -104,6 +114,16 @@ module.exports = {
104114
.assert.urlEquals('http://localhost:8080/redirect/with-params/123')
105115
.assert.containsText('.view', '123')
106116

117+
.url('http://localhost:8080/redirect/foobar')
118+
.waitForElementVisible('#app', 1000)
119+
.assert.urlEquals('http://localhost:8080/redirect/foobar')
120+
.assert.containsText('.view', 'foobar')
121+
122+
.url('http://localhost:8080/redirect/FooBar')
123+
.waitForElementVisible('#app', 1000)
124+
.assert.urlEquals('http://localhost:8080/redirect/FooBar')
125+
.assert.containsText('.view', 'FooBar')
126+
107127
.url('http://localhost:8080/redirect/not-found')
108128
.waitForElementVisible('#app', 1000)
109129
.assert.urlEquals('http://localhost:8080/redirect/')

test/e2e/specs/route-matching.js

-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ module.exports = {
124124
})
125125
)
126126
}, null, '/optional-group/foo/bar')
127-
128127
.end()
129128
}
130129
}

test/unit/specs/create-map.spec.js

+59
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { createRouteMap } from '../../../src/create-route-map'
33

44
const Home = { template: '<div>This is Home</div>' }
55
const Foo = { template: '<div>This is Foo</div>' }
6+
const FooBar = { template: '<div>This is FooBar</div>' }
7+
const Foobar = { template: '<div>This is foobar</div>' }
68
const Bar = { template: '<div>This is Bar <router-view></router-view></div>' }
79
const Baz = { template: '<div>This is Baz</div>' }
810

@@ -74,4 +76,61 @@ describe('Creating Route Map', function () {
7476
expect(console.warn).toHaveBeenCalled()
7577
expect(console.warn.calls.argsFor(0)[0]).toMatch('vue-router] Duplicate param keys in route with path: "/foo/:id/bar/:id"')
7678
})
79+
80+
describe('path-to-regexp options', function () {
81+
const routes = [
82+
{ path: '/foo', name: 'foo', component: Foo },
83+
{ path: '/bar', name: 'bar', component: Bar, caseSensitive: false },
84+
{ path: '/FooBar', name: 'FooBar', component: FooBar, caseSensitive: true },
85+
{ path: '/foobar', name: 'foobar', component: Foobar, caseSensitive: true }
86+
]
87+
88+
it('caseSensitive option in route', function () {
89+
const { nameMap } = createRouteMap(routes)
90+
91+
expect(nameMap.FooBar.regex.ignoreCase).toBe(false)
92+
expect(nameMap.bar.regex.ignoreCase).toBe(true)
93+
expect(nameMap.foo.regex.ignoreCase).toBe(true)
94+
})
95+
96+
it('pathToRegexpOptions option in route', function () {
97+
const { nameMap } = createRouteMap([
98+
{
99+
name: 'foo',
100+
path: '/foo',
101+
component: Foo,
102+
pathToRegexpOptions: {
103+
sensitive: true
104+
}
105+
},
106+
{
107+
name: 'bar',
108+
path: '/bar',
109+
component: Bar,
110+
pathToRegexpOptions: {
111+
sensitive: false
112+
}
113+
}
114+
])
115+
116+
expect(nameMap.foo.regex.ignoreCase).toBe(false)
117+
expect(nameMap.bar.regex.ignoreCase).toBe(true)
118+
})
119+
120+
it('caseSensitive over pathToRegexpOptions in route', function () {
121+
const { nameMap } = createRouteMap([
122+
{
123+
name: 'foo',
124+
path: '/foo',
125+
component: Foo,
126+
caseSensitive: true,
127+
pathToRegexpOptions: {
128+
sensitive: false
129+
}
130+
}
131+
])
132+
133+
expect(nameMap.foo.regex.ignoreCase).toBe(false)
134+
})
135+
})
77136
})

types/router.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export interface RouterOptions {
6161

6262
type RoutePropsFunction = (route: Route) => Object;
6363

64+
export interface PathToRegexpOptions {
65+
sensitive?: boolean;
66+
strict?: boolean;
67+
end?: boolean;
68+
}
69+
6470
export interface RouteConfig {
6571
path: string;
6672
name?: string;
@@ -72,6 +78,8 @@ export interface RouteConfig {
7278
meta?: any;
7379
beforeEnter?: NavigationGuard;
7480
props?: boolean | Object | RoutePropsFunction;
81+
caseSensitive?: boolean;
82+
pathToRegexpOptions?: PathToRegexpOptions;
7583
}
7684

7785
export interface RouteRecord {

0 commit comments

Comments
 (0)