Socket
Socket
Sign inDemoInstall

path-to-regexp

Package Overview
Dependencies
Maintainers
0
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

path-to-regexp - npm Package Compare versions

Comparing version 6.2.2 to 7.0.0

149

dist/index.d.ts

@@ -0,1 +1,9 @@

/**
* Encode a string into another string.
*/
export type Encode = (value: string) => string;
/**
* Decode a string into another string.
*/
export type Decode = (value: string) => string;
export interface ParseOptions {

@@ -7,11 +15,7 @@ /**

/**
* List of characters to automatically consider prefixes when parsing.
* Function for encoding input strings for output into path.
*/
prefixes?: string;
encodePath?: Encode;
}
/**
* Parse a string for the raw tokens.
*/
export declare function parse(str: string, options?: ParseOptions): Token[];
export interface TokensToFunctionOptions {
export interface PathToRegexpOptions extends ParseOptions {
/**

@@ -22,29 +26,64 @@ * When `true` the regexp will be case sensitive. (default: `false`)

/**
* Function for encoding input strings for output.
* Allow delimiter to be arbitrarily repeated. (default: `true`)
*/
encode?: (value: string, token: Key) => string;
loose?: boolean;
/**
* When `true` the regexp will match to the end of the string. (default: `true`)
*/
end?: boolean;
/**
* When `true` the regexp will match from the beginning of the string. (default: `true`)
*/
start?: boolean;
/**
* When `true` the regexp allows an optional trailing delimiter to match. (default: `true`)
*/
trailing?: boolean;
}
export interface MatchOptions extends PathToRegexpOptions {
/**
* Function for decoding strings for params, or `false` to disable entirely. (default: `decodeURIComponent`)
*/
decode?: Decode | false;
}
export interface CompileOptions extends ParseOptions {
/**
* When `true` the validation will be case sensitive. (default: `false`)
*/
sensitive?: boolean;
/**
* Allow delimiter to be arbitrarily repeated. (default: `true`)
*/
loose?: boolean;
/**
* When `false` the function can produce an invalid (unmatched) path. (default: `true`)
*/
validate?: boolean;
/**
* Function for encoding input strings for output into the path, or `false` to disable entirely. (default: `encodeURIComponent`)
*/
encode?: Encode | false;
}
/**
* Compile a string to a template function for the path.
* Tokenized path instance. Can we passed around instead of string.
*/
export declare function compile<P extends object = object>(str: string, options?: ParseOptions & TokensToFunctionOptions): PathFunction<P>;
export type PathFunction<P extends object = object> = (data?: P) => string;
export declare class TokenData {
readonly tokens: Token[];
readonly delimiter: string;
constructor(tokens: Token[], delimiter: string);
}
/**
* Expose a method for transforming tokens into the path function.
* Parse a string for the raw tokens.
*/
export declare function tokensToFunction<P extends object = object>(tokens: Token[], options?: TokensToFunctionOptions): PathFunction<P>;
export interface RegexpToFunctionOptions {
/**
* Function for decoding strings for params.
*/
decode?: (value: string, token: Key) => string;
}
export declare function parse(str: string, options?: ParseOptions): TokenData;
/**
* Compile a string to a template function for the path.
*/
export declare function compile<P extends object = object>(path: Path, options?: CompileOptions): PathFunction<P>;
export type ParamData = Partial<Record<string, string | string[]>>;
export type PathFunction<P extends ParamData> = (data?: P) => string;
/**
* A match result contains data about the path match.
*/
export interface MatchResult<P extends object = object> {
export interface MatchResult<P extends ParamData> {
path: string;

@@ -57,24 +96,21 @@ index: number;

*/
export type Match<P extends object = object> = false | MatchResult<P>;
export type Match<P extends ParamData> = false | MatchResult<P>;
/**
* The match function takes a string and returns whether it matched the path.
*/
export type MatchFunction<P extends object = object> = (path: string) => Match<P>;
export type MatchFunction<P extends ParamData> = (path: string) => Match<P>;
/**
* Create path match function from `path-to-regexp` spec.
*/
export declare function match<P extends object = object>(str: Path, options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions): MatchFunction<P>;
export declare function match<P extends ParamData>(path: Path, options?: MatchOptions): MatchFunction<P>;
/**
* Create a path match function from `path-to-regexp` output.
* A key is a capture group in the regex.
*/
export declare function regexpToFunction<P extends object = object>(re: RegExp, keys: Key[], options?: RegexpToFunctionOptions): MatchFunction<P>;
/**
* Metadata about a key.
*/
export interface Key {
name: string | number;
prefix: string;
suffix: string;
pattern: string;
modifier: string;
name: string;
prefix?: string;
suffix?: string;
pattern?: string;
modifier?: string;
separator?: string;
}

@@ -85,41 +121,10 @@ /**

export type Token = string | Key;
export interface TokensToRegexpOptions {
/**
* When `true` the regexp will be case sensitive. (default: `false`)
*/
sensitive?: boolean;
/**
* When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)
*/
strict?: boolean;
/**
* When `true` the regexp will match to the end of the string. (default: `true`)
*/
end?: boolean;
/**
* When `true` the regexp will match from the beginning of the string. (default: `true`)
*/
start?: boolean;
/**
* Sets the final character for non-ending optimistic matches. (default: `/`)
*/
delimiter?: string;
/**
* List of characters that can also be "end" characters.
*/
endsWith?: string;
/**
* Encode path tokens for use in the `RegExp`.
*/
encode?: (value: string) => string;
}
/**
* Expose a function for taking tokens and returning a RegExp.
* Repeated and simple input types.
*/
export declare function tokensToRegexp(tokens: Token[], keys?: Key[], options?: TokensToRegexpOptions): RegExp;
export type Path = string | TokenData;
export type PathRegExp = RegExp & {
keys: Key[];
};
/**
* Supported `path-to-regexp` input types.
*/
export type Path = string | RegExp | Array<string | RegExp>;
/**
* Normalize the given path string, returning a regular expression.

@@ -131,2 +136,4 @@ *

*/
export declare function pathToRegexp(path: Path, keys?: Key[], options?: TokensToRegexpOptions & ParseOptions): RegExp;
export declare function pathToRegexp(path: Path, options?: PathToRegexpOptions): RegExp & {
keys: Key[];
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pathToRegexp = exports.tokensToRegexp = exports.regexpToFunction = exports.match = exports.tokensToFunction = exports.compile = exports.parse = void 0;
exports.pathToRegexp = exports.match = exports.compile = exports.parse = exports.TokenData = void 0;
const DEFAULT_DELIMITER = "/";
const NOOP_VALUE = (value) => value;
const ID_CHAR = /^\p{XID_Continue}$/u;
const SIMPLE_TOKENS = {
"!": "!",
"@": "@",
";": ";",
",": ",",
"*": "*",
"+": "+",
"?": "?",
"{": "{",
"}": "}",
};
/**

@@ -8,169 +22,168 @@ * 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++] });
const chars = [...str];
const tokens = [];
let i = 0;
while (i < chars.length) {
const value = chars[i];
const type = SIMPLE_TOKENS[value];
if (type) {
tokens.push({ type, index: i++, value });
continue;
}
if (char === "\\") {
tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] });
if (value === "\\") {
tokens.push({ type: "ESCAPED", index: i++, value: chars[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 (value === ":") {
let name = "";
while (ID_CHAR.test(chars[++i])) {
name += chars[i];
}
if (!name)
throw new TypeError("Missing parameter name at ".concat(i));
if (!name) {
throw new TypeError(`Missing parameter name at ${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));
if (value === "(") {
const pos = i++;
let count = 1;
let pattern = "";
if (chars[i] === "?") {
throw new TypeError(`Pattern cannot start with "?" at ${i}`);
}
while (j < str.length) {
if (str[j] === "\\") {
pattern += str[j++] + str[j++];
while (i < chars.length) {
if (chars[i] === "\\") {
pattern += chars[i++] + chars[i++];
continue;
}
if (str[j] === ")") {
if (chars[i] === ")") {
count--;
if (count === 0) {
j++;
i++;
break;
}
}
else if (str[j] === "(") {
else if (chars[i] === "(") {
count++;
if (str[j + 1] !== "?") {
throw new TypeError("Capturing groups are not allowed at ".concat(j));
if (chars[i + 1] !== "?") {
throw new TypeError(`Capturing groups are not allowed at ${i}`);
}
}
pattern += str[j++];
pattern += chars[i++];
}
if (count)
throw new TypeError("Unbalanced pattern at ".concat(i));
throw new TypeError(`Unbalanced pattern at ${pos}`);
if (!pattern)
throw new TypeError("Missing pattern at ".concat(i));
throw new TypeError(`Missing pattern at ${pos}`);
tokens.push({ type: "PATTERN", index: i, value: pattern });
i = j;
continue;
}
tokens.push({ type: "CHAR", index: i, value: str[i++] });
tokens.push({ type: "CHAR", index: i, value: chars[i++] });
}
tokens.push({ type: "END", index: i, value: "" });
return tokens;
return new Iter(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 = "[^".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);
class Iter {
constructor(tokens) {
this.tokens = tokens;
this.index = 0;
}
peek() {
return this.tokens[this.index];
}
tryConsume(type) {
const token = this.peek();
if (token.type !== type)
return;
this.index++;
return token.value;
}
consume(type) {
const value = this.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"))) {
const { type: nextType, index } = this.peek();
throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}: https://git.new/pathToRegexpError`);
}
text() {
let result = "";
let value;
while ((value = this.tryConsume("CHAR") || this.tryConsume("ESCAPED"))) {
result += value;
}
return result;
};
while (i < tokens.length) {
var char = tryConsume("CHAR");
var name = tryConsume("NAME");
var pattern = tryConsume("PATTERN");
}
modifier() {
return (this.tryConsume("?") || this.tryConsume("*") || this.tryConsume("+") || "");
}
}
/**
* Tokenized path instance. Can we passed around instead of string.
*/
class TokenData {
constructor(tokens, delimiter) {
this.tokens = tokens;
this.delimiter = delimiter;
}
}
exports.TokenData = TokenData;
/**
* Parse a string for the raw tokens.
*/
function parse(str, options = {}) {
const { delimiter = DEFAULT_DELIMITER, encodePath = NOOP_VALUE } = options;
const tokens = [];
const it = lexer(str);
let key = 0;
do {
const path = it.text();
if (path)
tokens.push(encodePath(path));
const name = it.tryConsume("NAME");
const pattern = it.tryConsume("PATTERN");
if (name || pattern) {
var prefix = char || "";
if (prefixes.indexOf(prefix) === -1) {
path += prefix;
prefix = "";
tokens.push({
name: name || String(key++),
pattern,
});
const next = it.peek();
if (next.type === "*") {
throw new TypeError(`Unexpected * at ${next.index}, you probably want \`/*\` or \`{/:foo}*\`: https://git.new/pathToRegexpError`);
}
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;
const asterisk = it.tryConsume("*");
if (asterisk) {
tokens.push({
name: String(key++),
pattern: `[^${escape(delimiter)}]*`,
modifier: "*",
separator: delimiter,
});
continue;
}
if (path) {
result.push(path);
path = "";
}
var open = tryConsume("OPEN");
const open = it.tryConsume("{");
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") || "",
const prefix = it.text();
const name = it.tryConsume("NAME");
const pattern = it.tryConsume("PATTERN");
const suffix = it.text();
const separator = it.tryConsume(";") ? it.text() : prefix + suffix;
it.consume("}");
const modifier = it.modifier();
tokens.push({
name: name || (pattern ? String(key++) : ""),
prefix: encodePath(prefix),
suffix: encodePath(suffix),
pattern,
modifier,
separator,
});
continue;
}
mustConsume("END");
}
return result;
it.consume("END");
break;
} while (true);
return new TokenData(tokens, delimiter);
}

@@ -181,217 +194,199 @@ exports.parse = parse;

*/
function compile(str, options) {
return tokensToFunction(parse(str, options), options);
function compile(path, options = {}) {
const data = path instanceof TokenData ? path : parse(path, options);
return compileTokens(data, options);
}
exports.compile = compile;
/**
* Expose a method for transforming tokens into the path function.
* Convert a single token into a path building function.
*/
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;
function tokenToFunction(token, encode) {
if (typeof token === "string") {
return () => token;
}
const encodeValue = encode || NOOP_VALUE;
const repeated = token.modifier === "+" || token.modifier === "*";
const optional = token.modifier === "?" || token.modifier === "*";
const { prefix = "", suffix = "", separator = "" } = token;
if (encode && repeated) {
const stringify = (value, index) => {
if (typeof value !== "string") {
throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
}
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;
return encodeValue(value);
};
const compile = (value) => {
if (!Array.isArray(value)) {
throw new TypeError(`Expected "${token.name}" to be an array`);
}
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));
if (value.length === 0)
return "";
return prefix + value.map(stringify).join(separator) + suffix;
};
if (optional) {
return (data) => {
const value = data[token.name];
if (value == null)
return "";
return value.length ? compile(value) : "";
};
}
return path;
return (data) => {
const value = data[token.name];
return compile(value);
};
}
const stringify = (value) => {
if (typeof value !== "string") {
throw new TypeError(`Expected "${token.name}" to be a string`);
}
return prefix + encodeValue(value) + suffix;
};
if (optional) {
return (data) => {
const value = data[token.name];
if (value == null)
return "";
return stringify(value);
};
}
return (data) => {
const value = data[token.name];
return stringify(value);
};
}
exports.tokensToFunction = tokensToFunction;
/**
* Create path match function from `path-to-regexp` spec.
* Transform tokens into a path building function.
*/
function match(str, options) {
var keys = [];
var re = pathToRegexp(str, keys, options);
return regexpToFunction(re, keys, options);
function compileTokens(data, options) {
const { encode = encodeURIComponent, loose = true, validate = true, } = options;
const reFlags = flags(options);
const stringify = toStringify(loose, data.delimiter);
const keyToRegexp = toKeyRegexp(stringify, data.delimiter);
// Compile all the tokens into regexps.
const encoders = data.tokens.map((token) => {
const fn = tokenToFunction(token, encode);
if (!validate || typeof token === "string")
return fn;
const pattern = keyToRegexp(token);
const validRe = new RegExp(`^${pattern}$`, reFlags);
return (data) => {
const value = fn(data);
if (!validRe.test(value)) {
throw new TypeError(`Invalid value for "${token.name}": ${JSON.stringify(value)}`);
}
return value;
};
});
return function path(data = {}) {
let path = "";
for (const encoder of encoders)
path += encoder(data);
return path;
};
}
exports.match = match;
/**
* Create a path match function from `path-to-regexp` output.
* Create path match function from `path-to-regexp` spec.
*/
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);
function match(path, options = {}) {
const { decode = decodeURIComponent, loose = true } = options;
const data = path instanceof TokenData ? path : parse(path, options);
const stringify = toStringify(loose, data.delimiter);
const keys = [];
const re = tokensToRegexp(data, keys, options);
const decoders = keys.map((key) => {
if (decode && (key.modifier === "+" || key.modifier === "*")) {
const re = new RegExp(stringify(key.separator || ""), "g");
return (value) => value.split(re).map(decode);
}
return decode || NOOP_VALUE;
});
return function match(pathname) {
const 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) {
const { 0: path, index } = m;
const params = Object.create(null);
for (let i = 1; i < m.length; 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);
continue;
const key = keys[i - 1];
const decoder = decoders[i - 1];
params[key.name] = decoder(m[i]);
}
return { path: path, index: index, params: params };
return { path, index, params };
};
}
exports.regexpToFunction = regexpToFunction;
exports.match = match;
/**
* Escape a regular expression string.
*/
function escapeString(str) {
function escape(str) {
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
}
/**
* Get the flags for a regexp from the options.
* Escape and repeat loose characters for regular expressions.
*/
function flags(options) {
return options && options.sensitive ? "" : "i";
function looseReplacer(value, loose) {
return loose ? `${escape(value)}+` : escape(value);
}
/**
* Pull out keys from a regexp.
* Encode all non-delimiter characters using the encode function.
*/
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;
function toStringify(loose, delimiter) {
if (!loose)
return escape;
const re = new RegExp(`[^${escape(delimiter)}]+|(.)`, "g");
return (value) => value.replace(re, looseReplacer);
}
/**
* Transform an array into a regexp.
* Get the flags for a regexp from the options.
*/
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));
function flags(options) {
return options.sensitive ? "" : "i";
}
/**
* 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 _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];
function tokensToRegexp(data, keys, options) {
const { trailing = true, start = true, end = true, loose = true } = options;
const stringify = toStringify(loose, data.delimiter);
const keyToRegexp = toKeyRegexp(stringify, data.delimiter);
let pattern = start ? "^" : "";
for (const token of data.tokens) {
if (typeof token === "string") {
route += escapeString(encode(token));
pattern += stringify(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 (token.name)
keys.push(token);
pattern += keyToRegexp(token);
}
}
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 (trailing)
pattern += `(?:${stringify(data.delimiter)})?`;
pattern += end ? "$" : `(?=${escape(data.delimiter)}|$)`;
return new RegExp(pattern, flags(options));
}
/**
* Convert a token into a regexp string (re-used for path validation).
*/
function toKeyRegexp(stringify, delimiter) {
const segmentPattern = `[^${escape(delimiter)}]+?`;
return (key) => {
const prefix = key.prefix ? stringify(key.prefix) : "";
const suffix = key.suffix ? stringify(key.suffix) : "";
const modifier = key.modifier || "";
if (key.name) {
const pattern = key.pattern || segmentPattern;
if (key.modifier === "+" || key.modifier === "*") {
const mod = key.modifier === "*" ? "?" : "";
const split = key.separator ? stringify(key.separator) : "";
return `(?:${prefix}((?:${pattern})(?:${split}(?:${pattern}))*)${suffix})${mod}`;
}
return `(?:${prefix}(${pattern})${suffix})${modifier}`;
}
if (!isEndDelimited) {
route += "(?=".concat(delimiterRe, "|").concat(endsWithRe, ")");
}
}
return new RegExp(route, flags(options));
return `(?:${prefix}${suffix})${modifier}`;
};
}
exports.tokensToRegexp = tokensToRegexp;
/**

@@ -404,10 +399,9 @@ * Normalize the given path string, returning a regular expression.

*/
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);
function pathToRegexp(path, options = {}) {
const data = path instanceof TokenData ? path : parse(path, options);
const keys = [];
const regexp = tokensToRegexp(data, keys, options);
return Object.assign(regexp, { keys });
}
exports.pathToRegexp = pathToRegexp;
//# sourceMappingURL=index.js.map
{
"name": "path-to-regexp",
"version": "6.2.2",
"version": "7.0.0",
"description": "Express style path to RegExp utility",

@@ -16,8 +16,6 @@ "keywords": [

"license": "MIT",
"sideEffects": false,
"exports": "./dist/index.js",
"main": "dist/index.js",
"module": "dist.es2015/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist.es2015/",
"dist/"

@@ -40,6 +38,8 @@ ],

"@vitest/coverage-v8": "^1.4.0",
"semver": "^7.3.5",
"size-limit": "^11.1.2",
"typescript": "^5.1.6"
},
"engines": {
"node": ">=16"
},
"publishConfig": {

@@ -50,4 +50,4 @@ "access": "public"

{
"path": "dist.es2015/index.js",
"limit": "2 kB"
"path": "dist/index.js",
"limit": "2.2 kB"
}

@@ -57,10 +57,8 @@ ],

"dist": [
"dist",
"dist.es2015"
"dist"
],
"project": [
"tsconfig.build.json",
"tsconfig.es2015.json"
"tsconfig.build.json"
]
}
}

@@ -19,9 +19,9 @@ # Path-to-RegExp

```javascript
```js
const { pathToRegexp, match, parse, compile } = require("path-to-regexp");
// pathToRegexp(path, keys?, options?)
// match(path)
// parse(path)
// compile(path)
// pathToRegexp(path, options?)
// match(path, options?)
// parse(path, options?)
// compile(path, options?)
```

@@ -31,24 +31,21 @@

The `pathToRegexp` function will return a regular expression object based on the provided `path` argument. It accepts the following arguments:
The `pathToRegexp` function returns a regular expression with `keys` as a property. It accepts the following arguments:
- **path** A string, array of strings, or a regular expression.
- **keys** _(optional)_ An array to populate with keys found in the path.
- **path** A string.
- **options** _(optional)_
- **sensitive** When `true` the regexp will be case sensitive. (default: `false`)
- **strict** When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)
- **end** When `true` the regexp will match to the end of the string. (default: `true`)
- **start** When `true` the regexp will match from the beginning of the string. (default: `true`)
- **delimiter** The default delimiter for segments, e.g. `[^/#?]` for `:named` patterns. (default: `'/#?'`)
- **endsWith** Optional character, or list of characters, to treat as "end" characters.
- **encode** A function to encode strings before inserting into `RegExp`. (default: `x => x`)
- **prefixes** List of characters to automatically consider prefixes when parsing. (default: `./`)
- **sensitive** Regexp will be case sensitive. (default: `false`)
- **trailing** Regexp allows an optional trailing delimiter to match. (default: `true`)
- **end** Match to the end of the string. (default: `true`)
- **start** Match from the beginning of the string. (default: `true`)
- **loose** Allow the delimiter to be repeated an arbitrary number of times. (default: `true`)
- **delimiter** The default delimiter for segments, e.g. `[^/]` for `:named` parameters. (default: `'/'`)
- **encodePath** A function to encode strings before inserting into `RegExp`. (default: `x => x`, recommended: [`encodeurl`](https://github.com/pillarjs/encodeurl))
```javascript
const keys = [];
const regexp = pathToRegexp("/foo/:bar", keys);
// regexp = /^\/foo(?:\/([^\/#\?]+?))[\/#\?]?$/i
// keys = [{ name: 'bar', prefix: '/', suffix: '', pattern: '[^\\/#\\?]+?', modifier: '' }]
```js
const regexp = pathToRegexp("/foo/:bar");
// regexp = /^\/+foo(?:\/+([^\/]+?))(?:\/+)?$/i
// keys = [{ name: 'bar', prefix: '', suffix: '', pattern: '', modifier: '' }]
```
**Please note:** The `RegExp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc). When using paths that contain query strings, you need to escape the question mark (`?`) to ensure it does not flag the parameter as [optional](#optional).
**Please note:** The `RegExp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc).

@@ -59,18 +56,16 @@ ### Parameters

#### Named Parameters
#### Named parameters
Named parameters are defined by prefixing a colon to the parameter name (`:foo`).
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). Parameter names can use any valid unicode identifier characters (similar to JavaScript).
```js
const regexp = pathToRegexp("/:foo/:bar");
// keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
// keys = [{ name: 'foo', ... }, { name: 'bar', ... }]
regexp.exec("/test/route");
//=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
//=> [ '/test/route', 'test', 'route', index: 0 ]
```
**Please note:** Parameter names must use "word characters" (`[A-Za-z0-9_]`).
##### Custom matching parameters
##### 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:

@@ -100,8 +95,20 @@

##### Custom Prefix and Suffix
#### Unnamed parameters
It is possible to define a parameter without a name. The name will be numerically indexed:
```js
const regexp = pathToRegexp("/:foo/(.*)");
// keys = [{ name: 'foo', ... }, { name: '0', ... }]
regexp.exec("/test/route");
//=> [ '/test/route', 'test', 'route', index: 0 ]
```
##### 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}?");
const regexp = pathToRegexp("{/:attr1}?{-:attr2}?{-:attr3}?");

@@ -115,17 +122,5 @@ regexp.exec("/test");

#### 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)?`, `/:foo(test)?`, or `{-:foo(test)}?`).
Modifiers are used after parameters with custom prefixes and suffixes (`{}`).

@@ -137,27 +132,12 @@ ##### Optional

```js
const regexp = pathToRegexp("/:foo/:bar?");
const regexp = pathToRegexp("/:foo{/:bar}?");
// keys = [{ name: 'foo', ... }, { name: 'bar', prefix: '/', modifier: '?' }]
regexp.exec("/test");
//=> [ '/test', 'test', undefined, index: 0, input: '/test', groups: undefined ]
//=> [ '/test', 'test', undefined, index: 0 ]
regexp.exec("/test/route");
//=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
//=> [ '/test/route', 'test', 'route', index: 0 ]
```
**Tip:** The prefix is also optional, escape the prefix `\/` to make it required.
When dealing with query strings, escape the question mark (`?`) so it doesn't mark the parameter as optional. Handling unordered data is outside the scope of this library.
```js
const regexp = pathToRegexp("/search/:tableName\\?useIndex=true&term=amazing");
regexp.exec("/search/people?useIndex=true&term=amazing");
//=> [ '/search/people?useIndex=true&term=amazing', 'people', index: 0, input: '/search/people?useIndex=true&term=amazing', groups: undefined ]
// This library does not handle query strings in different orders
regexp.exec("/search/people?term=amazing&useIndex=true");
//=> null
```
##### Zero or more

@@ -168,10 +148,10 @@

```js
const regexp = pathToRegexp("/:foo*");
const regexp = pathToRegexp("{/:foo}*");
// keys = [{ name: 'foo', prefix: '/', modifier: '*' }]
regexp.exec("/");
//=> [ '/', undefined, index: 0, input: '/', groups: undefined ]
regexp.exec("/foo");
//=> [ '/foo', "foo", index: 0 ]
regexp.exec("/bar/baz");
//=> [ '/bar/baz', 'bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
//=> [ '/bar/baz', 'bar/baz', index: 0 ]
```

@@ -184,3 +164,3 @@

```js
const regexp = pathToRegexp("/:foo+");
const regexp = pathToRegexp("{/:foo}+");
// keys = [{ name: 'foo', prefix: '/', modifier: '+' }]

@@ -192,95 +172,39 @@

regexp.exec("/bar/baz");
//=> [ '/bar/baz','bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
//=> [ '/bar/baz', 'bar/baz', index: 0 ]
```
### Match
#### Wildcard
The `match` function will return a function for transforming paths into parameters:
A wildcard can also be used. It is roughly equivalent to `(.*)`.
```js
// Make sure you consistently `decode` segments.
const fn = match("/user/:id", { decode: decodeURIComponent });
const regexp = pathToRegexp("/*");
// keys = [{ name: '0', pattern: '[^\\/]*', separator: '/', modifier: '*' }]
fn("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } }
fn("/invalid"); //=> false
fn("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } }
```
regexp.exec("/");
//=> [ '/', '', index: 0 ]
The `match` function can be used to custom match named parameters. For example, this can be used to whitelist a small number of valid paths:
```js
const urlMatch = match("/users/:id/:tab(home|photos|bio)", {
decode: decodeURIComponent,
});
urlMatch("/users/1234/photos");
//=> { path: '/users/1234/photos', index: 0, params: { id: '1234', tab: 'photos' } }
urlMatch("/users/1234/bio");
//=> { path: '/users/1234/bio', index: 0, params: { id: '1234', tab: 'bio' } }
urlMatch("/users/1234/otherstuff");
//=> false
regexp.exec("/bar/baz");
//=> [ '/bar/baz', 'bar/baz', index: 0 ]
```
#### Process Pathname
### Match
You should make sure variations of the same path match the expected `path`. Here's one possible solution using `encode`:
The `match` function returns a function for transforming paths into parameters:
```js
const fn = match("/café", { encode: encodeURI });
- **path** A string.
- **options** _(optional)_ The same options as `pathToRegexp`, plus:
- **decode** Function for decoding strings for params, or `false` to disable entirely. (default: `decodeURIComponent`)
fn("/caf%C3%A9"); //=> { path: '/caf%C3%A9', index: 0, params: {} }
```
**Note:** [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) encodes paths, so `/café` would be normalized to `/caf%C3%A9` and match in the above example.
##### Alternative Using Normalize
Sometimes you won't have already normalized paths to use, so you could normalize it yourself before matching:
```js
/**
* 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: string) {
return (
decodeURI(pathname)
// Replaces repeated slashes in the URL.
.replace(/\/+/g, "/")
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
// Note: Missing native IE support, may want to skip this step.
.normalize()
);
}
// Make sure you consistently `decode` segments.
const fn = match("/user/:id", { decode: decodeURIComponent });
// Two possible ways of writing `/café`:
const re = pathToRegexp("/caf\u00E9");
const input = encodeURI("/cafe\u0301");
re.test(input); //=> false
re.test(normalizePathname(input)); //=> true
fn("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } }
fn("/invalid"); //=> false
fn("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } }
```
### Parse
**Note:** Setting `decode: false` disables the "splitting" behavior of repeated parameters, which is useful if you need the exactly matched parameter back.
The `parse` function will return a list of strings and keys from a path string:
```js
const tokens = parse("/route/:foo/(.*)");
console.log(tokens[0]);
//=> "/route"
console.log(tokens[1]);
//=> { name: 'foo', prefix: '/', suffix: '', pattern: '[^\\/#\\?]+?', modifier: '' }
console.log(tokens[2]);
//=> { name: 0, prefix: '/', suffix: '', pattern: '.*', modifier: '' }
```
**Note:** This method only works with strings.
### Compile ("Reverse" Path-To-RegExp)

@@ -290,5 +214,9 @@

- **path** A string.
- **options** _(optional)_ Similar to `pathToRegexp` (`delimiter`, `encodePath`, `sensitive`, and `loose`), plus:
- **validate** When `false` the function can produce an invalid (unmatched) path. (default: `true`)
- **encode** Function for encoding input strings for output into the path, or `false` to disable entirely. (default: `encodeURIComponent`)
```js
// Make sure you encode your path segments consistently.
const toPath = compile("/user/:id", { encode: encodeURIComponent });
const toPath = compile("/user/:id");

@@ -299,12 +227,11 @@ toPath({ id: 123 }); //=> "/user/123"

// Without `encode`, you need to make sure inputs are encoded correctly.
// (Note: You can use `validate: false` to create an invalid paths.)
const toPathRaw = compile("/user/:id", { validate: false });
// When disabling `encode`, you need to make sure inputs are encoded correctly. No arrays are accepted.
const toPathRaw = compile("/user/:id", { encode: false });
toPathRaw({ id: "%3A%2F" }); //=> "/user/%3A%2F"
toPathRaw({ id: ":/" }); //=> "/user/:/"
toPathRaw({ id: ":/" }); //=> "/user/:/", throws when `validate: false` is not set.
const toPathRepeated = compile("/:segment+");
const toPathRepeated = compile("{/:segment}+");
toPathRepeated({ segment: "foo" }); //=> "/foo"
toPathRepeated({ segment: ["foo"] }); //=> "/foo"
toPathRepeated({ segment: ["a", "b", "c"] }); //=> "/a/b/c"

@@ -314,36 +241,50 @@

toPathRegexp({ id: 123 }); //=> "/user/123"
toPathRegexp({ id: "123" }); //=> "/user/123"
```
**Note:** The generated function will throw on invalid input.
## Developers
### Working with Tokens
- If you are rewriting paths with match and compiler, consider using `encode: false` and `decode: false` to keep raw paths passed around.
- To ensure matches work on paths containing characters usually encoded, consider using [encodeurl](https://github.com/pillarjs/encodeurl) for `encodePath`.
- If matches are intended to be exact, you need to set `loose: false`, `trailing: false`, and `sensitive: true`.
Path-To-RegExp exposes the two functions used internally that accept an array of tokens:
### Parse
- `tokensToRegexp(tokens, keys?, options?)` Transform an array of tokens into a matching regular expression.
- `tokensToFunction(tokens)` Transform an array of tokens into a path generator function.
A `parse` function is available and returns `TokenData`, the set of tokens and other metadata parsed from the input string. `TokenData` is can passed directly into `pathToRegexp`, `match`, and `compile`. It accepts only two options, `delimiter` and `encodePath`, which makes those options redundant in the above methods.
#### Token Information
### Token Information
- `name` The name of the token (`string` for named or `number` for unnamed index)
- `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. `?`)
- `name` The name of the token
- `prefix` _(optional)_ The prefix string for the segment (e.g. `"/"`)
- `suffix` _(optional)_ The suffix string for the segment (e.g. `""`)
- `pattern` _(optional)_ The pattern defined to match this token
- `modifier` _(optional)_ The modifier character used for the segment (e.g. `?`)
- `separator` _(optional)_ The string used to separate repeated parameters
## Compatibility with Express <= 4.x
## Errors
Path-To-RegExp breaks compatibility with Express <= `4.x`:
An effort has been made to ensure ambiguous paths from previous releases throw an error. This means you might be seeing an error when things worked before.
- RegExp special characters can only be used in a parameter
- Express.js 4.x supported `RegExp` special characters regardless of position - this is considered a bug
- Parameters have suffixes that augment meaning - `*`, `+` and `?`. E.g. `/:user*`
- No wildcard asterisk (`*`) - use parameters instead (`(.*)` or `:splat*`)
### Unexpected `?`, `*`, or `+`
## Live Demo
In previous major versions `/` and `.` were used as implicit prefixes of parameters. So `/:key?` was implicitly `{/:key}?`. For example:
You can see a live demo of this library in use at [express-route-tester](http://forbeslindesay.github.io/express-route-tester/).
- `/:key?` → `{/:key}?` or `/:key*` → `{/:key}*` or `/:key+` → `{/:key}+`
- `.:key?` → `{.:key}?` or `.:key*` → `{.:key}*` or `.:key+` → `{.:key}+`
- `:key?` → `{:key}?` or `:key*` → `{:key}*` or `:key+` → `{:key}+`
### Unexpected `!`, `@`, `,`, or `;`
These characters have been reserved for future use.
### Express <= 4.x
Path-To-RegExp breaks compatibility with Express <= `4.x` in the following ways:
- The only part of the string that is a regex is within `()`.
- In Express.js 4.x, everything was passed as-is after a simple replacement, so you could write `/[a-z]+` to match `/test`.
- The `?` optional character must be used after `{}`.
- Some characters have new meaning or have been reserved (`{}?*+@!;`).
- The parameter name now supports all unicode identifier characters, previously it was only `[a-z0-9]`.
## License

@@ -350,0 +291,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc