"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Default configs. */ var DEFAULT_DELIMITER = "/"; /** * Normalize a pathname for matching, replaces multiple slashes with a single * slash and normalizes unicode characters to "NFC". When using this method, * `decode` should be an identity function so you don't decode strings twice. */ function normalizePathname(pathname) { return decodeURI(pathname).replace(/\/+/g, "/"); } exports.normalizePathname = normalizePathname; /** * Balanced bracket helper function. */ function balanced(open, close, str, index) { var count = 0; var i = index; while (i < str.length) { if (str[i] === "\\") { i += 2; continue; } if (str[i] === close) { count--; if (count === 0) return i + 1; } if (str[i] === open) { count++; } i++; } return -1; } /** * Parse a string for the raw tokens. */ function parse(str, options) { if (options === void 0) { options = {}; } var _a, _b; var tokens = []; var defaultDelimiter = (_a = options.delimiter, (_a !== null && _a !== void 0 ? _a : DEFAULT_DELIMITER)); var whitelist = (_b = options.whitelist, (_b !== null && _b !== void 0 ? _b : undefined)); var i = 0; var key = 0; var path = ""; var isEscaped = false; // tslint:disable-next-line while (i < str.length) { var prefix = ""; var name = ""; var pattern = ""; // Ignore escaped sequences. if (str[i] === "\\") { i++; path += str[i++]; isEscaped = true; continue; } if (str[i] === ":") { while (++i < str.length) { var code = str.charCodeAt(i); if ( // `0-9` (code >= 48 && code <= 57) || // `A-Z` (code >= 65 && code <= 90) || // `a-z` (code >= 97 && code <= 122) || // `_` code === 95) { name += str[i]; continue; } break; } // False positive on param name. if (!name) i--; } if (str[i] === "(") { var end = balanced("(", ")", str, i); // False positive on matching brackets. if (end > -1) { pattern = str.slice(i + 1, end - 1); i = end; if (pattern[0] === "?") { throw new TypeError("Path pattern must be a capturing group"); } if (/\((?=[^?])/.test(pattern)) { var validPattern = pattern.replace(/\((?=[^?])/, "(?:"); throw new TypeError("Capturing groups are not allowed in pattern, use a non-capturing group: (" + validPattern + ")"); } } } // Add regular characters to the path string. if (name === "" && pattern === "") { path += str[i++]; isEscaped = false; continue; } // Extract the final character from `path` for the prefix. if (path.length && !isEscaped) { var char = path[path.length - 1]; var matches = whitelist ? whitelist.indexOf(char) > -1 : true; if (matches) { prefix = char; path = path.slice(0, -1); } } // Push the current path onto the list of tokens. if (path.length) { tokens.push(path); path = ""; } var repeat = str[i] === "+" || str[i] === "*"; var optional = str[i] === "?" || str[i] === "*"; var delimiter = prefix || defaultDelimiter; // Increment `i` past modifier token. if (repeat || optional) i++; tokens.push({ name: name || key++, prefix: prefix, delimiter: delimiter, optional: optional, repeat: repeat, pattern: pattern || "[^" + escapeString(delimiter === defaultDelimiter ? delimiter : delimiter + defaultDelimiter) + "]+?" }); } if (path.length) tokens.push(path); return tokens; } exports.parse = parse; /** * Compile a string to a template function for the path. */ function compile(str, options) { return tokensToFunction(parse(str, options), options); } exports.compile = compile; /** * Expose a method for transforming tokens into the path function. */ function tokensToFunction(tokens, options) { if (options === void 0) { options = {}; } var reFlags = flags(options); var _a = options.encode, encode = _a === void 0 ? encodeURIComponent : _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("^(?:" + 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; if (Array.isArray(value)) { if (!token.repeat) { throw new TypeError("Expected \"" + token.name + "\" to not repeat, but got an array"); } if (value.length === 0) { if (token.optional) continue; throw new TypeError("Expected \"" + 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 \"" + token.name + "\" to match \"" + token.pattern + "\", but got \"" + segment + "\""); } path += (j === 0 ? token.prefix : token.delimiter) + segment; } continue; } if (typeof value === "string" || typeof value === "number") { var segment = encode(String(value), token); if (validate && !matches[i].test(segment)) { throw new TypeError("Expected \"" + token.name + "\" to match \"" + token.pattern + "\", but got \"" + segment + "\""); } path += token.prefix + segment; continue; } if (token.optional) continue; var typeOfMessage = token.repeat ? "an array" : "a string"; throw new TypeError("Expected \"" + token.name + "\" to be " + typeOfMessage); } return path; }; } exports.tokensToFunction = tokensToFunction; /** * Create path match function from `path-to-regexp` spec. */ function match(str, options) { var keys = []; var re = pathToRegexp(str, keys, options); return regexpToFunction(re, keys, options); } exports.match = match; /** * Create a path match function from `path-to-regexp` output. */ 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) { // tslint:disable-next-line if (m[i] === undefined) return "continue"; var key = keys[i - 1]; if (key.repeat) { params[key.name] = m[i].split(key.delimiter).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 }; }; } exports.regexpToFunction = regexpToFunction; /** * 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; // Use a negative lookahead to match only capturing groups. var groups = path.source.match(/\((?!\?)/g); if (groups) { for (var i = 0; i < groups.length; i++) { keys.push({ name: i, prefix: "", delimiter: "", optional: false, repeat: false, pattern: "" }); } } 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("(?:" + 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. */ function tokensToRegexp(tokens, keys, options) { if (options === void 0) { options = {}; } var strict = options.strict, _a = options.start, start = _a === void 0 ? true : _a, _b = options.end, end = _b === void 0 ? true : _b, _c = options.delimiter, delimiter = _c === void 0 ? DEFAULT_DELIMITER : _c; var endsWith = (typeof options.endsWith === "string" ? options.endsWith.split("") : options.endsWith || []) .map(escapeString) .concat("$") .join("|"); 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(token); } else { var capture = token.repeat ? "(?:" + token.pattern + ")(?:" + escapeString(token.delimiter) + "(?:" + token.pattern + "))*" : token.pattern; if (keys) keys.push(token); if (token.optional) { if (!token.prefix) { route += "(" + capture + ")?"; } else { route += "(?:" + escapeString(token.prefix) + "(" + capture + "))?"; } } else { route += escapeString(token.prefix) + "(" + capture + ")"; } } } if (end) { if (!strict) route += "(?:" + escapeString(delimiter) + ")?"; route += endsWith === "$" ? "$" : "(?=" + endsWith + ")"; } else { var endToken = tokens[tokens.length - 1]; var isEndDelimited = typeof endToken === "string" ? endToken[endToken.length - 1] === delimiter : // tslint:disable-next-line endToken === undefined; if (!strict) { route += "(?:" + escapeString(delimiter) + "(?=" + endsWith + "))?"; } if (!isEndDelimited) { route += "(?=" + escapeString(delimiter) + "|" + endsWith + ")"; } } return new RegExp(route, flags(options)); } exports.tokensToRegexp = tokensToRegexp; /** * 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 }]`. */ 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); } exports.pathToRegexp = pathToRegexp; //# sourceMappingURL=index.js.map