123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- /**
- * Tokenize input string.
- */
- function lexer(str) {
- var tokens = [];
- var i = 0;
- while (i < str.length) {
- var char = str[i];
- if (char === "*" || char === "+" || char === "?") {
- tokens.push({ type: "MODIFIER", index: i, value: str[i++] });
- continue;
- }
- if (char === "\\") {
- tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] });
- continue;
- }
- if (char === "{") {
- tokens.push({ type: "OPEN", index: i, value: str[i++] });
- continue;
- }
- if (char === "}") {
- tokens.push({ type: "CLOSE", index: i, value: str[i++] });
- continue;
- }
- if (char === ":") {
- var name = "";
- var j = i + 1;
- while (j < str.length) {
- var code = str.charCodeAt(j);
- if (
- // `0-9`
- (code >= 48 && code <= 57) ||
- // `A-Z`
- (code >= 65 && code <= 90) ||
- // `a-z`
- (code >= 97 && code <= 122) ||
- // `_`
- code === 95) {
- name += str[j++];
- continue;
- }
- break;
- }
- if (!name)
- throw new TypeError("Missing parameter name at ".concat(i));
- tokens.push({ type: "NAME", index: i, value: name });
- i = j;
- continue;
- }
- if (char === "(") {
- var count = 1;
- var pattern = "";
- var j = i + 1;
- if (str[j] === "?") {
- throw new TypeError("Pattern cannot start with \"?\" at ".concat(j));
- }
- while (j < str.length) {
- if (str[j] === "\\") {
- pattern += str[j++] + str[j++];
- continue;
- }
- if (str[j] === ")") {
- count--;
- if (count === 0) {
- j++;
- break;
- }
- }
- else if (str[j] === "(") {
- count++;
- if (str[j + 1] !== "?") {
- throw new TypeError("Capturing groups are not allowed at ".concat(j));
- }
- }
- pattern += str[j++];
- }
- if (count)
- throw new TypeError("Unbalanced pattern at ".concat(i));
- if (!pattern)
- throw new TypeError("Missing pattern at ".concat(i));
- tokens.push({ type: "PATTERN", index: i, value: pattern });
- i = j;
- continue;
- }
- tokens.push({ type: "CHAR", index: i, value: str[i++] });
- }
- tokens.push({ type: "END", index: i, value: "" });
- return tokens;
- }
- /**
- * Parse a string for the raw tokens.
- */
- export function parse(str, options) {
- if (options === void 0) { options = {}; }
- var tokens = lexer(str);
- var _a = options.prefixes, prefixes = _a === void 0 ? "./" : _a;
- var defaultPattern = "[^".concat(escapeString(options.delimiter || "/#?"), "]+?");
- var result = [];
- var key = 0;
- var i = 0;
- var path = "";
- var tryConsume = function (type) {
- if (i < tokens.length && tokens[i].type === type)
- return tokens[i++].value;
- };
- var mustConsume = function (type) {
- var value = tryConsume(type);
- if (value !== undefined)
- return value;
- var _a = tokens[i], nextType = _a.type, index = _a.index;
- throw new TypeError("Unexpected ".concat(nextType, " at ").concat(index, ", expected ").concat(type));
- };
- var consumeText = function () {
- var result = "";
- var value;
- while ((value = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR"))) {
- result += value;
- }
- return result;
- };
- while (i < tokens.length) {
- var char = tryConsume("CHAR");
- var name = tryConsume("NAME");
- var pattern = tryConsume("PATTERN");
- if (name || pattern) {
- var prefix = char || "";
- if (prefixes.indexOf(prefix) === -1) {
- path += prefix;
- prefix = "";
- }
- if (path) {
- result.push(path);
- path = "";
- }
- result.push({
- name: name || key++,
- prefix: prefix,
- suffix: "",
- pattern: pattern || defaultPattern,
- modifier: tryConsume("MODIFIER") || "",
- });
- continue;
- }
- var value = char || tryConsume("ESCAPED_CHAR");
- if (value) {
- path += value;
- continue;
- }
- if (path) {
- result.push(path);
- path = "";
- }
- var open = tryConsume("OPEN");
- if (open) {
- var prefix = consumeText();
- var name_1 = tryConsume("NAME") || "";
- var pattern_1 = tryConsume("PATTERN") || "";
- var suffix = consumeText();
- mustConsume("CLOSE");
- result.push({
- name: name_1 || (pattern_1 ? key++ : ""),
- pattern: name_1 && !pattern_1 ? defaultPattern : pattern_1,
- prefix: prefix,
- suffix: suffix,
- modifier: tryConsume("MODIFIER") || "",
- });
- continue;
- }
- mustConsume("END");
- }
- return result;
- }
- /**
- * Compile a string to a template function for the path.
- */
- export function compile(str, options) {
- return tokensToFunction(parse(str, options), options);
- }
- /**
- * Expose a method for transforming tokens into the path function.
- */
- export function tokensToFunction(tokens, options) {
- if (options === void 0) { options = {}; }
- var reFlags = flags(options);
- var _a = options.encode, encode = _a === void 0 ? function (x) { return x; } : _a, _b = options.validate, validate = _b === void 0 ? true : _b;
- // Compile all the tokens into regexps.
- var matches = tokens.map(function (token) {
- if (typeof token === "object") {
- return new RegExp("^(?:".concat(token.pattern, ")$"), reFlags);
- }
- });
- return function (data) {
- var path = "";
- for (var i = 0; i < tokens.length; i++) {
- var token = tokens[i];
- if (typeof token === "string") {
- path += token;
- continue;
- }
- var value = data ? data[token.name] : undefined;
- var optional = token.modifier === "?" || token.modifier === "*";
- var repeat = token.modifier === "*" || token.modifier === "+";
- if (Array.isArray(value)) {
- if (!repeat) {
- throw new TypeError("Expected \"".concat(token.name, "\" to not repeat, but got an array"));
- }
- if (value.length === 0) {
- if (optional)
- continue;
- throw new TypeError("Expected \"".concat(token.name, "\" to not be empty"));
- }
- for (var j = 0; j < value.length; j++) {
- var segment = encode(value[j], token);
- if (validate && !matches[i].test(segment)) {
- throw new TypeError("Expected all \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
- }
- path += token.prefix + segment + token.suffix;
- }
- continue;
- }
- if (typeof value === "string" || typeof value === "number") {
- var segment = encode(String(value), token);
- if (validate && !matches[i].test(segment)) {
- throw new TypeError("Expected \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
- }
- path += token.prefix + segment + token.suffix;
- continue;
- }
- if (optional)
- continue;
- var typeOfMessage = repeat ? "an array" : "a string";
- throw new TypeError("Expected \"".concat(token.name, "\" to be ").concat(typeOfMessage));
- }
- return path;
- };
- }
- /**
- * Create path match function from `path-to-regexp` spec.
- */
- export function match(str, options) {
- var keys = [];
- var re = pathToRegexp(str, keys, options);
- return regexpToFunction(re, keys, options);
- }
- /**
- * Create a path match function from `path-to-regexp` output.
- */
- export function regexpToFunction(re, keys, options) {
- if (options === void 0) { options = {}; }
- var _a = options.decode, decode = _a === void 0 ? function (x) { return x; } : _a;
- return function (pathname) {
- var m = re.exec(pathname);
- if (!m)
- return false;
- var path = m[0], index = m.index;
- var params = Object.create(null);
- var _loop_1 = function (i) {
- if (m[i] === undefined)
- return "continue";
- var key = keys[i - 1];
- if (key.modifier === "*" || key.modifier === "+") {
- params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) {
- return decode(value, key);
- });
- }
- else {
- params[key.name] = decode(m[i], key);
- }
- };
- for (var i = 1; i < m.length; i++) {
- _loop_1(i);
- }
- return { path: path, index: index, params: params };
- };
- }
- /**
- * Escape a regular expression string.
- */
- function escapeString(str) {
- return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
- }
- /**
- * Get the flags for a regexp from the options.
- */
- function flags(options) {
- return options && options.sensitive ? "" : "i";
- }
- /**
- * Pull out keys from a regexp.
- */
- function regexpToRegexp(path, keys) {
- if (!keys)
- return path;
- var groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g;
- var index = 0;
- var execResult = groupsRegex.exec(path.source);
- while (execResult) {
- keys.push({
- // Use parenthesized substring match if available, index otherwise
- name: execResult[1] || index++,
- prefix: "",
- suffix: "",
- modifier: "",
- pattern: "",
- });
- execResult = groupsRegex.exec(path.source);
- }
- return path;
- }
- /**
- * Transform an array into a regexp.
- */
- function arrayToRegexp(paths, keys, options) {
- var parts = paths.map(function (path) { return pathToRegexp(path, keys, options).source; });
- return new RegExp("(?:".concat(parts.join("|"), ")"), flags(options));
- }
- /**
- * Create a path regexp from string input.
- */
- function stringToRegexp(path, keys, options) {
- return tokensToRegexp(parse(path, options), keys, options);
- }
- /**
- * Expose a function for taking tokens and returning a RegExp.
- */
- export function tokensToRegexp(tokens, keys, options) {
- if (options === void 0) { options = {}; }
- 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, _e = options.delimiter, delimiter = _e === void 0 ? "/#?" : _e, _f = options.endsWith, endsWith = _f === void 0 ? "" : _f;
- var endsWithRe = "[".concat(escapeString(endsWith), "]|$");
- var delimiterRe = "[".concat(escapeString(delimiter), "]");
- var route = start ? "^" : "";
- // Iterate over the tokens and create our regexp string.
- for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
- var token = tokens_1[_i];
- if (typeof token === "string") {
- route += escapeString(encode(token));
- }
- else {
- var prefix = escapeString(encode(token.prefix));
- var suffix = escapeString(encode(token.suffix));
- if (token.pattern) {
- if (keys)
- keys.push(token);
- if (prefix || suffix) {
- if (token.modifier === "+" || token.modifier === "*") {
- var mod = token.modifier === "*" ? "?" : "";
- route += "(?:".concat(prefix, "((?:").concat(token.pattern, ")(?:").concat(suffix).concat(prefix, "(?:").concat(token.pattern, "))*)").concat(suffix, ")").concat(mod);
- }
- else {
- route += "(?:".concat(prefix, "(").concat(token.pattern, ")").concat(suffix, ")").concat(token.modifier);
- }
- }
- else {
- if (token.modifier === "+" || token.modifier === "*") {
- route += "((?:".concat(token.pattern, ")").concat(token.modifier, ")");
- }
- else {
- route += "(".concat(token.pattern, ")").concat(token.modifier);
- }
- }
- }
- else {
- route += "(?:".concat(prefix).concat(suffix, ")").concat(token.modifier);
- }
- }
- }
- if (end) {
- if (!strict)
- route += "".concat(delimiterRe, "?");
- route += !options.endsWith ? "$" : "(?=".concat(endsWithRe, ")");
- }
- else {
- var endToken = tokens[tokens.length - 1];
- var isEndDelimited = typeof endToken === "string"
- ? delimiterRe.indexOf(endToken[endToken.length - 1]) > -1
- : endToken === undefined;
- if (!strict) {
- route += "(?:".concat(delimiterRe, "(?=").concat(endsWithRe, "))?");
- }
- if (!isEndDelimited) {
- route += "(?=".concat(delimiterRe, "|").concat(endsWithRe, ")");
- }
- }
- return new RegExp(route, flags(options));
- }
- /**
- * Normalize the given path string, returning a regular expression.
- *
- * An empty array can be passed in for the keys, which will hold the
- * placeholder key descriptions. For example, using `/user/:id`, `keys` will
- * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
- */
- export function pathToRegexp(path, keys, options) {
- if (path instanceof RegExp)
- return regexpToRegexp(path, keys);
- if (Array.isArray(path))
- return arrayToRegexp(path, keys, options);
- return stringToRegexp(path, keys, options);
- }
- //# sourceMappingURL=index.js.map
|