path-to-regexp
Advanced tools
Comparing version 5.0.0 to 6.0.0
/** | ||
* Default configs. | ||
* Tokenize input string. | ||
*/ | ||
var DEFAULT_DELIMITER = "/"; | ||
/** | ||
* Balanced bracket helper function. | ||
*/ | ||
function balanced(open, close, str, index) { | ||
var count = 0; | ||
var i = index; | ||
function lexer(str) { | ||
var tokens = []; | ||
var i = 0; | ||
while (i < str.length) { | ||
if (str[i] === "\\") { | ||
i += 2; | ||
var char = str[i]; | ||
if (char === "*" || char === "+" || char === "?") { | ||
tokens.push({ type: "MODIFIER", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === close) { | ||
count--; | ||
if (count === 0) | ||
return i + 1; | ||
if (char === "\\") { | ||
tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === open) { | ||
count++; | ||
if (char === "{") { | ||
tokens.push({ type: "OPEN", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
i++; | ||
} | ||
return -1; | ||
} | ||
/** | ||
* Parse a string for the raw tokens. | ||
*/ | ||
export 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; | ||
if (char === "}") { | ||
tokens.push({ type: "CLOSE", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === ":") { | ||
while (++i < str.length) { | ||
var code = str.charCodeAt(i); | ||
if (char === ":") { | ||
var name = ""; | ||
var j = i + 1; | ||
while (j < str.length) { | ||
var code = str.charCodeAt(j); | ||
if ( | ||
@@ -65,3 +39,3 @@ // `0-9` | ||
code === 95) { | ||
name += str[i]; | ||
name += str[j++]; | ||
continue; | ||
@@ -71,62 +45,131 @@ } | ||
} | ||
// False positive on param name. | ||
if (!name) | ||
i--; | ||
throw new TypeError("Missing parameter name at " + i); | ||
tokens.push({ type: "NAME", index: i, value: name }); | ||
i = j; | ||
continue; | ||
} | ||
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 (char === "(") { | ||
var count = 1; | ||
var pattern = ""; | ||
var j = i + 1; | ||
if (str[j] === "?") { | ||
throw new TypeError("Pattern cannot start with \"?\" at " + j); | ||
} | ||
while (j < str.length) { | ||
if (str[j] === "\\") { | ||
pattern += str[j++] + str[j++]; | ||
continue; | ||
} | ||
if (/\((?=[^?])/.test(pattern)) { | ||
var validPattern = pattern.replace(/\((?=[^?])/, "(?:"); | ||
throw new TypeError("Capturing groups are not allowed in pattern, use a non-capturing group: (" + validPattern + ")"); | ||
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 " + j); | ||
} | ||
} | ||
pattern += str[j++]; | ||
} | ||
} | ||
// Add regular characters to the path string. | ||
if (name === "" && pattern === "") { | ||
path += str[i++]; | ||
isEscaped = false; | ||
if (count) | ||
throw new TypeError("Unbalanced pattern at " + i); | ||
if (!pattern) | ||
throw new TypeError("Missing pattern at " + i); | ||
tokens.push({ type: "PATTERN", index: i, value: pattern }); | ||
i = j; | ||
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); | ||
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 = "[^" + 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 " + nextType + " at " + index + ", expected " + type); | ||
}; | ||
var consumeText = function () { | ||
var result = ""; | ||
var value; | ||
// tslint:disable-next-line | ||
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; | ||
} | ||
// Push the current path onto the list of tokens. | ||
if (path.length) { | ||
tokens.push(path); | ||
var value = char || tryConsume("ESCAPED_CHAR"); | ||
if (value) { | ||
path += value; | ||
continue; | ||
} | ||
if (path) { | ||
result.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) + "]+?" | ||
}); | ||
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"); | ||
} | ||
if (path.length) | ||
tokens.push(path); | ||
return tokens; | ||
return result; | ||
} | ||
@@ -161,8 +204,10 @@ /** | ||
var value = data ? data[token.name] : undefined; | ||
var optional = token.modifier === "?" || token.modifier === "*"; | ||
var repeat = token.modifier === "*" || token.modifier === "+"; | ||
if (Array.isArray(value)) { | ||
if (!token.repeat) { | ||
if (!repeat) { | ||
throw new TypeError("Expected \"" + token.name + "\" to not repeat, but got an array"); | ||
} | ||
if (value.length === 0) { | ||
if (token.optional) | ||
if (optional) | ||
continue; | ||
@@ -176,3 +221,3 @@ throw new TypeError("Expected \"" + token.name + "\" to not be empty"); | ||
} | ||
path += (j === 0 ? token.prefix : token.delimiter) + segment; | ||
path += token.prefix + segment + token.suffix; | ||
} | ||
@@ -186,8 +231,8 @@ continue; | ||
} | ||
path += token.prefix + segment; | ||
path += token.prefix + segment + token.suffix; | ||
continue; | ||
} | ||
if (token.optional) | ||
if (optional) | ||
continue; | ||
var typeOfMessage = token.repeat ? "an array" : "a string"; | ||
var typeOfMessage = repeat ? "an array" : "a string"; | ||
throw new TypeError("Expected \"" + token.name + "\" to be " + typeOfMessage); | ||
@@ -223,4 +268,4 @@ } | ||
var key = keys[i - 1]; | ||
if (key.repeat) { | ||
params[key.name] = m[i].split(key.delimiter).map(function (value) { | ||
if (key.modifier === "*" || key.modifier === "+") { | ||
params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) { | ||
return decode(value, key); | ||
@@ -264,5 +309,4 @@ }); | ||
prefix: "", | ||
delimiter: "", | ||
optional: false, | ||
repeat: false, | ||
suffix: "", | ||
modifier: "", | ||
pattern: "" | ||
@@ -292,9 +336,5 @@ }); | ||
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, _d = options.encode, encode = _d === void 0 ? function (x) { return x; } : _d; | ||
var endsWith = (typeof options.endsWith === "string" | ||
? options.endsWith.split("") | ||
: options.endsWith || []) | ||
.map(escapeString) | ||
.concat("$") | ||
.join("|"); | ||
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; | ||
var endsWith = "[" + escapeString(options.endsWith || "") + "]|$"; | ||
var delimiter = "[" + escapeString(options.delimiter || "/") + "]"; | ||
var route = start ? "^" : ""; | ||
@@ -308,17 +348,22 @@ // Iterate over the tokens and create our regexp string. | ||
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 + ")?"; | ||
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 += "(?:" + prefix + "((?:" + token.pattern + ")(?:" + suffix + prefix + "(?:" + token.pattern + "))*)" + suffix + ")" + mod; | ||
} | ||
else { | ||
route += "(?:" + prefix + "(" + token.pattern + ")" + suffix + ")" + token.modifier; | ||
} | ||
} | ||
else { | ||
route += "(?:" + escapeString(token.prefix) + "(" + capture + "))?"; | ||
route += "(" + token.pattern + ")" + token.modifier; | ||
} | ||
} | ||
else { | ||
route += escapeString(token.prefix) + "(" + capture + ")"; | ||
route += "(?:" + prefix + suffix + ")" + token.modifier; | ||
} | ||
@@ -329,3 +374,3 @@ } | ||
if (!strict) | ||
route += "(?:" + escapeString(delimiter) + ")?"; | ||
route += delimiter + "?"; | ||
route += endsWith === "$" ? "$" : "(?=" + endsWith + ")"; | ||
@@ -336,10 +381,10 @@ } | ||
var isEndDelimited = typeof endToken === "string" | ||
? endToken[endToken.length - 1] === delimiter | ||
? delimiter.indexOf(endToken[endToken.length - 1]) > -1 | ||
: // tslint:disable-next-line | ||
endToken === undefined; | ||
if (!strict) { | ||
route += "(?:" + escapeString(delimiter) + "(?=" + endsWith + "))?"; | ||
route += "(?:" + delimiter + "(?=" + endsWith + "))?"; | ||
} | ||
if (!isEndDelimited) { | ||
route += "(?=" + escapeString(delimiter) + "|" + endsWith + ")"; | ||
route += "(?=" + delimiter + "|" + endsWith + ")"; | ||
} | ||
@@ -357,10 +402,8 @@ } | ||
export function pathToRegexp(path, keys, options) { | ||
if (path instanceof RegExp) { | ||
if (path instanceof RegExp) | ||
return regexpToRegexp(path, keys); | ||
} | ||
if (Array.isArray(path)) { | ||
if (Array.isArray(path)) | ||
return arrayToRegexp(path, keys, options); | ||
} | ||
return stringToRegexp(path, keys, options); | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -7,5 +7,5 @@ export interface ParseOptions { | ||
/** | ||
* List of characters to consider delimiters when parsing. (default: `undefined`, any character) | ||
* List of characters to automatically consider prefixes when parsing. | ||
*/ | ||
whitelist?: string | string[]; | ||
prefixes?: string; | ||
} | ||
@@ -75,6 +75,5 @@ /** | ||
prefix: string; | ||
delimiter: string; | ||
optional: boolean; | ||
repeat: boolean; | ||
suffix: string; | ||
pattern: string; | ||
modifier: string; | ||
} | ||
@@ -109,3 +108,3 @@ /** | ||
*/ | ||
endsWith?: string | string[]; | ||
endsWith?: string; | ||
/** | ||
@@ -112,0 +111,0 @@ * Encode path tokens for use in the `RegExp`. |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Default configs. | ||
* Tokenize input string. | ||
*/ | ||
var DEFAULT_DELIMITER = "/"; | ||
/** | ||
* Balanced bracket helper function. | ||
*/ | ||
function balanced(open, close, str, index) { | ||
var count = 0; | ||
var i = index; | ||
function lexer(str) { | ||
var tokens = []; | ||
var i = 0; | ||
while (i < str.length) { | ||
if (str[i] === "\\") { | ||
i += 2; | ||
var char = str[i]; | ||
if (char === "*" || char === "+" || char === "?") { | ||
tokens.push({ type: "MODIFIER", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === close) { | ||
count--; | ||
if (count === 0) | ||
return i + 1; | ||
if (char === "\\") { | ||
tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === open) { | ||
count++; | ||
if (char === "{") { | ||
tokens.push({ type: "OPEN", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
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; | ||
if (char === "}") { | ||
tokens.push({ type: "CLOSE", index: i, value: str[i++] }); | ||
continue; | ||
} | ||
if (str[i] === ":") { | ||
while (++i < str.length) { | ||
var code = str.charCodeAt(i); | ||
if (char === ":") { | ||
var name = ""; | ||
var j = i + 1; | ||
while (j < str.length) { | ||
var code = str.charCodeAt(j); | ||
if ( | ||
@@ -67,3 +41,3 @@ // `0-9` | ||
code === 95) { | ||
name += str[i]; | ||
name += str[j++]; | ||
continue; | ||
@@ -73,62 +47,131 @@ } | ||
} | ||
// False positive on param name. | ||
if (!name) | ||
i--; | ||
throw new TypeError("Missing parameter name at " + i); | ||
tokens.push({ type: "NAME", index: i, value: name }); | ||
i = j; | ||
continue; | ||
} | ||
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 (char === "(") { | ||
var count = 1; | ||
var pattern = ""; | ||
var j = i + 1; | ||
if (str[j] === "?") { | ||
throw new TypeError("Pattern cannot start with \"?\" at " + j); | ||
} | ||
while (j < str.length) { | ||
if (str[j] === "\\") { | ||
pattern += str[j++] + str[j++]; | ||
continue; | ||
} | ||
if (/\((?=[^?])/.test(pattern)) { | ||
var validPattern = pattern.replace(/\((?=[^?])/, "(?:"); | ||
throw new TypeError("Capturing groups are not allowed in pattern, use a non-capturing group: (" + validPattern + ")"); | ||
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 " + j); | ||
} | ||
} | ||
pattern += str[j++]; | ||
} | ||
} | ||
// Add regular characters to the path string. | ||
if (name === "" && pattern === "") { | ||
path += str[i++]; | ||
isEscaped = false; | ||
if (count) | ||
throw new TypeError("Unbalanced pattern at " + i); | ||
if (!pattern) | ||
throw new TypeError("Missing pattern at " + i); | ||
tokens.push({ type: "PATTERN", index: i, value: pattern }); | ||
i = j; | ||
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); | ||
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. | ||
*/ | ||
function parse(str, options) { | ||
if (options === void 0) { options = {}; } | ||
var tokens = lexer(str); | ||
var _a = options.prefixes, prefixes = _a === void 0 ? "./" : _a; | ||
var defaultPattern = "[^" + 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 " + nextType + " at " + index + ", expected " + type); | ||
}; | ||
var consumeText = function () { | ||
var result = ""; | ||
var value; | ||
// tslint:disable-next-line | ||
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; | ||
} | ||
// Push the current path onto the list of tokens. | ||
if (path.length) { | ||
tokens.push(path); | ||
var value = char || tryConsume("ESCAPED_CHAR"); | ||
if (value) { | ||
path += value; | ||
continue; | ||
} | ||
if (path) { | ||
result.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) + "]+?" | ||
}); | ||
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"); | ||
} | ||
if (path.length) | ||
tokens.push(path); | ||
return tokens; | ||
return result; | ||
} | ||
@@ -165,8 +208,10 @@ exports.parse = parse; | ||
var value = data ? data[token.name] : undefined; | ||
var optional = token.modifier === "?" || token.modifier === "*"; | ||
var repeat = token.modifier === "*" || token.modifier === "+"; | ||
if (Array.isArray(value)) { | ||
if (!token.repeat) { | ||
if (!repeat) { | ||
throw new TypeError("Expected \"" + token.name + "\" to not repeat, but got an array"); | ||
} | ||
if (value.length === 0) { | ||
if (token.optional) | ||
if (optional) | ||
continue; | ||
@@ -180,3 +225,3 @@ throw new TypeError("Expected \"" + token.name + "\" to not be empty"); | ||
} | ||
path += (j === 0 ? token.prefix : token.delimiter) + segment; | ||
path += token.prefix + segment + token.suffix; | ||
} | ||
@@ -190,8 +235,8 @@ continue; | ||
} | ||
path += token.prefix + segment; | ||
path += token.prefix + segment + token.suffix; | ||
continue; | ||
} | ||
if (token.optional) | ||
if (optional) | ||
continue; | ||
var typeOfMessage = token.repeat ? "an array" : "a string"; | ||
var typeOfMessage = repeat ? "an array" : "a string"; | ||
throw new TypeError("Expected \"" + token.name + "\" to be " + typeOfMessage); | ||
@@ -229,4 +274,4 @@ } | ||
var key = keys[i - 1]; | ||
if (key.repeat) { | ||
params[key.name] = m[i].split(key.delimiter).map(function (value) { | ||
if (key.modifier === "*" || key.modifier === "+") { | ||
params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) { | ||
return decode(value, key); | ||
@@ -271,5 +316,4 @@ }); | ||
prefix: "", | ||
delimiter: "", | ||
optional: false, | ||
repeat: false, | ||
suffix: "", | ||
modifier: "", | ||
pattern: "" | ||
@@ -299,9 +343,5 @@ }); | ||
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, _d = options.encode, encode = _d === void 0 ? function (x) { return x; } : _d; | ||
var endsWith = (typeof options.endsWith === "string" | ||
? options.endsWith.split("") | ||
: options.endsWith || []) | ||
.map(escapeString) | ||
.concat("$") | ||
.join("|"); | ||
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; | ||
var endsWith = "[" + escapeString(options.endsWith || "") + "]|$"; | ||
var delimiter = "[" + escapeString(options.delimiter || "/") + "]"; | ||
var route = start ? "^" : ""; | ||
@@ -315,17 +355,22 @@ // Iterate over the tokens and create our regexp string. | ||
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 + ")?"; | ||
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 += "(?:" + prefix + "((?:" + token.pattern + ")(?:" + suffix + prefix + "(?:" + token.pattern + "))*)" + suffix + ")" + mod; | ||
} | ||
else { | ||
route += "(?:" + prefix + "(" + token.pattern + ")" + suffix + ")" + token.modifier; | ||
} | ||
} | ||
else { | ||
route += "(?:" + escapeString(token.prefix) + "(" + capture + "))?"; | ||
route += "(" + token.pattern + ")" + token.modifier; | ||
} | ||
} | ||
else { | ||
route += escapeString(token.prefix) + "(" + capture + ")"; | ||
route += "(?:" + prefix + suffix + ")" + token.modifier; | ||
} | ||
@@ -336,3 +381,3 @@ } | ||
if (!strict) | ||
route += "(?:" + escapeString(delimiter) + ")?"; | ||
route += delimiter + "?"; | ||
route += endsWith === "$" ? "$" : "(?=" + endsWith + ")"; | ||
@@ -343,10 +388,10 @@ } | ||
var isEndDelimited = typeof endToken === "string" | ||
? endToken[endToken.length - 1] === delimiter | ||
? delimiter.indexOf(endToken[endToken.length - 1]) > -1 | ||
: // tslint:disable-next-line | ||
endToken === undefined; | ||
if (!strict) { | ||
route += "(?:" + escapeString(delimiter) + "(?=" + endsWith + "))?"; | ||
route += "(?:" + delimiter + "(?=" + endsWith + "))?"; | ||
} | ||
if (!isEndDelimited) { | ||
route += "(?=" + escapeString(delimiter) + "|" + endsWith + ")"; | ||
route += "(?=" + delimiter + "|" + endsWith + ")"; | ||
} | ||
@@ -365,8 +410,6 @@ } | ||
function pathToRegexp(path, keys, options) { | ||
if (path instanceof RegExp) { | ||
if (path instanceof RegExp) | ||
return regexpToRegexp(path, keys); | ||
} | ||
if (Array.isArray(path)) { | ||
if (Array.isArray(path)) | ||
return arrayToRegexp(path, keys, options); | ||
} | ||
return stringToRegexp(path, keys, options); | ||
@@ -373,0 +416,0 @@ } |
{ | ||
"name": "path-to-regexp", | ||
"description": "Express style path to RegExp utility", | ||
"version": "5.0.0", | ||
"version": "6.0.0", | ||
"main": "dist/index.js", | ||
@@ -37,3 +37,3 @@ "typings": "dist/index.d.ts", | ||
"path": "dist/index.js", | ||
"limit": "1.75 kB" | ||
"limit": "2 kB" | ||
} | ||
@@ -40,0 +40,0 @@ ], |
136
Readme.md
@@ -36,6 +36,6 @@ # Path-to-RegExp | ||
- **start** When `true` the regexp will match from the beginning of the string. (default: `true`) | ||
- **delimiter** The default delimiter for segments. (default: `'/'`) | ||
- **delimiter** The default delimiter for segments, e.g. `[^/]` for `:named` patterns. (default: `'/'`) | ||
- **endsWith** Optional character, or list of characters, to treat as "end" characters. | ||
- **whitelist** List of characters to consider delimiters when parsing. (default: `undefined`, any character) | ||
- **encode** A function to encode strings before inserting into `RegExp`. (default: `x => x`) | ||
- **prefixes** List of characters to automatically consider prefixes when parsing. (default: `./`) | ||
@@ -53,7 +53,7 @@ ```javascript | ||
The path argument is used to define parameters and populate the list of keys. | ||
The path argument is used to define parameters and populate keys. | ||
#### Named Parameters | ||
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, the parameter will match until the next prefix (e.g. `[^/]+`). | ||
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). | ||
@@ -70,4 +70,58 @@ ```js | ||
#### Parameter Modifiers | ||
##### Custom Matching Parameters | ||
Parameters can have a custom regexp, which overrides the default match (`[^/]+`). For example, you can match digits or names in a path: | ||
```js | ||
const regexpNumbers = pathToRegexp("/icon-:foo(\\d+).png"); | ||
// keys = [{ name: 'foo', ... }] | ||
regexpNumbers.exec("/icon-123.png"); | ||
//=> ['/icon-123.png', '123'] | ||
regexpNumbers.exec("/icon-abc.png"); | ||
//=> null | ||
const regexpWord = pathToRegexp("/(user|u)"); | ||
// keys = [{ name: 0, ... }] | ||
regexpWord.exec("/u"); | ||
//=> ['/u', 'u'] | ||
regexpWord.exec("/users"); | ||
//=> null | ||
``` | ||
**Tip:** Backslashes need to be escaped with another backslash in JavaScript strings. | ||
##### Custom Prefix and Suffix | ||
Parameters can be wrapped in `{}` to create custom prefixes or suffixes for your segment: | ||
```js | ||
const regexp = pathToRegexp("/:attr1?{-:attr2}?{-:attr3}?"); | ||
regexp.exec("/test"); | ||
// => ['/test', 'test', undefined, undefined] | ||
regexp.exec("/test-test"); | ||
// => ['/test', 'test', 'test', undefined] | ||
``` | ||
#### Unnamed Parameters | ||
It is possible to write an unnamed parameter that only consists of a regexp. It works the same the named parameter, except it will be numerically indexed: | ||
```js | ||
const regexp = pathToRegexp("/:foo/(.*)"); | ||
// keys = [{ name: 'foo', ... }, { name: 0, ... }] | ||
regexp.exec("/test/route"); | ||
//=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ] | ||
``` | ||
#### Modifiers | ||
Modifiers must be placed after the parameter (e.g. `/:foo?`, `/(test)?`, or `/:foo(test)?`). | ||
##### Optional | ||
@@ -92,3 +146,3 @@ | ||
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is used for each match. | ||
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. | ||
@@ -108,3 +162,3 @@ ```js | ||
Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefix is used for each match. | ||
Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. | ||
@@ -122,58 +176,32 @@ ```js | ||
#### Unnamed Parameters | ||
### Match | ||
It is possible to write an unnamed parameter that only consists of a matching group. It works the same as a named parameter, except it will be numerically indexed. | ||
The `match` function will return a function for transforming paths into parameters: | ||
```js | ||
const regexp = pathToRegexp("/:foo/(.*)"); | ||
// keys = [{ name: 'foo', ... }, { name: 0, ... }] | ||
// Make sure you consistently `decode` segments. | ||
const match = match("/user/:id", { decode: decodeURIComponent }); | ||
regexp.exec("/test/route"); | ||
//=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ] | ||
match("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } } | ||
match("/invalid"); //=> false | ||
match("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } } | ||
``` | ||
#### Custom Matching Parameters | ||
#### Process Pathname | ||
All parameters can have a custom regexp, which overrides the default match (`[^/]+`). For example, you can match digits or names in a path: | ||
You should make sure variations of the same path match the expected `path`. Here's one possible solution using `encode`: | ||
```js | ||
const regexpNumbers = pathToRegexp("/icon-:foo(\\d+).png"); | ||
// keys = [{ name: 'foo', ... }] | ||
const match = match("/café", { encode: encodeURI, decode: decodeURIComponent }); | ||
regexpNumbers.exec("/icon-123.png"); | ||
//=> ['/icon-123.png', '123'] | ||
regexpNumbers.exec("/icon-abc.png"); | ||
//=> null | ||
const regexpWord = pathToRegexp("/(user|u)"); | ||
// keys = [{ name: 0, ... }] | ||
regexpWord.exec("/u"); | ||
//=> ['/u', 'u'] | ||
regexpWord.exec("/users"); | ||
//=> null | ||
match("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } } | ||
``` | ||
**Tip:** Backslashes need to be escaped with another backslash in JavaScript strings. | ||
**Note:** [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) automatically encodes pathnames for you. | ||
### Match | ||
##### Alternative Using Normalize | ||
The `match` function will return a function for transforming paths into parameters: | ||
Sometimes you won't have an already normalized pathname. You can normalize it yourself before processing: | ||
```js | ||
// Make sure you consistently `decode` segments. | ||
const match = match("/user/:id", { decode: decodeURIComponent }); | ||
match("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } } | ||
match("/invalid"); //=> false | ||
match("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } } | ||
``` | ||
#### Normalize Pathname | ||
You should make sure variations of the same path to match your input `path`. Here's one possible solution: | ||
```js | ||
/** | ||
@@ -195,2 +223,3 @@ * Normalize a pathname for matching, replaces multiple slashes with a single | ||
// Two possible ways of writing `/café`: | ||
const re = pathToRegexp("/caf\u00E9"); | ||
@@ -203,4 +232,2 @@ const input = encodeURI("/cafe\u0301"); | ||
**Note:** [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) automatically encodes pathnames for you, which would result in a consistent match if you use `encodeURI` in `pathToRegexp` options. | ||
### Parse | ||
@@ -258,7 +285,7 @@ | ||
**Note:** The generated function will throw on invalid input. It will do all necessary checks to ensure the generated path is valid. This method only works with strings. | ||
**Note:** The generated function will throw on invalid input. | ||
### Working with Tokens | ||
Path-To-RegExp exposes the two functions used internally that accept an array of tokens. | ||
Path-To-RegExp exposes the two functions used internally that accept an array of tokens: | ||
@@ -271,7 +298,6 @@ - `tokensToRegexp(tokens, keys?, options?)` Transform an array of tokens into a matching regular expression. | ||
- `name` The name of the token (`string` for named or `number` for unnamed index) | ||
- `prefix` The prefix character for the segment (e.g. `/`) | ||
- `delimiter` The delimiter for the segment (same as prefix or default delimiter) | ||
- `optional` Indicates the token is optional (`boolean`) | ||
- `repeat` Indicates the token is repeated (`boolean`) | ||
- `prefix` The prefix string for the segment (e.g. `"/"`) | ||
- `suffix` The suffix string for the segment (e.g. `""`) | ||
- `pattern` The RegExp used to match this token (`string`) | ||
- `modifier` The modifier character used for the segment (e.g. `?`) | ||
@@ -278,0 +304,0 @@ ## Compatibility with Express <= 4.x |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
323
468051
6307