1 |
|
2 | /**
|
3 | * Expose `pathToRegexp`.
|
4 | */
|
5 | module.exports = pathToRegexp;
|
6 |
|
7 | // module.exports = function( path ) {
|
8 | // path = path.replace( /\:\w+\/?/g, function( val ) {
|
9 | // var suffix = '';
|
10 | // if( val.length - 1 === val.lastIndexOf( '\/' ) ) {
|
11 | // suffix = '\\/'
|
12 | // }
|
13 | // return '(\\w*)' + suffix;
|
14 | // } );
|
15 | // path = path.replace( /\//, '\\/' );
|
16 | // console.log( new RegExp( "^"+ path +".*$", "i" ) );
|
17 | // return new RegExp( "^"+ path +"(?:$|\/\\w*)$", "i" );
|
18 | // };
|
19 |
|
20 | /**
|
21 | * The main path matching regexp utility.
|
22 | *
|
23 | * @type {RegExp}
|
24 | */
|
25 | var PATH_REGEXP = new RegExp([
|
26 | // Match escaped characters that would otherwise appear in future matches.
|
27 | // This allows the user to escape special characters that won't transform.
|
28 | '(\\\\.)',
|
29 | // Match Express-style parameters and un-named parameters with a prefix
|
30 | // and optional suffixes. Matches appear as:
|
31 | //
|
32 | // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
|
33 | // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
|
34 | '([\\/.])?(?:\\:(\\w+)(?:\\(((?:\\\\.|[^)])*)\\))?|\\(((?:\\\\.|[^)])*)\\))([+*?])?',
|
35 | // Match regexp special characters that are always escaped.
|
36 | '([.+*?=^!:${}()[\\]|\\/])'
|
37 | ].join('|'), 'g');
|
38 |
|
39 | /**
|
40 | * Escape the capturing group by escaping special characters and meaning.
|
41 | *
|
42 | * @param {String} group
|
43 | * @return {String}
|
44 | */
|
45 | function escapeGroup (group) {
|
46 | return group.replace(/([=!:$\/()])/g, '\\$1');
|
47 | }
|
48 |
|
49 | /**
|
50 | * Attach the keys as a property of the regexp.
|
51 | *
|
52 | * @param {RegExp} re
|
53 | * @param {Array} keys
|
54 | * @return {RegExp}
|
55 | */
|
56 | function attachKeys (re, keys) {
|
57 | re.keys = keys;
|
58 | return re;
|
59 | }
|
60 |
|
61 | /**
|
62 | * Get the flags for a regexp from the options.
|
63 | *
|
64 | * @param {Object} options
|
65 | * @return {String}
|
66 | */
|
67 | function flags (options) {
|
68 | return options.sensitive ? '' : 'i';
|
69 | }
|
70 |
|
71 | /**
|
72 | * Pull out keys from a regexp.
|
73 | *
|
74 | * @param {RegExp} path
|
75 | * @param {Array} keys
|
76 | * @return {RegExp}
|
77 | */
|
78 | function regexpToRegexp (path, keys) {
|
79 | // Use a negative lookahead to match only capturing groups.
|
80 | var groups = path.source.match(/\((?!\?)/g);
|
81 |
|
82 | if (groups) {
|
83 | for (var i = 0; i < groups.length; i++) {
|
84 | keys.push({
|
85 | name: i,
|
86 | delimiter: null,
|
87 | optional: false,
|
88 | repeat: false
|
89 | });
|
90 | }
|
91 | }
|
92 |
|
93 | return attachKeys(path, keys);
|
94 | }
|
95 |
|
96 | /**
|
97 | * Transform an array into a regexp.
|
98 | *
|
99 | * @param {Array} path
|
100 | * @param {Array} keys
|
101 | * @param {Object} options
|
102 | * @return {RegExp}
|
103 | */
|
104 | function arrayToRegexp (path, keys, options) {
|
105 | var parts = [];
|
106 |
|
107 | for (var i = 0; i < path.length; i++) {
|
108 | parts.push(pathToRegexp(path[i], keys, options).source);
|
109 | }
|
110 |
|
111 | var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
|
112 | return attachKeys(regexp, keys);
|
113 | }
|
114 |
|
115 | /**
|
116 | * Replace the specific tags with regexp strings.
|
117 | *
|
118 | * @param {String} path
|
119 | * @param {Array} keys
|
120 | * @return {String}
|
121 | */
|
122 | function replacePath (path, keys) {
|
123 | var index = 0;
|
124 |
|
125 | if( '/' === path ) {
|
126 | path = '';
|
127 | }
|
128 |
|
129 | path += '__regexp__';
|
130 |
|
131 | function replace (_, escaped, prefix, key, capture, group, suffix, escape) {
|
132 | if (escaped) {
|
133 | return escaped;
|
134 | }
|
135 |
|
136 | if (escape) {
|
137 | var pf = '\/' === escape ? '\\' : '';
|
138 | return pf + escape;
|
139 | }
|
140 |
|
141 | var repeat = suffix === '+' || suffix === '*';
|
142 | var optional = suffix === '?' || suffix === '*';
|
143 |
|
144 | keys.push({
|
145 | name: key || index++,
|
146 | delimiter: prefix || '/',
|
147 | optional: optional,
|
148 | repeat: repeat
|
149 | });
|
150 |
|
151 | prefix = prefix ? ('\\' + prefix) : '';
|
152 | capture = escapeGroup(capture || group || '[^' + (prefix || '\\/') + ']*');
|
153 |
|
154 | if (repeat) {
|
155 | capture = capture + '(?:' + prefix + capture + ')*';
|
156 | }
|
157 |
|
158 | if (optional) {
|
159 | return '(?:' + prefix + '(' + capture + '))?';
|
160 | }
|
161 |
|
162 | // Basic parameter support.
|
163 | return prefix + '(' + capture + ')';
|
164 | }
|
165 |
|
166 | // path = path.replace( /\//g, '\\/' );
|
167 |
|
168 | path = path.replace( PATH_REGEXP, replace );
|
169 | return path.replace( '__regexp__', '(?:$|\\/\\S*)' );
|
170 |
|
171 | // return path.replace(PATH_REGEXP, replace);
|
172 | }
|
173 |
|
174 | /**
|
175 | * Normalize the given path string, returning a regular expression.
|
176 | *
|
177 | * An empty array can be passed in for the keys, which will hold the
|
178 | * placeholder key descriptions. For example, using `/user/:id`, `keys` will
|
179 | * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
|
180 | *
|
181 | * @param {(String|RegExp|Array)} path
|
182 | * @param {Array} [keys]
|
183 | * @param {Object} [options]
|
184 | * @return {RegExp}
|
185 | */
|
186 | function pathToRegexp (path, keys, options) {
|
187 | keys = keys || [];
|
188 |
|
189 | if (Array.isArray(keys)) {
|
190 | options = keys;
|
191 | keys = [];
|
192 | } else if (!options) {
|
193 | options = {};
|
194 | }
|
195 |
|
196 | if (path instanceof RegExp) {
|
197 | return regexpToRegexp(path, keys, options);
|
198 | }
|
199 |
|
200 | if (Array.isArray(path)) {
|
201 | return arrayToRegexp(path, keys, options);
|
202 | }
|
203 |
|
204 | var strict = options.strict;
|
205 | var end = options.end !== false;
|
206 | var route = replacePath(path, keys);
|
207 | var endsWithSlash = false;
|
208 | // var endsWithSlash = path.charAt(path.length - 1) === '/';
|
209 |
|
210 | // In non-strict mode we allow a slash at the end of match. If the path to
|
211 | // match already ends with a slash, we remove it for consistency. The slash
|
212 | // is valid at the end of a path match, not in the middle. This is important
|
213 | // in non-ending mode, where "/test/" shouldn't match "/test//route".
|
214 | if (!strict) {
|
215 | route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
|
216 | }
|
217 |
|
218 |
|
219 | if (end) {
|
220 | route += '$';
|
221 | } else {
|
222 | // In non-ending mode, we need the capturing groups to match as much as
|
223 | // possible by using a positive lookahead to the end or next path segment.
|
224 | route += strict && endsWithSlash ? '' : '(?=\\/|$)';
|
225 | }
|
226 | return attachKeys(new RegExp('^' + route, flags(options)), keys);
|
227 | }
|