Skip to content

Commit

Permalink
Support array inputs for match
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Sep 10, 2024
1 parent c36bdfa commit 3fdd88f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 23 deletions.
33 changes: 31 additions & 2 deletions src/cases.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface ParserTestSet {
}

export interface CompileTestSet {
path: string;
path: Path;
options?: CompileOptions;
tests: Array<{
input: ParamData | undefined;
Expand All @@ -24,7 +24,7 @@ export interface CompileTestSet {
}

export interface MatchTestSet {
path: Path;
path: Path | Path[];
options?: MatchOptions;
tests: Array<{
input: string;
Expand Down Expand Up @@ -3191,4 +3191,33 @@ export const MATCH_TESTS: MatchTestSet[] = [
},
],
},

/**
* Array input.
*/
{
path: ["/a", "/b"],
tests: [
{
input: "/a",
matches: ["/a"],
expected: { path: "/a", index: 0, params: {} },
},
{
input: "/b",
matches: ["/b"],
expected: { path: "/b", index: 0, params: {} },
},
{
input: "/c",
matches: null,
expected: false,
},
{
input: "/a/b",
matches: null,
expected: false,
},
],
},
];
53 changes: 32 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,16 +475,18 @@ export type MatchFunction<P extends ParamData> = (path: string) => Match<P>;
* Create path match function from `path-to-regexp` spec.
*/
export function match<P extends ParamData>(
path: Path,
path: Path | Path[],
options: MatchOptions = {},
): MatchFunction<P> {
const { decode = decodeURIComponent, loose = true } = options;
const data = path instanceof TokenData ? path : parse(path, options);
const stringify = toStringify(loose, data.delimiter);
const keys: Key[] = [];
const re = tokensToRegexp(data, keys, options);
const {
decode = decodeURIComponent,
loose = true,
delimiter = DEFAULT_DELIMITER,
} = options;
const re = pathToRegexp(path, options);
const stringify = toStringify(loose, delimiter);

const decoders = keys.map((key) => {
const decoders = re.keys.map((key) => {
if (decode && (key.modifier === "+" || key.modifier === "*")) {
const { prefix = "", suffix = "", separator = suffix + prefix } = key;
const re = new RegExp(stringify(separator), "g");
Expand All @@ -504,7 +506,7 @@ export function match<P extends ParamData>(
for (let i = 1; i < m.length; i++) {
if (m[i] === undefined) continue;

const key = keys[i - 1];
const key = re.keys[i - 1];
const decoder = decoders[i - 1];
params[key.name] = decoder(m[i]);
}
Expand All @@ -517,7 +519,7 @@ export function match<P extends ParamData>(
* Escape a regular expression string.
*/
function escape(str: string) {
return str.replace(/([.+*?^${}()[\]|/\\])/g, "\\$1");
return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&");
}

/**
Expand Down Expand Up @@ -565,27 +567,27 @@ export type Token = string | Key;
/**
* Expose a function for taking tokens and returning a RegExp.
*/
function tokensToRegexp(
data: TokenData,
function pathToSource(
path: Path,
keys: Key[],
flags: string,
options: PathToRegexpOptions,
): RegExp {
): string {
const data = path instanceof TokenData ? path : parse(path, options);
const {
trailing = true,
loose = true,
start = true,
end = true,
strict = false,
} = options;
const flags = toFlags(options);
const stringify = toStringify(loose, data.delimiter);
const sources = toRegExpSource(data, stringify, keys, flags, strict);
let pattern = start ? "^" : "";
pattern += sources.join("");
if (trailing) pattern += `(?:${stringify(data.delimiter)})?`;
if (trailing) pattern += `(?:${stringify(data.delimiter)}$)?`;
pattern += end ? "$" : `(?=${escape(data.delimiter)}|$)`;

return new RegExp(pattern, flags);
return pattern;
}

/**
Expand All @@ -602,7 +604,7 @@ function toRegExpSource(
let backtrack = "";
let safe = true;

return data.tokens.map((token, index) => {
return data.tokens.map((token) => {
if (typeof token === "string") {
backtrack = token;
return stringify(token);
Expand Down Expand Up @@ -685,9 +687,18 @@ export type Path = string | TokenData;
* placeholder key descriptions. For example, using `/user/:id`, `keys` will
* contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
*/
export function pathToRegexp(path: Path, options: PathToRegexpOptions = {}) {
const data = path instanceof TokenData ? path : parse(path, options);
export function pathToRegexp(
path: Path | Path[],
options: PathToRegexpOptions = {},
) {
const keys: Key[] = [];
const regexp = tokensToRegexp(data, keys, options);
return Object.assign(regexp, { keys });
const flags = toFlags(options);

if (Array.isArray(path)) {
const regexps = path.map((p) => pathToSource(p, keys, flags, options));
return Object.assign(new RegExp(regexps.join("|")), { keys });
}

const regexp = pathToSource(path, keys, flags, options);
return Object.assign(new RegExp(regexp), { keys });
}

0 comments on commit 3fdd88f

Please sign in to comment.