UNPKG

universal-router

Version:

Isomorphic router for JavaScript web applications

504 lines (503 loc) 18.6 kB
"use strict"; /*! Path-to-RegExp | MIT License | https://github.com/pillarjs/path-to-regexp */ // @ts-nocheck var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenData = void 0; exports.parse = parse; exports.compile = compile; exports.match = match; exports.pathToRegexp = pathToRegexp; exports.stringify = stringify; var DEFAULT_DELIMITER = "/"; var NOOP_VALUE = function (value) { return value; }; var ID_START = /^[$_\p{ID_Start}]$/u; var ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u; var DEBUG_URL = "https://git.new/pathToRegexpError"; var SIMPLE_TOKENS = { // Groups. "{": "{", "}": "}", // Reserved. "(": "(", ")": ")", "[": "[", "]": "]", "+": "+", "?": "?", "!": "!", }; /** * Escape text for stringify to path. */ function escapeText(str) { return str.replace(/[{}()\[\]+?!:*]/g, "\\$&"); } /** * Escape a regular expression string. */ function escape(str) { return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&"); } /** * Tokenize input string. */ function lexer(str) { function name() { var value = ""; if (ID_START.test(chars[++i])) { value += chars[i]; while (ID_CONTINUE.test(chars[++i])) { value += chars[i]; } } else if (chars[i] === '"') { var pos = i; while (i < chars.length) { if (chars[++i] === '"') { i++; pos = 0; break; } if (chars[i] === "\\") { value += chars[++i]; } else { value += chars[i]; } } if (pos) { throw new TypeError("Unterminated quote at ".concat(pos, ": ").concat(DEBUG_URL)); } } if (!value) { throw new TypeError("Missing parameter name at ".concat(i, ": ").concat(DEBUG_URL)); } return value; } var chars, i, value, type, value_1, value_2; return __generator(this, function (_a) { switch (_a.label) { case 0: chars = __spreadArray([], str, true); i = 0; _a.label = 1; case 1: if (!(i < chars.length)) return [3 /*break*/, 12]; value = chars[i]; type = SIMPLE_TOKENS[value]; if (!type) return [3 /*break*/, 3]; return [4 /*yield*/, { type: type, index: i++, value: value }]; case 2: _a.sent(); return [3 /*break*/, 11]; case 3: if (!(value === "\\")) return [3 /*break*/, 5]; return [4 /*yield*/, { type: "ESCAPED", index: i++, value: chars[i++] }]; case 4: _a.sent(); return [3 /*break*/, 11]; case 5: if (!(value === ":")) return [3 /*break*/, 7]; value_1 = name(); return [4 /*yield*/, { type: "PARAM", index: i, value: value_1 }]; case 6: _a.sent(); return [3 /*break*/, 11]; case 7: if (!(value === "*")) return [3 /*break*/, 9]; value_2 = name(); return [4 /*yield*/, { type: "WILDCARD", index: i, value: value_2 }]; case 8: _a.sent(); return [3 /*break*/, 11]; case 9: return [4 /*yield*/, { type: "CHAR", index: i, value: chars[i++] }]; case 10: _a.sent(); _a.label = 11; case 11: return [3 /*break*/, 1]; case 12: return [2 /*return*/, { type: "END", index: i, value: "" }]; } }); } var Iter = /** @class */ (function () { function Iter(tokens) { this.tokens = tokens; } Iter.prototype.peek = function () { if (!this._peek) { var next = this.tokens.next(); this._peek = next.value; } return this._peek; }; Iter.prototype.tryConsume = function (type) { var token = this.peek(); if (token.type !== type) return; this._peek = undefined; // Reset after consumed. return token.value; }; Iter.prototype.consume = function (type) { var value = this.tryConsume(type); if (value !== undefined) return value; var _a = this.peek(), nextType = _a.type, index = _a.index; throw new TypeError("Unexpected ".concat(nextType, " at ").concat(index, ", expected ").concat(type, ": ").concat(DEBUG_URL)); }; Iter.prototype.text = function () { var result = ""; var value; while ((value = this.tryConsume("CHAR") || this.tryConsume("ESCAPED"))) { result += value; } return result; }; return Iter; }()); /** * Tokenized path instance. */ var TokenData = /** @class */ (function () { function TokenData(tokens) { this.tokens = tokens; } return TokenData; }()); exports.TokenData = TokenData; /** * Parse a string for the raw tokens. */ function parse(str, options) { if (options === void 0) { options = {}; } var _a = options.encodePath, encodePath = _a === void 0 ? NOOP_VALUE : _a; var it = new Iter(lexer(str)); function consume(endType) { var tokens = []; while (true) { var path = it.text(); if (path) tokens.push({ type: "text", value: encodePath(path) }); var param = it.tryConsume("PARAM"); if (param) { tokens.push({ type: "param", name: param, }); continue; } var wildcard = it.tryConsume("WILDCARD"); if (wildcard) { tokens.push({ type: "wildcard", name: wildcard, }); continue; } var open_1 = it.tryConsume("{"); if (open_1) { tokens.push({ type: "group", tokens: consume("}"), }); continue; } it.consume(endType); return tokens; } } var tokens = consume("END"); return new TokenData(tokens); } /** * Compile a string to a template function for the path. */ function compile(path, options) { if (options === void 0) { options = {}; } var _a = options.encode, encode = _a === void 0 ? encodeURIComponent : _a, _b = options.delimiter, delimiter = _b === void 0 ? DEFAULT_DELIMITER : _b; var data = path instanceof TokenData ? path : parse(path, options); var fn = tokensToFunction(data.tokens, delimiter, encode); return function path(data) { if (data === void 0) { data = {}; } var _a = fn(data), path = _a[0], missing = _a.slice(1); if (missing.length) { throw new TypeError("Missing parameters: ".concat(missing.join(", "))); } return path; }; } function tokensToFunction(tokens, delimiter, encode) { var encoders = tokens.map(function (token) { return tokenToFunction(token, delimiter, encode); }); return function (data) { var result = [""]; for (var _i = 0, encoders_1 = encoders; _i < encoders_1.length; _i++) { var encoder = encoders_1[_i]; var _a = encoder(data), value = _a[0], extras = _a.slice(1); result[0] += value; result.push.apply(result, extras); } return result; }; } /** * Convert a single token into a path building function. */ function tokenToFunction(token, delimiter, encode) { if (token.type === "text") return function () { return [token.value]; }; if (token.type === "group") { var fn_1 = tokensToFunction(token.tokens, delimiter, encode); return function (data) { var _a = fn_1(data), value = _a[0], missing = _a.slice(1); if (!missing.length) return [value]; return [""]; }; } var encodeValue = encode || NOOP_VALUE; if (token.type === "wildcard" && encode !== false) { return function (data) { var value = data[token.name]; if (value == null) return ["", token.name]; if (!Array.isArray(value) || value.length === 0) { throw new TypeError("Expected \"".concat(token.name, "\" to be a non-empty array")); } return [ value .map(function (value, index) { if (typeof value !== "string") { throw new TypeError("Expected \"".concat(token.name, "/").concat(index, "\" to be a string")); } return encodeValue(value); }) .join(delimiter), ]; }; } return function (data) { var value = data[token.name]; if (value == null) return ["", token.name]; if (typeof value !== "string") { throw new TypeError("Expected \"".concat(token.name, "\" to be a string")); } return [encodeValue(value)]; }; } /** * Transform a path into a match function. */ function match(path, options) { if (options === void 0) { options = {}; } var _a = options.decode, decode = _a === void 0 ? decodeURIComponent : _a, _b = options.delimiter, delimiter = _b === void 0 ? DEFAULT_DELIMITER : _b; var _c = pathToRegexp(path, options), regexp = _c.regexp, keys = _c.keys; var decoders = keys.map(function (key) { if (decode === false) return NOOP_VALUE; if (key.type === "param") return decode; return function (value) { return value.split(delimiter).map(decode); }; }); return function match(input) { var m = regexp.exec(input); if (!m) return false; var path = m[0]; var params = Object.create(null); for (var i = 1; i < m.length; i++) { if (m[i] === undefined) continue; var key = keys[i - 1]; var decoder = decoders[i - 1]; params[key.name] = decoder(m[i]); } return { path: path, params: params }; }; } function pathToRegexp(path, options) { if (options === void 0) { options = {}; } var _a = options.delimiter, delimiter = _a === void 0 ? DEFAULT_DELIMITER : _a, _b = options.end, end = _b === void 0 ? true : _b, _c = options.sensitive, sensitive = _c === void 0 ? false : _c, _d = options.trailing, trailing = _d === void 0 ? true : _d; var keys = []; var sources = []; var flags = sensitive ? "" : "i"; var paths = Array.isArray(path) ? path : [path]; var items = paths.map(function (path) { return path instanceof TokenData ? path : parse(path, options); }); for (var _i = 0, items_1 = items; _i < items_1.length; _i++) { var tokens = items_1[_i].tokens; for (var _e = 0, _f = flatten(tokens, 0, []); _e < _f.length; _e++) { var seq = _f[_e]; var regexp_1 = sequenceToRegExp(seq, delimiter, keys); sources.push(regexp_1); } } var pattern = "^(?:".concat(sources.join("|"), ")"); if (trailing) pattern += "(?:".concat(escape(delimiter), "$)?"); pattern += end ? "$" : "(?=".concat(escape(delimiter), "|$)"); var regexp = new RegExp(pattern, flags); return { regexp: regexp, keys: keys }; } /** * Generate a flat list of sequence tokens from the given tokens. */ function flatten(tokens, index, init) { var token, fork, _i, _a, seq; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!(index === tokens.length)) return [3 /*break*/, 2]; return [4 /*yield*/, init]; case 1: return [2 /*return*/, _b.sent()]; case 2: token = tokens[index]; if (!(token.type === "group")) return [3 /*break*/, 7]; fork = init.slice(); _i = 0, _a = flatten(token.tokens, 0, fork); _b.label = 3; case 3: if (!(_i < _a.length)) return [3 /*break*/, 6]; seq = _a[_i]; return [5 /*yield**/, __values(flatten(tokens, index + 1, seq))]; case 4: _b.sent(); _b.label = 5; case 5: _i++; return [3 /*break*/, 3]; case 6: return [3 /*break*/, 8]; case 7: init.push(token); _b.label = 8; case 8: return [5 /*yield**/, __values(flatten(tokens, index + 1, init))]; case 9: _b.sent(); return [2 /*return*/]; } }); } /** * Transform a flat sequence of tokens into a regular expression. */ function sequenceToRegExp(tokens, delimiter, keys) { var result = ""; var backtrack = ""; var isSafeSegmentParam = true; for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; if (token.type === "text") { result += escape(token.value); backtrack += token.value; isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter)); continue; } if (token.type === "param" || token.type === "wildcard") { if (!isSafeSegmentParam && !backtrack) { throw new TypeError("Missing text after \"".concat(token.name, "\": ").concat(DEBUG_URL)); } if (token.type === "param") { result += "(".concat(negate(delimiter, isSafeSegmentParam ? "" : backtrack), "+)"); } else { result += "([\\s\\S]+)"; } keys.push(token); backtrack = ""; isSafeSegmentParam = false; continue; } } return result; } function negate(delimiter, backtrack) { if (backtrack.length < 2) { if (delimiter.length < 2) return "[^".concat(escape(delimiter + backtrack), "]"); return "(?:(?!".concat(escape(delimiter), ")[^").concat(escape(backtrack), "])"); } if (delimiter.length < 2) { return "(?:(?!".concat(escape(backtrack), ")[^").concat(escape(delimiter), "])"); } return "(?:(?!".concat(escape(backtrack), "|").concat(escape(delimiter), ")[\\s\\S])"); } /** * Stringify token data into a path string. */ function stringify(data) { return data.tokens .map(function stringifyToken(token, index, tokens) { if (token.type === "text") return escapeText(token.value); if (token.type === "group") { return "{".concat(token.tokens.map(stringifyToken).join(""), "}"); } var isSafe = isNameSafe(token.name) && isNextNameSafe(tokens[index + 1]); var key = isSafe ? token.name : JSON.stringify(token.name); if (token.type === "param") return ":".concat(key); if (token.type === "wildcard") return "*".concat(key); throw new TypeError("Unexpected token: ".concat(token)); }) .join(""); } function isNameSafe(name) { var first = name[0], rest = name.slice(1); if (!ID_START.test(first)) return false; return rest.every(function (char) { return ID_CONTINUE.test(char); }); } function isNextNameSafe(token) { if ((token === null || token === void 0 ? void 0 : token.type) !== "text") return true; return !ID_CONTINUE.test(token.value[0]); }