1
+ /* jshint -W084 */
1
2
var invariant = require ( 'react/lib/invariant' ) ;
2
3
var assign = require ( 'object-assign' ) ;
3
4
var qs = require ( 'qs' ) ;
4
5
5
- var paramCompileMatcher = / : ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) | [ * . ( ) \[ \] \\ + | { } ^ $ ] / g;
6
- var paramInjectMatcher = / : ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ? ] * [ ? ] ? ) | [ * ] / g;
7
- var paramInjectTrailingSlashMatcher = / \/ \/ \? | \/ \? \/ | \/ \? / g;
8
6
var queryMatcher = / \? ( .* ) $ / ;
9
7
10
- var _compiledPatterns = { } ;
8
+ function escapeRegExp ( string ) {
9
+ return string . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
10
+ }
11
11
12
- function compilePattern ( pattern ) {
13
- if ( ! ( pattern in _compiledPatterns ) ) {
14
- var paramNames = [ ] ;
15
- var source = pattern . replace ( paramCompileMatcher , function ( match , paramName ) {
16
- if ( paramName ) {
17
- paramNames . push ( paramName ) ;
18
- return '([^/?#]+)' ;
19
- } else if ( match === '*' ) {
20
- paramNames . push ( 'splat' ) ;
21
- return '(.*?)' ;
22
- } else {
23
- return '\\' + match ;
24
- }
25
- } ) ;
12
+ function _compilePattern ( pattern ) {
13
+ var escapedSource = '' ;
14
+ var paramNames = [ ] ;
15
+ var tokens = [ ] ;
16
+
17
+ var match , lastIndex = 0 , matcher = / : ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) | \* | \( | \) / g;
18
+ while ( match = matcher . exec ( pattern ) ) {
19
+ if ( match . index !== lastIndex ) {
20
+ tokens . push ( pattern . slice ( lastIndex , match . index ) ) ;
21
+ escapedSource += escapeRegExp ( pattern . slice ( lastIndex , match . index ) ) ;
22
+ }
23
+
24
+ if ( match [ 1 ] ) {
25
+ escapedSource += '([^/?#]+)' ;
26
+ paramNames . push ( match [ 1 ] ) ;
27
+ } else if ( match [ 0 ] === '*' ) {
28
+ escapedSource += '(.*?)' ;
29
+ paramNames . push ( 'splat' ) ;
30
+ } else if ( match [ 0 ] === '(' ) {
31
+ escapedSource += '(?:' ;
32
+ } else if ( match [ 0 ] === ')' ) {
33
+ escapedSource += ')?' ;
34
+ }
35
+
36
+ tokens . push ( match [ 0 ] ) ;
37
+
38
+ lastIndex = matcher . lastIndex ;
39
+ }
26
40
27
- _compiledPatterns [ pattern ] = {
28
- matcher : new RegExp ( '^' + source + '$' , 'i' ) ,
29
- paramNames : paramNames
30
- } ;
41
+ if ( lastIndex !== pattern . length ) {
42
+ tokens . push ( pattern . slice ( lastIndex , pattern . length ) ) ;
43
+ escapedSource += escapeRegExp ( pattern . slice ( lastIndex , pattern . length ) ) ;
31
44
}
32
45
46
+ return {
47
+ pattern,
48
+ escapedSource,
49
+ paramNames,
50
+ tokens
51
+ } ;
52
+ }
53
+
54
+ var _compiledPatterns = { } ;
55
+
56
+ function compilePattern ( pattern ) {
57
+ if ( ! ( pattern in _compiledPatterns ) )
58
+ _compiledPatterns [ pattern ] = _compilePattern ( pattern ) ;
59
+
33
60
return _compiledPatterns [ pattern ] ;
34
61
}
35
62
@@ -62,7 +89,8 @@ var PathUtils = {
62
89
* pattern does not match the given path.
63
90
*/
64
91
extractParams : function ( pattern , path ) {
65
- var { matcher, paramNames } = compilePattern ( pattern ) ;
92
+ var { escapedSource, paramNames } = compilePattern ( pattern ) ;
93
+ var matcher = new RegExp ( '^' + escapedSource + '$' , 'i' ) ;
66
94
var match = path . match ( matcher ) ;
67
95
68
96
if ( ! match )
@@ -84,40 +112,46 @@ var PathUtils = {
84
112
injectParams : function ( pattern , params ) {
85
113
params = params || { } ;
86
114
87
- var splatIndex = 0 ;
115
+ var { tokens } = compilePattern ( pattern ) ;
116
+ var parenCount = 0 , pathname = '' , splatIndex = 0 ;
88
117
89
- return pattern . replace ( paramInjectMatcher , function ( match , paramName ) {
90
- paramName = paramName || 'splat' ;
118
+ var token , paramName , paramValue ;
119
+ for ( var i = 0 , len = tokens . length ; i < len ; ++ i ) {
120
+ token = tokens [ i ] ;
91
121
92
- // If param is optional don't check for existence
93
- if ( paramName . slice ( - 1 ) === '?' ) {
94
- paramName = paramName . slice ( 0 , - 1 ) ;
122
+ if ( token === '*' ) {
123
+ paramValue = Array . isArray ( params . splat ) ? params . splat [ splatIndex ++ ] : params . splat ;
95
124
96
- if ( params [ paramName ] == null )
97
- return '' ;
98
- } else {
99
125
invariant (
100
- params [ paramName ] != null ,
101
- 'Missing "%s" parameter for path "%s"' ,
102
- paramName , pattern
126
+ paramValue != null || parenCount > 0 ,
127
+ 'Missing splat #%s for path "%s"' ,
128
+ splatIndex , pattern
103
129
) ;
104
- }
105
130
106
- var segment ;
107
- if ( paramName === 'splat' && Array . isArray ( params [ paramName ] ) ) {
108
- segment = params [ paramName ] [ splatIndex ++ ] ;
131
+ if ( paramValue != null )
132
+ pathname += paramValue ;
133
+ } else if ( token === '(' ) {
134
+ parenCount += 1 ;
135
+ } else if ( token === ')' ) {
136
+ parenCount -= 1 ;
137
+ } else if ( token . charAt ( 0 ) === ':' ) {
138
+ paramName = token . substring ( 1 ) ;
139
+ paramValue = params [ paramName ] ;
109
140
110
141
invariant (
111
- segment != null ,
112
- 'Missing splat # %s for path "%s"' ,
113
- splatIndex , pattern
142
+ paramValue != null || parenCount > 0 ,
143
+ 'Missing "%s" parameter for path "%s"' ,
144
+ paramName , pattern
114
145
) ;
146
+
147
+ if ( paramValue != null )
148
+ pathname += paramValue ;
115
149
} else {
116
- segment = params [ paramName ] ;
150
+ pathname += token ;
117
151
}
152
+ }
118
153
119
- return segment ;
120
- } ) . replace ( paramInjectTrailingSlashMatcher , '/' ) ;
154
+ return pathname . replace ( / \/ + / g, '/' ) ;
121
155
} ,
122
156
123
157
/**
0 commit comments