Skip to content

Commit 4bea66a

Browse files
committed
make patternCompiler configurable
1 parent 373c2da commit 4bea66a

File tree

4 files changed

+67
-60
lines changed

4 files changed

+67
-60
lines changed

lib/path.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
import invariant from './invariant.js'
2-
import { pathToRegexp } from 'path-to-regexp'
32

43
const paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?+*]?)/g
54
const specialParamChars = /[+*?]$/g
65
const queryMatcher = /\?(.+)/
76

87
const _compiledPatterns = {}
98

10-
function compilePattern (pattern) {
9+
function compilePattern (pattern, compiler) {
1110
if (!(pattern in _compiledPatterns)) {
12-
const paramNames = []
13-
const re = pathToRegexp(pattern, paramNames)
14-
15-
_compiledPatterns[pattern] = {
16-
matcher: re,
17-
paramNames: paramNames.map(p => p.name)
18-
}
11+
_compiledPatterns[pattern] = compiler(pattern)
1912
}
2013

2114
return _compiledPatterns[pattern]
@@ -24,17 +17,17 @@ function compilePattern (pattern) {
2417
/**
2518
* Returns an array of the names of all parameters in the given pattern.
2619
*/
27-
export function extractParamNames (pattern) {
28-
return compilePattern(pattern).paramNames
20+
export function extractParamNames (pattern, compiler) {
21+
return compilePattern(pattern, compiler).paramNames
2922
}
3023

3124
/**
3225
* Extracts the portions of the given URL path that match the given pattern
3326
* and returns an object of param name => value pairs. Returns null if the
3427
* pattern does not match the given path.
3528
*/
36-
export function extractParams (pattern, path) {
37-
const cp = compilePattern(pattern)
29+
export function extractParams (pattern, path, compiler) {
30+
const cp = compilePattern(pattern, compiler)
3831
const matcher = cp.matcher
3932
const paramNames = cp.paramNames
4033
const match = path.match(matcher)

lib/patternCompiler.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { pathToRegexp } from 'path-to-regexp'
2+
3+
export function patternCompiler (pattern) {
4+
const paramNames = []
5+
const re = pathToRegexp(pattern, paramNames)
6+
7+
return {
8+
matcher: re,
9+
paramNames: paramNames.map(p => p.name)
10+
}
11+
}

lib/router.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import transition from './transition.js'
1010
import { intercept } from './links.js'
1111
import defineLogger from './logger.js'
1212
import qs from './qs.js'
13+
import { patternCompiler } from './patternCompiler.js'
1314
import { TRANSITION_CANCELLED, TRANSITION_REDIRECTED } from './constants.js'
1415

1516
class Router {
@@ -20,7 +21,8 @@ class Router {
2021
this.options = extend({
2122
location: 'browser',
2223
logError: true,
23-
qs
24+
qs,
25+
patternCompiler
2426
}, options)
2527
defineLogger(this, 'log', this.options.log)
2628
defineLogger(this, 'logError', this.options.logError)
@@ -291,9 +293,9 @@ class Router {
291293
let params
292294
let routes = []
293295
const pathWithoutQuery = withoutQuery(path)
294-
const qs = this.options.qs
296+
const { qs, patternCompiler } = this.options
295297
this.matchers.some(matcher => {
296-
params = extractParams(matcher.path, pathWithoutQuery)
298+
params = extractParams(matcher.path, pathWithoutQuery, patternCompiler)
297299
if (params) {
298300
routes = matcher.routes
299301
return true
@@ -316,7 +318,7 @@ class Router {
316318
return {
317319
name: route.name,
318320
path: route.path,
319-
params: pick(params, extractParamNames(route.path)),
321+
params: pick(params, extractParamNames(route.path, patternCompiler)),
320322
options: clone(route.options)
321323
}
322324
}

tests/unit/pathTest.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,72 @@
11
import qs from '../../lib/qs'
22
import * as Path from '../../lib/path'
33
import 'chai/chai.js'
4+
import { patternCompiler } from '../../lib/patternCompiler.js'
45

56
const { assert } = window.chai
67
const { describe, it } = window
78

89
describe('Path', () => {
910
it('Path.extractParamNames', () => {
10-
assert.deepEqual(Path.extractParamNames('a/b/c'), [])
11-
assert.deepEqual(Path.extractParamNames('/comments/:a/:b/edit'), ['a', 'b'])
12-
assert.deepEqual(Path.extractParamNames('/files/:path*.jpg'), ['path'])
11+
assert.deepEqual(Path.extractParamNames('a/b/c', patternCompiler), [])
12+
assert.deepEqual(Path.extractParamNames('/comments/:a/:b/edit', patternCompiler), ['a', 'b'])
13+
assert.deepEqual(Path.extractParamNames('/files/:path*.jpg', patternCompiler), ['path'])
1314
})
1415

1516
it('Path.extractParams', () => {
16-
assert.deepEqual(Path.extractParams('a/b/c', 'a/b/c'), {})
17-
assert.deepEqual(Path.extractParams('a/b/c', 'd/e/f'), null)
17+
assert.deepEqual(Path.extractParams('a/b/c', 'a/b/c', patternCompiler), {})
18+
assert.deepEqual(Path.extractParams('a/b/c', 'd/e/f', patternCompiler), null)
1819

19-
assert.deepEqual(Path.extractParams('comments/:id.:ext/edit', 'comments/abc.js/edit'), { id: 'abc', ext: 'js' })
20+
assert.deepEqual(Path.extractParams('comments/:id.:ext/edit', 'comments/abc.js/edit', patternCompiler), { id: 'abc', ext: 'js' })
2021

21-
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments/123/edit'), { id: '123' })
22-
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments/the%2Fid/edit'), { id: 'the/id' })
23-
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments//edit'), null)
24-
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'users/123'), null)
22+
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments/123/edit', patternCompiler), { id: '123' })
23+
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments/the%2Fid/edit', patternCompiler), { id: 'the/id' })
24+
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'comments//edit', patternCompiler), null)
25+
assert.deepEqual(Path.extractParams('comments/:id?/edit', 'users/123', patternCompiler), null)
2526

26-
assert.deepEqual(Path.extractParams('one, two', 'one, two'), {})
27-
assert.deepEqual(Path.extractParams('one, two', 'one two'), null)
27+
assert.deepEqual(Path.extractParams('one, two', 'one, two', patternCompiler), {})
28+
assert.deepEqual(Path.extractParams('one, two', 'one two', patternCompiler), null)
2829

29-
assert.deepEqual(Path.extractParams('/comments/:id/edit now', '/comments/abc/edit now'), { id: 'abc' })
30-
assert.deepEqual(Path.extractParams('/comments/:id/edit now', '/users/123'), null)
30+
assert.deepEqual(Path.extractParams('/comments/:id/edit now', '/comments/abc/edit now', patternCompiler), { id: 'abc' })
31+
assert.deepEqual(Path.extractParams('/comments/:id/edit now', '/users/123', patternCompiler), null)
3132

32-
assert.deepEqual(Path.extractParams('/files/:path*', '/files/my/photo.jpg'), { path: 'my/photo.jpg' })
33-
assert.deepEqual(Path.extractParams('/files/:path*', '/files/my/photo.jpg.zip'), { path: 'my/photo.jpg.zip' })
34-
assert.deepEqual(Path.extractParams('/files/:path*.jpg', '/files/my%2Fphoto.jpg'), { path: 'my/photo' })
35-
assert.deepEqual(Path.extractParams('/files/:path*', '/files'), { path: undefined })
36-
assert.deepEqual(Path.extractParams('/files/:path*', '/files/'), { path: undefined })
37-
assert.deepEqual(Path.extractParams('/files/:path*.jpg', '/files/my/photo.png'), null)
33+
assert.deepEqual(Path.extractParams('/files/:path*', '/files/my/photo.jpg', patternCompiler), { path: 'my/photo.jpg' })
34+
assert.deepEqual(Path.extractParams('/files/:path*', '/files/my/photo.jpg.zip', patternCompiler), { path: 'my/photo.jpg.zip' })
35+
assert.deepEqual(Path.extractParams('/files/:path*.jpg', '/files/my%2Fphoto.jpg', patternCompiler), { path: 'my/photo' })
36+
assert.deepEqual(Path.extractParams('/files/:path*', '/files', patternCompiler), { path: undefined })
37+
assert.deepEqual(Path.extractParams('/files/:path*', '/files/', patternCompiler), { path: undefined })
38+
assert.deepEqual(Path.extractParams('/files/:path*.jpg', '/files/my/photo.png', patternCompiler), null)
3839

3940
// splat with named
40-
assert.deepEqual(Path.extractParams('/files/:path*.:ext', '/files/my/photo.jpg'), { path: 'my/photo', ext: 'jpg' })
41+
assert.deepEqual(Path.extractParams('/files/:path*.:ext', '/files/my/photo.jpg', patternCompiler), { path: 'my/photo', ext: 'jpg' })
4142

4243
// multiple splats
43-
assert.deepEqual(Path.extractParams('/files/:path*\\.:ext*', '/files/my/photo.jpg/gif'), { path: 'my/photo', ext: 'jpg/gif' })
44+
assert.deepEqual(Path.extractParams('/files/:path*\\.:ext*', '/files/my/photo.jpg/gif', patternCompiler), { path: 'my/photo', ext: 'jpg/gif' })
4445

4546
// one more more segments
46-
assert.deepEqual(Path.extractParams('/files/:path+', '/files/my/photo.jpg'), { path: 'my/photo.jpg' })
47-
assert.deepEqual(Path.extractParams('/files/:path+', '/files/my/photo.jpg.zip'), { path: 'my/photo.jpg.zip' })
48-
assert.deepEqual(Path.extractParams('/files/:path+.jpg', '/files/my/photo.jpg'), { path: 'my/photo' })
49-
assert.deepEqual(Path.extractParams('/files/:path+', '/files'), null)
50-
assert.deepEqual(Path.extractParams('/files/:path+', '/files/'), null)
51-
assert.deepEqual(Path.extractParams('/files/:path+.jpg', '/files/my/photo.png'), null)
52-
53-
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive'), { name: undefined })
54-
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive/'), { name: undefined })
55-
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive/foo'), { name: 'foo' })
56-
assert.deepEqual(Path.extractParams('/archive/:name?', '/archivefoo'), null)
57-
assert.deepEqual(Path.extractParams('/archive/:name?', '/archiv'), null)
58-
59-
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo/with/foo.app'), { query: 'foo', domain: 'foo.app' })
60-
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap/with/foo'), { query: 'foo.ap', domain: 'foo' })
61-
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap/with/foo.app'), { query: 'foo.ap', domain: 'foo.app' })
62-
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap'), null)
47+
assert.deepEqual(Path.extractParams('/files/:path+', '/files/my/photo.jpg', patternCompiler), { path: 'my/photo.jpg' })
48+
assert.deepEqual(Path.extractParams('/files/:path+', '/files/my/photo.jpg.zip', patternCompiler), { path: 'my/photo.jpg.zip' })
49+
assert.deepEqual(Path.extractParams('/files/:path+.jpg', '/files/my/photo.jpg', patternCompiler), { path: 'my/photo' })
50+
assert.deepEqual(Path.extractParams('/files/:path+', '/files', patternCompiler), null)
51+
assert.deepEqual(Path.extractParams('/files/:path+', '/files/', patternCompiler), null)
52+
assert.deepEqual(Path.extractParams('/files/:path+.jpg', '/files/my/photo.png', patternCompiler), null)
53+
54+
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive', patternCompiler), { name: undefined })
55+
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive/', patternCompiler), { name: undefined })
56+
assert.deepEqual(Path.extractParams('/archive/:name?', '/archive/foo', patternCompiler), { name: 'foo' })
57+
assert.deepEqual(Path.extractParams('/archive/:name?', '/archivefoo', patternCompiler), null)
58+
assert.deepEqual(Path.extractParams('/archive/:name?', '/archiv', patternCompiler), null)
59+
60+
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo/with/foo.app', patternCompiler), { query: 'foo', domain: 'foo.app' })
61+
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap/with/foo', patternCompiler), { query: 'foo.ap', domain: 'foo' })
62+
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap/with/foo.app', patternCompiler), { query: 'foo.ap', domain: 'foo.app' })
63+
assert.deepEqual(Path.extractParams('/:query/with/:domain', '/foo.ap', patternCompiler), null)
6364

6465
// advanced use case of making params in the middle of the url optional
65-
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/123/edit'), { id: '123/edit' })
66-
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/edit'), { id: 'edit' })
67-
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/editor'), null)
68-
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/123'), null)
66+
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/123/edit', patternCompiler), { id: '123/edit' })
67+
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/edit', patternCompiler), { id: 'edit' })
68+
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/editor', patternCompiler), null)
69+
assert.deepEqual(Path.extractParams('/comments/:id(.*/?edit)', '/comments/123', patternCompiler), null)
6970
})
7071

7172
it('Path.injectParams', () => {

0 commit comments

Comments
 (0)