-
Notifications
You must be signed in to change notification settings - Fork 20
/
path.js
138 lines (111 loc) · 3.29 KB
/
path.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
var invariant = require('./invariant')
var merge = require('qs/lib/utils').merge
var qs = require('qs')
var pathToRegexp = require('path-to-regexp')
var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?+*]?)/g
var specialParamChars = /[+*?]$/g
var queryMatcher = /\?(.+)/
var _compiledPatterns = {}
function compilePattern (pattern) {
if (!(pattern in _compiledPatterns)) {
var paramNames = []
var re = pathToRegexp(pattern, paramNames)
_compiledPatterns[pattern] = {
matcher: re,
paramNames: paramNames.map(p => p.name)
}
}
return _compiledPatterns[pattern]
}
var Path = {
/**
* Returns true if the given path is absolute.
*/
isAbsolute: function (path) {
return path.charAt(0) === '/'
},
/**
* Joins two URL paths together.
*/
join: function (a, b) {
return a.replace(/\/*$/, '/') + b
},
/**
* Returns an array of the names of all parameters in the given pattern.
*/
extractParamNames: function (pattern) {
return compilePattern(pattern).paramNames
},
/**
* Extracts the portions of the given URL path that match the given pattern
* and returns an object of param name => value pairs. Returns null if the
* pattern does not match the given path.
*/
extractParams: function (pattern, path) {
var cp = compilePattern(pattern)
var matcher = cp.matcher
var paramNames = cp.paramNames
var match = path.match(matcher)
if (!match) {
return null
}
var params = {}
paramNames.forEach(function (paramName, index) {
params[paramName] = match[index + 1]
})
return params
},
/**
* Returns a version of the given route path with params interpolated. Throws
* if there is a dynamic segment of the route path for which there is no param.
*/
injectParams: function (pattern, params) {
params = params || {}
return pattern.replace(paramInjectMatcher, function (match, param) {
let paramName = param.replace(specialParamChars, '')
// If param is optional don't check for existence
if (param.slice(-1) === '?' || param.slice(-1) === '*') {
if (params[paramName] == null) {
return ''
}
} else {
invariant(
params[paramName] != null,
"Missing '%s' parameter for path '%s'",
paramName, pattern
)
}
return params[paramName]
})
},
/**
* Returns an object that is the result of parsing any query string contained
* in the given path, null if the path contains no query string.
*/
extractQuery: function (path) {
var match = path.match(queryMatcher)
return match && qs.parse(match[1])
},
/**
* Returns a version of the given path without the query string.
*/
withoutQuery: function (path) {
return path.replace(queryMatcher, '')
},
/**
* Returns a version of the given path with the parameters in the given
* query merged into the query string.
*/
withQuery: function (path, query) {
var existingQuery = Path.extractQuery(path)
if (existingQuery) {
query = query ? merge(existingQuery, query) : existingQuery
}
var queryString = qs.stringify(query, { indices: false })
if (queryString) {
return Path.withoutQuery(path) + '?' + queryString
}
return path
}
}
module.exports = Path