UNPKG

9.03 kBJavaScriptView Raw
1'use strict';
2
3const __chunk_4 = require('./chunk-e6311a56.js');
4
5/**
6 * TS adaption of https://github.com/pillarjs/path-to-regexp/blob/master/index.js
7 */
8/**
9 * Default configs.
10 */
11const DEFAULT_DELIMITER = '/';
12const DEFAULT_DELIMITERS = './';
13/**
14 * The main path matching regexp utility.
15 */
16const PATH_REGEXP = new RegExp([
17 // Match escaped characters that would otherwise appear in future matches.
18 // This allows the user to escape special characters that won't transform.
19 '(\\\\.)',
20 // Match Express-style parameters and un-named parameters with a prefix
21 // and optional suffixes. Matches appear as:
22 //
23 // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
24 // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
25 '(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?'
26].join('|'), 'g');
27/**
28 * Parse a string for the raw tokens.
29 */
30const parse = (str, options) => {
31 var tokens = [];
32 var key = 0;
33 var index = 0;
34 var path = '';
35 var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER;
36 var delimiters = (options && options.delimiters) || DEFAULT_DELIMITERS;
37 var pathEscaped = false;
38 var res;
39 while ((res = PATH_REGEXP.exec(str)) !== null) {
40 var m = res[0];
41 var escaped = res[1];
42 var offset = res.index;
43 path += str.slice(index, offset);
44 index = offset + m.length;
45 // Ignore already escaped sequences.
46 if (escaped) {
47 path += escaped[1];
48 pathEscaped = true;
49 continue;
50 }
51 var prev = '';
52 var next = str[index];
53 var name = res[2];
54 var capture = res[3];
55 var group = res[4];
56 var modifier = res[5];
57 if (!pathEscaped && path.length) {
58 var k = path.length - 1;
59 if (delimiters.indexOf(path[k]) > -1) {
60 prev = path[k];
61 path = path.slice(0, k);
62 }
63 }
64 // Push the current path onto the tokens.
65 if (path) {
66 tokens.push(path);
67 path = '';
68 pathEscaped = false;
69 }
70 var partial = prev !== '' && next !== undefined && next !== prev;
71 var repeat = modifier === '+' || modifier === '*';
72 var optional = modifier === '?' || modifier === '*';
73 var delimiter = prev || defaultDelimiter;
74 var pattern = capture || group;
75 tokens.push({
76 name: name || key++,
77 prefix: prev,
78 delimiter: delimiter,
79 optional: optional,
80 repeat: repeat,
81 partial: partial,
82 pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?'
83 });
84 }
85 // Push any remaining characters.
86 if (path || index < str.length) {
87 tokens.push(path + str.substr(index));
88 }
89 return tokens;
90};
91/**
92 * Escape a regular expression string.
93 */
94const escapeString = (str) => {
95 return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1');
96};
97/**
98 * Escape the capturing group by escaping special characters and meaning.
99 */
100const escapeGroup = (group) => {
101 return group.replace(/([=!:$/()])/g, '\\$1');
102};
103/**
104 * Get the flags for a regexp from the options.
105 */
106const flags = (options) => {
107 return options && options.sensitive ? '' : 'i';
108};
109/**
110 * Pull out keys from a regexp.
111 */
112const regexpToRegexp = (path, keys) => {
113 if (!keys)
114 return path;
115 // Use a negative lookahead to match only capturing groups.
116 var groups = path.source.match(/\((?!\?)/g);
117 if (groups) {
118 for (var i = 0; i < groups.length; i++) {
119 keys.push({
120 name: i,
121 prefix: null,
122 delimiter: null,
123 optional: false,
124 repeat: false,
125 partial: false,
126 pattern: null
127 });
128 }
129 }
130 return path;
131};
132/**
133 * Transform an array into a regexp.
134 */
135const arrayToRegexp = (path, keys, options) => {
136 var parts = [];
137 for (var i = 0; i < path.length; i++) {
138 parts.push(pathToRegexp(path[i], keys, options).source);
139 }
140 return new RegExp('(?:' + parts.join('|') + ')', flags(options));
141};
142/**
143 * Create a path regexp from string input.
144 */
145const stringToRegexp = (path, keys, options) => {
146 return tokensToRegExp(parse(path, options), keys, options);
147};
148/**
149 * Expose a function for taking tokens and returning a RegExp.
150 */
151const tokensToRegExp = (tokens, keys, options) => {
152 options = options || {};
153 var strict = options.strict;
154 var end = options.end !== false;
155 var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER);
156 var delimiters = options.delimiters || DEFAULT_DELIMITERS;
157 var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|');
158 var route = '';
159 var isEndDelimited = false;
160 // Iterate over the tokens and create our regexp string.
161 for (var i = 0; i < tokens.length; i++) {
162 var token = tokens[i];
163 if (typeof token === 'string') {
164 route += escapeString(token);
165 isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1;
166 }
167 else {
168 var prefix = escapeString(token.prefix || '');
169 var capture = token.repeat
170 ? '(?:' + token.pattern + ')(?:' + prefix + '(?:' + token.pattern + '))*'
171 : token.pattern;
172 if (keys)
173 keys.push(token);
174 if (token.optional) {
175 if (token.partial) {
176 route += prefix + '(' + capture + ')?';
177 }
178 else {
179 route += '(?:' + prefix + '(' + capture + '))?';
180 }
181 }
182 else {
183 route += prefix + '(' + capture + ')';
184 }
185 }
186 }
187 if (end) {
188 if (!strict)
189 route += '(?:' + delimiter + ')?';
190 route += endsWith === '$' ? '$' : '(?=' + endsWith + ')';
191 }
192 else {
193 if (!strict)
194 route += '(?:' + delimiter + '(?=' + endsWith + '))?';
195 if (!isEndDelimited)
196 route += '(?=' + delimiter + '|' + endsWith + ')';
197 }
198 return new RegExp('^' + route, flags(options));
199};
200/**
201 * Normalize the given path string, returning a regular expression.
202 *
203 * An empty array can be passed in for the keys, which will hold the
204 * placeholder key descriptions. For example, using `/user/:id`, `keys` will
205 * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
206 */
207const pathToRegexp = (path, keys, options) => {
208 if (path instanceof RegExp) {
209 return regexpToRegexp(path, keys);
210 }
211 if (Array.isArray(path)) {
212 return arrayToRegexp(path, keys, options);
213 }
214 return stringToRegexp(path, keys, options);
215};
216
217let cacheCount = 0;
218const patternCache = {};
219const cacheLimit = 10000;
220// Memoized function for creating the path match regex
221const compilePath = (pattern, options) => {
222 const cacheKey = `${options.end}${options.strict}`;
223 const cache = patternCache[cacheKey] || (patternCache[cacheKey] = {});
224 const cachePattern = JSON.stringify(pattern);
225 if (cache[cachePattern]) {
226 return cache[cachePattern];
227 }
228 const keys = [];
229 const re = pathToRegexp(pattern, keys, options);
230 const compiledPattern = { re, keys };
231 if (cacheCount < cacheLimit) {
232 cache[cachePattern] = compiledPattern;
233 cacheCount += 1;
234 }
235 return compiledPattern;
236};
237/**
238 * Public API for matching a URL pathname to a path pattern.
239 */
240const matchPath = (pathname, options = {}) => {
241 if (typeof options === 'string') {
242 options = { path: options };
243 }
244 const { path = '/', exact = false, strict = false } = options;
245 const { re, keys } = compilePath(path, { end: exact, strict });
246 const match = re.exec(pathname);
247 if (!match) {
248 return null;
249 }
250 const [url, ...values] = match;
251 const isExact = pathname === url;
252 if (exact && !isExact) {
253 return null;
254 }
255 return {
256 path,
257 url: path === '/' && url === '' ? '/' : url,
258 isExact,
259 params: keys.reduce((memo, key, index) => {
260 memo[key.name] = values[index];
261 return memo;
262 }, {})
263 };
264};
265const matchesAreEqual = (a, b) => {
266 if (a == null && b == null) {
267 return true;
268 }
269 if (b == null) {
270 return false;
271 }
272 return a && b &&
273 a.path === b.path &&
274 a.url === b.url &&
275 __chunk_4.valueEqual(a.params, b.params);
276};
277
278exports.matchPath = matchPath;
279exports.matchesAreEqual = matchesAreEqual;