UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

319 lines (316 loc) 10.2 kB
import _typeof from '@babel/runtime/helpers/typeof'; /** * Default configs. * @ignore */ var DEFAULT_DELIMITER = '/'; var DEFAULT_DELIMITERS = './'; /** * The main path matching regexp utility. * @ignore * @type {RegExp} */ var PATH_REGEXP = new RegExp([ // Match escaped characters that would otherwise appear in future matches. // This allows the user to escape special characters that won't transform. '(\\\\.)', // Match Express-style parameters and un-named parameters with a prefix // and optional suffixes. Matches appear as: // // ":test(\\d+)?" => ["test", "\d+", undefined, "?"] // "(\\d+)" => [undefined, undefined, "\d+", undefined] '(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?'].join('|'), 'g'); /** * Parse a string for the raw tokens. * @ignore * @param {string} str * @param {Object=} options * @returns {!Array} */ function parse(str, options) { var tokens = []; var key = 0; var index = 0; var path = ''; var defaultDelimiter = (options === null || options === void 0 ? void 0 : options.delimiter) || DEFAULT_DELIMITER; var delimiters = (options === null || options === void 0 ? void 0 : options.delimiters) || DEFAULT_DELIMITERS; var pathEscaped = false; var res; while ((res = PATH_REGEXP.exec(str)) !== null) { var m = res[0]; var escaped = res[1]; var offset = res.index; path += str.slice(index, offset); index = offset + m.length; // Ignore already escaped sequences. if (escaped) { path += escaped[1]; pathEscaped = true; continue; } var prev = ''; var next = str[index]; var name_1 = res[2]; var capture = res[3]; var group = res[4]; var modifier = res[5]; if (!pathEscaped && path.length) { var k = path.length - 1; if (delimiters.indexOf(path[k]) > -1) { prev = path[k]; path = path.slice(0, k); } } // Push the current path onto the tokens. if (path) { tokens.push(path); path = ''; pathEscaped = false; } var partial = prev !== '' && next !== undefined && next !== prev; var repeat = modifier === '+' || modifier === '*'; var optional = modifier === '?' || modifier === '*'; var delimiter = prev || defaultDelimiter; var pattern = capture || group; tokens.push({ name: name_1 || key, prefix: prev, delimiter: delimiter, optional: optional, repeat: repeat, partial: partial, pattern: pattern ? escapeGroup(pattern) : "[^".concat(escapeString(delimiter), "]+?") }); key += 1; } // Push any remaining characters. if (path || index < str.length) { tokens.push(path + str.substr(index)); } return tokens; } /** * Compile a string to a template function for the path. * @ignore * @param {string} str * @param {Object=} options * @returns {!function(Object=, Object=)} */ function compile(str, options) { return tokensToFunction(parse(str, options)); } /** * Expose a method for transforming tokens into the path function. * @ignore */ function tokensToFunction(tokens) { // Compile all the tokens into regexps. var matches = new Array(tokens.length); // Compile all the patterns before compilation. for (var i = 0; i < tokens.length; i++) { if (_typeof(tokens[i]) === 'object') { matches[i] = new RegExp("^(?:".concat(tokens[i].pattern, ")$")); } } return function (data, options) { var path = ''; var encode = (options === null || options === void 0 ? void 0 : options.encode) || encodeURIComponent; 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 segment = void 0; if (Array.isArray(value)) { if (!token.repeat) { throw new TypeError("Expected \"".concat(token.name, "\" to not repeat, but got array")); } if (value.length === 0) { if (token.optional) continue; throw new TypeError("Expected \"".concat(token.name, "\" to not be empty")); } for (var j = 0; j < value.length; j++) { segment = encode(value[j], token); if (!matches[i].test(segment)) { throw new TypeError("Expected all \"".concat(token.name, "\" to match \"").concat(token.pattern, "\"")); } path += (j === 0 ? token.prefix : token.delimiter) + segment; } continue; } if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { segment = encode(String(value), token); if (!matches[i].test(segment)) { throw new TypeError("Expected \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\"")); } path += token.prefix + segment; continue; } if (token.optional) { // Prepend partial segment prefixes. if (token.partial) path += token.prefix; continue; } throw new TypeError("Expected \"".concat(token.name, "\" to be ").concat(token.repeat ? 'an array' : 'a string')); } return path; }; } /** * Escape a regular expression string. * @ignore * @param {string} str * @returns {string} */ function escapeString(str) { return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1'); } /** * Escape the capturing group by escaping special characters and meaning. * @ignore * @param {string} group * @returns {string} */ function escapeGroup(group) { return group.replace(/([=!:$/()])/g, '\\$1'); } /** * Get the flags for a regexp from the options. * @ignore * @param {Object} options * @returns {string} */ function flags(options) { return (options === null || options === void 0 ? void 0 : options.sensitive) ? '' : 'i'; } /** * Pull out keys from a regexp. * @ignore * @param {!RegExp} path * @param {Array=} keys * @returns {!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: null, delimiter: null, optional: false, repeat: false, partial: false, pattern: null }); } } return path; } /** * Transform an array into a regexp. * @ignore * @param {!Array} path * @param {Array=} keys * @param {Object=} options * @returns {!RegExp} */ function arrayToRegexp(path, keys, options) { var parts = []; for (var i = 0; i < path.length; i++) { parts.push(pathToRegexp(path[i], keys, options).source); } return new RegExp("(?:".concat(parts.join('|'), ")"), flags(options)); } /** * Create a path regexp from string input. * @ignore * @param {string} path * @param {Array=} keys * @param {Object=} options * @returns {!RegExp} */ function stringToRegexp(path, keys, options) { return tokensToRegExp(parse(path, options), keys, options); } /** * Expose a function for taking tokens and returning a RegExp. * @ignore * @param {!Array} tokens * @param {Array=} keys * @param {Object=} options * @returns {!RegExp} */ function tokensToRegExp(tokens, keys, options) { options = options || {}; var strict = options.strict; var start = options.start !== false; var end = options.end !== false; var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER); var delimiters = options.delimiters || DEFAULT_DELIMITERS; var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|'); var route = start ? '^' : ''; var isEndDelimited = tokens.length === 0; // Iterate over the tokens and create our regexp string. for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; if (typeof token === 'string') { route += escapeString(token); isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1; } else { var capture = token.repeat ? "(?:".concat(token.pattern, ")(?:").concat(escapeString(token.delimiter), "(?:").concat(token.pattern, "))*") : token.pattern; if (keys) keys.push(token); if (token.optional) { if (token.partial) { route += "".concat(escapeString(token.prefix), "(").concat(capture, ")?"); } else { route += "(?:".concat(escapeString(token.prefix), "(").concat(capture, "))?"); } } else { route += "".concat(escapeString(token.prefix), "(").concat(capture, ")"); } } } if (end) { if (!strict) route += "(?:".concat(delimiter, ")?"); route += endsWith === '$' ? '$' : "(?=".concat(endsWith, ")"); } else { if (!strict) route += "(?:".concat(delimiter, "(?=").concat(endsWith, "))?"); if (!isEndDelimited) route += "(?=".concat(delimiter, "|").concat(endsWith, ")"); } 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 }]`. * @ignore * @param {(string|RegExp|Array)} path * @param {Array=} keys * @param {Object=} options * @returns {!RegExp} */ function pathToRegexp(path, keys, options) { if (path instanceof RegExp) { return regexpToRegexp(path, keys); } if (Array.isArray(path)) { return arrayToRegexp(/** @type {!Array} */path, keys, options); } return stringToRegexp(/** @type {string} */path, keys, options); } /** * Expose `pathToRegexp`. * @ignore */ pathToRegexp.parse = parse; pathToRegexp.compile = compile; pathToRegexp.tokensToFunction = tokensToFunction; pathToRegexp.tokensToRegExp = tokensToRegExp; export { compile, pathToRegexp as default, parse, tokensToFunction, tokensToRegExp };