1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 |
|
4 |
|
5 |
|
6 | function lexer(str) {
|
7 | var tokens = [];
|
8 | var i = 0;
|
9 | while (i < str.length) {
|
10 | var char = str[i];
|
11 | if (char === "*" || char === "+" || char === "?") {
|
12 | tokens.push({ type: "MODIFIER", index: i, value: str[i++] });
|
13 | continue;
|
14 | }
|
15 | if (char === "\\") {
|
16 | tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] });
|
17 | continue;
|
18 | }
|
19 | if (char === "{") {
|
20 | tokens.push({ type: "OPEN", index: i, value: str[i++] });
|
21 | continue;
|
22 | }
|
23 | if (char === "}") {
|
24 | tokens.push({ type: "CLOSE", index: i, value: str[i++] });
|
25 | continue;
|
26 | }
|
27 | if (char === ":") {
|
28 | var name = "";
|
29 | var j = i + 1;
|
30 | while (j < str.length) {
|
31 | var code = str.charCodeAt(j);
|
32 | if (
|
33 |
|
34 | (code >= 48 && code <= 57) ||
|
35 |
|
36 | (code >= 65 && code <= 90) ||
|
37 |
|
38 | (code >= 97 && code <= 122) ||
|
39 |
|
40 | code === 95) {
|
41 | name += str[j++];
|
42 | continue;
|
43 | }
|
44 | break;
|
45 | }
|
46 | if (!name)
|
47 | throw new TypeError("Missing parameter name at " + i);
|
48 | tokens.push({ type: "NAME", index: i, value: name });
|
49 | i = j;
|
50 | continue;
|
51 | }
|
52 | if (char === "(") {
|
53 | var count = 1;
|
54 | var pattern = "";
|
55 | var j = i + 1;
|
56 | if (str[j] === "?") {
|
57 | throw new TypeError("Pattern cannot start with \"?\" at " + j);
|
58 | }
|
59 | while (j < str.length) {
|
60 | if (str[j] === "\\") {
|
61 | pattern += str[j++] + str[j++];
|
62 | continue;
|
63 | }
|
64 | if (str[j] === ")") {
|
65 | count--;
|
66 | if (count === 0) {
|
67 | j++;
|
68 | break;
|
69 | }
|
70 | }
|
71 | else if (str[j] === "(") {
|
72 | count++;
|
73 | if (str[j + 1] !== "?") {
|
74 | throw new TypeError("Capturing groups are not allowed at " + j);
|
75 | }
|
76 | }
|
77 | pattern += str[j++];
|
78 | }
|
79 | if (count)
|
80 | throw new TypeError("Unbalanced pattern at " + i);
|
81 | if (!pattern)
|
82 | throw new TypeError("Missing pattern at " + i);
|
83 | tokens.push({ type: "PATTERN", index: i, value: pattern });
|
84 | i = j;
|
85 | continue;
|
86 | }
|
87 | tokens.push({ type: "CHAR", index: i, value: str[i++] });
|
88 | }
|
89 | tokens.push({ type: "END", index: i, value: "" });
|
90 | return tokens;
|
91 | }
|
92 |
|
93 |
|
94 |
|
95 | function parse(str, options) {
|
96 | if (options === void 0) { options = {}; }
|
97 | var tokens = lexer(str);
|
98 | var _a = options.prefixes, prefixes = _a === void 0 ? "./" : _a;
|
99 | var defaultPattern = "[^" + escapeString(options.delimiter || "/#?") + "]+?";
|
100 | var result = [];
|
101 | var key = 0;
|
102 | var i = 0;
|
103 | var path = "";
|
104 | var tryConsume = function (type) {
|
105 | if (i < tokens.length && tokens[i].type === type)
|
106 | return tokens[i++].value;
|
107 | };
|
108 | var mustConsume = function (type) {
|
109 | var value = tryConsume(type);
|
110 | if (value !== undefined)
|
111 | return value;
|
112 | var _a = tokens[i], nextType = _a.type, index = _a.index;
|
113 | throw new TypeError("Unexpected " + nextType + " at " + index + ", expected " + type);
|
114 | };
|
115 | var consumeText = function () {
|
116 | var result = "";
|
117 | var value;
|
118 |
|
119 | while ((value = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR"))) {
|
120 | result += value;
|
121 | }
|
122 | return result;
|
123 | };
|
124 | while (i < tokens.length) {
|
125 | var char = tryConsume("CHAR");
|
126 | var name = tryConsume("NAME");
|
127 | var pattern = tryConsume("PATTERN");
|
128 | if (name || pattern) {
|
129 | var prefix = char || "";
|
130 | if (prefixes.indexOf(prefix) === -1) {
|
131 | path += prefix;
|
132 | prefix = "";
|
133 | }
|
134 | if (path) {
|
135 | result.push(path);
|
136 | path = "";
|
137 | }
|
138 | result.push({
|
139 | name: name || key++,
|
140 | prefix: prefix,
|
141 | suffix: "",
|
142 | pattern: pattern || defaultPattern,
|
143 | modifier: tryConsume("MODIFIER") || ""
|
144 | });
|
145 | continue;
|
146 | }
|
147 | var value = char || tryConsume("ESCAPED_CHAR");
|
148 | if (value) {
|
149 | path += value;
|
150 | continue;
|
151 | }
|
152 | if (path) {
|
153 | result.push(path);
|
154 | path = "";
|
155 | }
|
156 | var open = tryConsume("OPEN");
|
157 | if (open) {
|
158 | var prefix = consumeText();
|
159 | var name_1 = tryConsume("NAME") || "";
|
160 | var pattern_1 = tryConsume("PATTERN") || "";
|
161 | var suffix = consumeText();
|
162 | mustConsume("CLOSE");
|
163 | result.push({
|
164 | name: name_1 || (pattern_1 ? key++ : ""),
|
165 | pattern: name_1 && !pattern_1 ? defaultPattern : pattern_1,
|
166 | prefix: prefix,
|
167 | suffix: suffix,
|
168 | modifier: tryConsume("MODIFIER") || ""
|
169 | });
|
170 | continue;
|
171 | }
|
172 | mustConsume("END");
|
173 | }
|
174 | return result;
|
175 | }
|
176 | exports.parse = parse;
|
177 |
|
178 |
|
179 |
|
180 | function compile(str, options) {
|
181 | return tokensToFunction(parse(str, options), options);
|
182 | }
|
183 | exports.compile = compile;
|
184 |
|
185 |
|
186 |
|
187 | function tokensToFunction(tokens, options) {
|
188 | if (options === void 0) { options = {}; }
|
189 | var reFlags = flags(options);
|
190 | var _a = options.encode, encode = _a === void 0 ? function (x) { return x; } : _a, _b = options.validate, validate = _b === void 0 ? true : _b;
|
191 |
|
192 | var matches = tokens.map(function (token) {
|
193 | if (typeof token === "object") {
|
194 | return new RegExp("^(?:" + token.pattern + ")$", reFlags);
|
195 | }
|
196 | });
|
197 | return function (data) {
|
198 | var path = "";
|
199 | for (var i = 0; i < tokens.length; i++) {
|
200 | var token = tokens[i];
|
201 | if (typeof token === "string") {
|
202 | path += token;
|
203 | continue;
|
204 | }
|
205 | var value = data ? data[token.name] : undefined;
|
206 | var optional = token.modifier === "?" || token.modifier === "*";
|
207 | var repeat = token.modifier === "*" || token.modifier === "+";
|
208 | if (Array.isArray(value)) {
|
209 | if (!repeat) {
|
210 | throw new TypeError("Expected \"" + token.name + "\" to not repeat, but got an array");
|
211 | }
|
212 | if (value.length === 0) {
|
213 | if (optional)
|
214 | continue;
|
215 | throw new TypeError("Expected \"" + token.name + "\" to not be empty");
|
216 | }
|
217 | for (var j = 0; j < value.length; j++) {
|
218 | var segment = encode(value[j], token);
|
219 | if (validate && !matches[i].test(segment)) {
|
220 | throw new TypeError("Expected all \"" + token.name + "\" to match \"" + token.pattern + "\", but got \"" + segment + "\"");
|
221 | }
|
222 | path += token.prefix + segment + token.suffix;
|
223 | }
|
224 | continue;
|
225 | }
|
226 | if (typeof value === "string" || typeof value === "number") {
|
227 | var segment = encode(String(value), token);
|
228 | if (validate && !matches[i].test(segment)) {
|
229 | throw new TypeError("Expected \"" + token.name + "\" to match \"" + token.pattern + "\", but got \"" + segment + "\"");
|
230 | }
|
231 | path += token.prefix + segment + token.suffix;
|
232 | continue;
|
233 | }
|
234 | if (optional)
|
235 | continue;
|
236 | var typeOfMessage = repeat ? "an array" : "a string";
|
237 | throw new TypeError("Expected \"" + token.name + "\" to be " + typeOfMessage);
|
238 | }
|
239 | return path;
|
240 | };
|
241 | }
|
242 | exports.tokensToFunction = tokensToFunction;
|
243 |
|
244 |
|
245 |
|
246 | function match(str, options) {
|
247 | var keys = [];
|
248 | var re = pathToRegexp(str, keys, options);
|
249 | return regexpToFunction(re, keys, options);
|
250 | }
|
251 | exports.match = match;
|
252 |
|
253 |
|
254 |
|
255 | function regexpToFunction(re, keys, options) {
|
256 | if (options === void 0) { options = {}; }
|
257 | var _a = options.decode, decode = _a === void 0 ? function (x) { return x; } : _a;
|
258 | return function (pathname) {
|
259 | var m = re.exec(pathname);
|
260 | if (!m)
|
261 | return false;
|
262 | var path = m[0], index = m.index;
|
263 | var params = Object.create(null);
|
264 | var _loop_1 = function (i) {
|
265 |
|
266 | if (m[i] === undefined)
|
267 | return "continue";
|
268 | var key = keys[i - 1];
|
269 | if (key.modifier === "*" || key.modifier === "+") {
|
270 | params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) {
|
271 | return decode(value, key);
|
272 | });
|
273 | }
|
274 | else {
|
275 | params[key.name] = decode(m[i], key);
|
276 | }
|
277 | };
|
278 | for (var i = 1; i < m.length; i++) {
|
279 | _loop_1(i);
|
280 | }
|
281 | return { path: path, index: index, params: params };
|
282 | };
|
283 | }
|
284 | exports.regexpToFunction = regexpToFunction;
|
285 |
|
286 |
|
287 |
|
288 | function escapeString(str) {
|
289 | return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
|
290 | }
|
291 |
|
292 |
|
293 |
|
294 | function flags(options) {
|
295 | return options && options.sensitive ? "" : "i";
|
296 | }
|
297 |
|
298 |
|
299 |
|
300 | function regexpToRegexp(path, keys) {
|
301 | if (!keys)
|
302 | return path;
|
303 |
|
304 | var groups = path.source.match(/\((?!\?)/g);
|
305 | if (groups) {
|
306 | for (var i = 0; i < groups.length; i++) {
|
307 | keys.push({
|
308 | name: i,
|
309 | prefix: "",
|
310 | suffix: "",
|
311 | modifier: "",
|
312 | pattern: ""
|
313 | });
|
314 | }
|
315 | }
|
316 | return path;
|
317 | }
|
318 |
|
319 |
|
320 |
|
321 | function arrayToRegexp(paths, keys, options) {
|
322 | var parts = paths.map(function (path) { return pathToRegexp(path, keys, options).source; });
|
323 | return new RegExp("(?:" + parts.join("|") + ")", flags(options));
|
324 | }
|
325 |
|
326 |
|
327 |
|
328 | function stringToRegexp(path, keys, options) {
|
329 | return tokensToRegexp(parse(path, options), keys, options);
|
330 | }
|
331 |
|
332 |
|
333 |
|
334 | function tokensToRegexp(tokens, keys, options) {
|
335 | if (options === void 0) { options = {}; }
|
336 | var _a = options.strict, strict = _a === void 0 ? false : _a, _b = options.start, start = _b === void 0 ? true : _b, _c = options.end, end = _c === void 0 ? true : _c, _d = options.encode, encode = _d === void 0 ? function (x) { return x; } : _d;
|
337 | var endsWith = "[" + escapeString(options.endsWith || "") + "]|$";
|
338 | var delimiter = "[" + escapeString(options.delimiter || "/#?") + "]";
|
339 | var route = start ? "^" : "";
|
340 |
|
341 | for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
|
342 | var token = tokens_1[_i];
|
343 | if (typeof token === "string") {
|
344 | route += escapeString(encode(token));
|
345 | }
|
346 | else {
|
347 | var prefix = escapeString(encode(token.prefix));
|
348 | var suffix = escapeString(encode(token.suffix));
|
349 | if (token.pattern) {
|
350 | if (keys)
|
351 | keys.push(token);
|
352 | if (prefix || suffix) {
|
353 | if (token.modifier === "+" || token.modifier === "*") {
|
354 | var mod = token.modifier === "*" ? "?" : "";
|
355 | route += "(?:" + prefix + "((?:" + token.pattern + ")(?:" + suffix + prefix + "(?:" + token.pattern + "))*)" + suffix + ")" + mod;
|
356 | }
|
357 | else {
|
358 | route += "(?:" + prefix + "(" + token.pattern + ")" + suffix + ")" + token.modifier;
|
359 | }
|
360 | }
|
361 | else {
|
362 | route += "(" + token.pattern + ")" + token.modifier;
|
363 | }
|
364 | }
|
365 | else {
|
366 | route += "(?:" + prefix + suffix + ")" + token.modifier;
|
367 | }
|
368 | }
|
369 | }
|
370 | if (end) {
|
371 | if (!strict)
|
372 | route += delimiter + "?";
|
373 | route += !options.endsWith ? "$" : "(?=" + endsWith + ")";
|
374 | }
|
375 | else {
|
376 | var endToken = tokens[tokens.length - 1];
|
377 | var isEndDelimited = typeof endToken === "string"
|
378 | ? delimiter.indexOf(endToken[endToken.length - 1]) > -1
|
379 | :
|
380 | endToken === undefined;
|
381 | if (!strict) {
|
382 | route += "(?:" + delimiter + "(?=" + endsWith + "))?";
|
383 | }
|
384 | if (!isEndDelimited) {
|
385 | route += "(?=" + delimiter + "|" + endsWith + ")";
|
386 | }
|
387 | }
|
388 | return new RegExp(route, flags(options));
|
389 | }
|
390 | exports.tokensToRegexp = tokensToRegexp;
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 | function pathToRegexp(path, keys, options) {
|
399 | if (path instanceof RegExp)
|
400 | return regexpToRegexp(path, keys);
|
401 | if (Array.isArray(path))
|
402 | return arrayToRegexp(path, keys, options);
|
403 | return stringToRegexp(path, keys, options);
|
404 | }
|
405 | exports.pathToRegexp = pathToRegexp;
|
406 |
|
\ | No newline at end of file |