diff --git a/examples/redirect/app.js b/examples/redirect/app.js index d81664c85..32beeb885 100644 --- a/examples/redirect/app.js +++ b/examples/redirect/app.js @@ -9,6 +9,8 @@ const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } const Baz = { template: '
baz
' } const WithParams = { template: '
{{ $route.params.id }}
' } +const Foobar = { template: '
foobar
' } +const FooBar = { template: '
FooBar
' } const router = new VueRouter({ mode: 'history', @@ -50,6 +52,12 @@ const router = new VueRouter({ // redirect with params { path: '/redirect-with-params/:id', redirect: '/with-params/:id' }, + // redirect with caseSensitive + { path: '/foobar', component: Foobar, caseSensitive: true }, + + // redirect with pathToRegexpOptions + { path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }}, + // catch all redirect { path: '*', redirect: '/' } ] @@ -97,6 +105,14 @@ new Vue({ /redirect-with-params/123 (redirects to /with-params/123) +
  • + /foobar +
  • + +
  • + /FooBar +
  • +
  • /not-found (redirects to /)
  • diff --git a/flow/declarations.js b/flow/declarations.js index 91ed03ef8..e62aeb9f9 100644 --- a/flow/declarations.js +++ b/flow/declarations.js @@ -4,9 +4,15 @@ declare class RouteRegExp extends RegExp { keys: Array<{ name: string, optional: boolean }>; } +declare type PathToRegexpOptions = { + sensitive?: boolean, + strict?: boolean, + end?: boolean +} + declare module 'path-to-regexp' { declare var exports: { - (path: string, keys?: Array): RouteRegExp; + (path: string, keys?: Array, options?: PathToRegexpOptions): RouteRegExp; compile: (path: string) => (params: Object) => string; } } @@ -48,6 +54,8 @@ declare type RouteConfig = { beforeEnter?: NavigationGuard; meta?: any; props?: boolean | Object | Function; + caseSensitive?: boolean; + pathToRegexpOptions?: PathToRegexpOptions; } declare type RouteRecord = { diff --git a/src/create-route-map.js b/src/create-route-map.js index c68d211b7..bd865a27c 100644 --- a/src/create-route-map.js +++ b/src/create-route-map.js @@ -58,9 +58,15 @@ function addRouteRecord ( } const normalizedPath = normalizePath(path, parent) + const pathToRegexpOptions: PathToRegexpOptions = route.pathToRegexpOptions || {} + + if (typeof route.caseSensitive === 'boolean') { + pathToRegexpOptions.sensitive = route.caseSensitive + } + const record: RouteRecord = { path: normalizedPath, - regex: compileRouteRegex(normalizedPath), + regex: compileRouteRegex(normalizedPath, pathToRegexpOptions), components: route.components || { default: route.component }, instances: {}, name, @@ -136,8 +142,8 @@ function addRouteRecord ( } } -function compileRouteRegex (path: string): RouteRegExp { - const regex = Regexp(path) +function compileRouteRegex (path: string, pathToRegexpOptions: PathToRegexpOptions): RouteRegExp { + const regex = Regexp(path, [], pathToRegexpOptions) if (process.env.NODE_ENV !== 'production') { const keys: any = {} regex.keys.forEach(key => { diff --git a/test/e2e/specs/redirect.js b/test/e2e/specs/redirect.js index f91e66342..ba39d3b7a 100644 --- a/test/e2e/specs/redirect.js +++ b/test/e2e/specs/redirect.js @@ -3,7 +3,7 @@ module.exports = { browser .url('http://localhost:8080/redirect/') .waitForElementVisible('#app', 1000) - .assert.count('li a', 10) + .assert.count('li a', 12) // assert correct href with base .assert.attributeContains('li:nth-child(1) a', 'href', '/redirect/relative-redirect') .assert.attributeContains('li:nth-child(2) a', 'href', '/redirect/relative-redirect?foo=bar') @@ -14,7 +14,9 @@ module.exports = { .assert.attributeContains('li:nth-child(7) a', 'href', '/redirect/dynamic-redirect#baz') .assert.attributeContains('li:nth-child(8) a', 'href', '/redirect/named-redirect') .assert.attributeContains('li:nth-child(9) a', 'href', '/redirect/redirect-with-params/123') - .assert.attributeContains('li:nth-child(10) a', 'href', '/not-found') + .assert.attributeContains('li:nth-child(10) a', 'href', '/redirect/foobar') + .assert.attributeContains('li:nth-child(11) a', 'href', '/redirect/FooBar') + .assert.attributeContains('li:nth-child(12) a', 'href', '/not-found') .assert.containsText('.view', 'default') @@ -55,6 +57,14 @@ module.exports = { .assert.containsText('.view', '123') .click('li:nth-child(10) a') + .assert.urlEquals('http://localhost:8080/redirect/foobar') + .assert.containsText('.view', 'foobar') + + .click('li:nth-child(11) a') + .assert.urlEquals('http://localhost:8080/redirect/FooBar') + .assert.containsText('.view', 'FooBar') + + .click('li:nth-child(12) a') .assert.urlEquals('http://localhost:8080/redirect/') .assert.containsText('.view', 'default') @@ -104,6 +114,16 @@ module.exports = { .assert.urlEquals('http://localhost:8080/redirect/with-params/123') .assert.containsText('.view', '123') + .url('http://localhost:8080/redirect/foobar') + .waitForElementVisible('#app', 1000) + .assert.urlEquals('http://localhost:8080/redirect/foobar') + .assert.containsText('.view', 'foobar') + + .url('http://localhost:8080/redirect/FooBar') + .waitForElementVisible('#app', 1000) + .assert.urlEquals('http://localhost:8080/redirect/FooBar') + .assert.containsText('.view', 'FooBar') + .url('http://localhost:8080/redirect/not-found') .waitForElementVisible('#app', 1000) .assert.urlEquals('http://localhost:8080/redirect/') diff --git a/test/e2e/specs/route-matching.js b/test/e2e/specs/route-matching.js index 79e5b3745..ea1ef3a29 100644 --- a/test/e2e/specs/route-matching.js +++ b/test/e2e/specs/route-matching.js @@ -124,7 +124,6 @@ module.exports = { }) ) }, null, '/optional-group/foo/bar') - .end() } } diff --git a/test/unit/specs/create-map.spec.js b/test/unit/specs/create-map.spec.js index ff1ccf46d..ad5803d1c 100644 --- a/test/unit/specs/create-map.spec.js +++ b/test/unit/specs/create-map.spec.js @@ -3,6 +3,8 @@ import { createRouteMap } from '../../../src/create-route-map' const Home = { template: '
    This is Home
    ' } const Foo = { template: '
    This is Foo
    ' } +const FooBar = { template: '
    This is FooBar
    ' } +const Foobar = { template: '
    This is foobar
    ' } const Bar = { template: '
    This is Bar
    ' } const Baz = { template: '
    This is Baz
    ' } @@ -74,4 +76,61 @@ describe('Creating Route Map', function () { expect(console.warn).toHaveBeenCalled() expect(console.warn.calls.argsFor(0)[0]).toMatch('vue-router] Duplicate param keys in route with path: "/foo/:id/bar/:id"') }) + + describe('path-to-regexp options', function () { + const routes = [ + { path: '/foo', name: 'foo', component: Foo }, + { path: '/bar', name: 'bar', component: Bar, caseSensitive: false }, + { path: '/FooBar', name: 'FooBar', component: FooBar, caseSensitive: true }, + { path: '/foobar', name: 'foobar', component: Foobar, caseSensitive: true } + ] + + it('caseSensitive option in route', function () { + const { nameMap } = createRouteMap(routes) + + expect(nameMap.FooBar.regex.ignoreCase).toBe(false) + expect(nameMap.bar.regex.ignoreCase).toBe(true) + expect(nameMap.foo.regex.ignoreCase).toBe(true) + }) + + it('pathToRegexpOptions option in route', function () { + const { nameMap } = createRouteMap([ + { + name: 'foo', + path: '/foo', + component: Foo, + pathToRegexpOptions: { + sensitive: true + } + }, + { + name: 'bar', + path: '/bar', + component: Bar, + pathToRegexpOptions: { + sensitive: false + } + } + ]) + + expect(nameMap.foo.regex.ignoreCase).toBe(false) + expect(nameMap.bar.regex.ignoreCase).toBe(true) + }) + + it('caseSensitive over pathToRegexpOptions in route', function () { + const { nameMap } = createRouteMap([ + { + name: 'foo', + path: '/foo', + component: Foo, + caseSensitive: true, + pathToRegexpOptions: { + sensitive: false + } + } + ]) + + expect(nameMap.foo.regex.ignoreCase).toBe(false) + }) + }) }) diff --git a/types/router.d.ts b/types/router.d.ts index ac702ab11..6992461de 100644 --- a/types/router.d.ts +++ b/types/router.d.ts @@ -60,6 +60,12 @@ export interface RouterOptions { type RoutePropsFunction = (route: Route) => Object; +export interface PathToRegexpOptions { + sensitive?: boolean; + strict?: boolean; + end?: boolean; +} + export interface RouteConfig { path: string; name?: string; @@ -71,6 +77,8 @@ export interface RouteConfig { meta?: any; beforeEnter?: NavigationGuard; props?: boolean | Object | RoutePropsFunction; + caseSensitive?: boolean; + pathToRegexpOptions?: PathToRegexpOptions; } export interface RouteRecord {