universal-router
Version:
Isomorphic router for JavaScript web applications
504 lines (503 loc) • 18.6 kB
JavaScript
"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]);
}