UNPKG

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