1
+ /* jshint -W084 */
2
+ "use strict" ;
3
+
4
+ var invariant = require ( "react/lib/invariant" ) ;
5
+ var assign = require ( "object-assign" ) ;
6
+ var qs = require ( "qs" ) ;
7
+
8
+ var queryMatcher = / \? ( .* ) $ / ;
9
+
10
+ function escapeRegExp ( string ) {
11
+ return string . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
12
+ }
13
+
14
+ function _compilePattern ( pattern ) {
15
+ var escapedSource = "" ;
16
+ var paramNames = [ ] ;
17
+ var tokens = [ ] ;
18
+
19
+ var match ,
20
+ lastIndex = 0 ,
21
+ matcher = / : ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) | \* | \( | \) / g;
22
+ while ( match = matcher . exec ( pattern ) ) {
23
+ if ( match . index !== lastIndex ) {
24
+ tokens . push ( pattern . slice ( lastIndex , match . index ) ) ;
25
+ escapedSource += escapeRegExp ( pattern . slice ( lastIndex , match . index ) ) ;
26
+ }
27
+
28
+ if ( match [ 1 ] ) {
29
+ escapedSource += "([^/?#]+)" ;
30
+ paramNames . push ( match [ 1 ] ) ;
31
+ } else if ( match [ 0 ] === "*" ) {
32
+ escapedSource += "(.*?)" ;
33
+ paramNames . push ( "splat" ) ;
34
+ } else if ( match [ 0 ] === "(" ) {
35
+ escapedSource += "(?:" ;
36
+ } else if ( match [ 0 ] === ")" ) {
37
+ escapedSource += ")?" ;
38
+ }
39
+
40
+ tokens . push ( match [ 0 ] ) ;
41
+
42
+ lastIndex = matcher . lastIndex ;
43
+ }
44
+
45
+ if ( lastIndex !== pattern . length ) {
46
+ tokens . push ( pattern . slice ( lastIndex , pattern . length ) ) ;
47
+ escapedSource += escapeRegExp ( pattern . slice ( lastIndex , pattern . length ) ) ;
48
+ }
49
+
50
+ return {
51
+ pattern : pattern ,
52
+ escapedSource : escapedSource ,
53
+ paramNames : paramNames ,
54
+ tokens : tokens
55
+ } ;
56
+ }
57
+
58
+ var _compiledPatterns = { } ;
59
+
60
+ function compilePattern ( pattern ) {
61
+ if ( ! ( pattern in _compiledPatterns ) ) _compiledPatterns [ pattern ] = _compilePattern ( pattern ) ;
62
+
63
+ return _compiledPatterns [ pattern ] ;
64
+ }
65
+
66
+ var PathUtils = {
67
+
68
+ /**
69
+ * Returns true if the given path is absolute.
70
+ */
71
+ isAbsolute : function isAbsolute ( path ) {
72
+ return path . charAt ( 0 ) === "/" ;
73
+ } ,
74
+
75
+ /**
76
+ * Joins two URL paths together.
77
+ */
78
+ join : function join ( a , b ) {
79
+ return a . replace ( / \/ * $ / , "/" ) + b ;
80
+ } ,
81
+
82
+ /**
83
+ * Returns an array of the names of all parameters in the given pattern.
84
+ */
85
+ extractParamNames : function extractParamNames ( pattern ) {
86
+ return compilePattern ( pattern ) . paramNames ;
87
+ } ,
88
+
89
+ /**
90
+ * Extracts the portions of the given URL path that match the given pattern
91
+ * and returns an object of param name => value pairs. Returns null if the
92
+ * pattern does not match the given path.
93
+ */
94
+ extractParams : function extractParams ( pattern , path ) {
95
+ var _compilePattern2 = compilePattern ( pattern ) ;
96
+
97
+ var escapedSource = _compilePattern2 . escapedSource ;
98
+ var paramNames = _compilePattern2 . paramNames ;
99
+
100
+ var matcher = new RegExp ( "^" + escapedSource + "$" , "i" ) ;
101
+ var match = path . match ( matcher ) ;
102
+
103
+ if ( ! match ) {
104
+ return null ;
105
+ } var params = { } ;
106
+
107
+ paramNames . forEach ( function ( paramName , index ) {
108
+ params [ paramName ] = match [ index + 1 ] ;
109
+ } ) ;
110
+
111
+ return params ;
112
+ } ,
113
+
114
+ /**
115
+ * Returns a version of the given route path with params interpolated. Throws
116
+ * if there is a dynamic segment of the route path for which there is no param.
117
+ */
118
+ injectParams : function injectParams ( pattern , params ) {
119
+ params = params || { } ;
120
+
121
+ var _compilePattern2 = compilePattern ( pattern ) ;
122
+
123
+ var tokens = _compilePattern2 . tokens ;
124
+
125
+ var parenCount = 0 ,
126
+ pathname = "" ,
127
+ splatIndex = 0 ;
128
+
129
+ var token , paramName , paramValue ;
130
+ for ( var i = 0 , len = tokens . length ; i < len ; ++ i ) {
131
+ token = tokens [ i ] ;
132
+
133
+ if ( token === "*" ) {
134
+ paramValue = Array . isArray ( params . splat ) ? params . splat [ splatIndex ++ ] : params . splat ;
135
+
136
+ invariant ( paramValue != null || parenCount > 0 , "Missing splat #%s for path \"%s\"" , splatIndex , pattern ) ;
137
+
138
+ if ( paramValue != null ) pathname += paramValue ;
139
+ } else if ( token === "(" ) {
140
+ parenCount += 1 ;
141
+ } else if ( token === ")" ) {
142
+ parenCount -= 1 ;
143
+ } else if ( token . charAt ( 0 ) === ":" ) {
144
+ paramName = token . substring ( 1 ) ;
145
+ paramValue = params [ paramName ] ;
146
+
147
+ invariant ( paramValue != null || parenCount > 0 , "Missing \"%s\" parameter for path \"%s\"" , paramName , pattern ) ;
148
+
149
+ if ( paramValue != null ) pathname += paramValue ;
150
+ } else {
151
+ pathname += token ;
152
+ }
153
+ }
154
+
155
+ return pathname . replace ( / \/ + / g, "/" ) ;
156
+ } ,
157
+
158
+ /**
159
+ * Returns an object that is the result of parsing any query string contained
160
+ * in the given path, null if the path contains no query string.
161
+ */
162
+ extractQuery : function extractQuery ( path ) {
163
+ var match = path . match ( queryMatcher ) ;
164
+ return match && qs . parse ( match [ 1 ] ) ;
165
+ } ,
166
+
167
+ /**
168
+ * Returns a version of the given path without the query string.
169
+ */
170
+ withoutQuery : function withoutQuery ( path ) {
171
+ return path . replace ( queryMatcher , "" ) ;
172
+ } ,
173
+
174
+ /**
175
+ * Returns a version of the given path with the parameters in the given
176
+ * query merged into the query string.
177
+ */
178
+ withQuery : function withQuery ( path , query ) {
179
+ var existingQuery = PathUtils . extractQuery ( path ) ;
180
+
181
+ if ( existingQuery ) query = query ? assign ( existingQuery , query ) : existingQuery ;
182
+
183
+ var queryString = qs . stringify ( query , { arrayFormat : "brackets" } ) ;
184
+
185
+ if ( queryString ) {
186
+ return PathUtils . withoutQuery ( path ) + "?" + queryString ;
187
+ } return PathUtils . withoutQuery ( path ) ;
188
+ }
189
+
190
+ } ;
191
+
192
+ module . exports = PathUtils ;
0 commit comments