Skip to content

Commit 26551ec

Browse files
committed
Code gen smaller and faster regexps
1 parent 0d20b15 commit 26551ec

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

src/index.ts

+44-18
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
297297
if (asterisk) {
298298
tokens.push({
299299
name: String(key++),
300-
pattern: `${negate(escape(delimiter))}*`,
300+
pattern: `${negate(delimiter)}*`,
301301
modifier: "*",
302302
separator: delimiter,
303303
});
@@ -590,7 +590,6 @@ function tokensToRegexp(data: TokenData, options: PathOptions) {
590590
* Convert a token into a regexp string (re-used for path validation).
591591
*/
592592
function toRegExpSource(data: TokenData, keys: Key[]): string[] {
593-
const delim = escape(data.delimiter);
594593
const sources = Array(data.tokens.length);
595594
let backtrack = "";
596595

@@ -600,7 +599,8 @@ function toRegExpSource(data: TokenData, keys: Key[]): string[] {
600599
const token = data.tokens[i];
601600

602601
if (typeof token === "string") {
603-
sources[i] = backtrack = escape(token);
602+
backtrack = token;
603+
sources[i] = escape(token);
604604
continue;
605605
}
606606

@@ -615,39 +615,65 @@ function toRegExpSource(data: TokenData, keys: Key[]): string[] {
615615
const post = escape(suffix);
616616

617617
if (token.name) {
618-
let pattern = token.pattern || "";
619-
618+
backtrack = suffix || backtrack;
620619
keys.unshift(token);
621620

622621
if (isRepeat(token)) {
623-
const mod = modifier === "*" ? "?" : "";
624-
const sep = escape(separator);
625-
626-
if (!sep) {
622+
if (!separator) {
627623
throw new TypeError(
628624
`Missing separator for "${token.name}": ${DEBUG_URL}`,
629625
);
630626
}
631627

632-
pattern ||= `${negate(delim, sep, post || backtrack)}+`;
633-
sources[i] =
634-
`(?:${pre}((?:${pattern})(?:${sep}(?:${pattern}))*)${post})${mod}`;
628+
const mod = modifier === "*" ? "?" : "";
629+
const sep = escape(separator);
630+
const pattern =
631+
token.pattern || `${negate(data.delimiter, separator, backtrack)}+`;
632+
633+
sources[i] = wrap(
634+
pre,
635+
`(?:${pattern})(?:${sep}(?:${pattern}))*`,
636+
post,
637+
mod,
638+
);
635639
} else {
636-
pattern ||= `${negate(delim, post || backtrack)}+`;
637-
sources[i] = `(?:${pre}(${pattern})${post})${modifier}`;
640+
sources[i] = wrap(
641+
pre,
642+
token.pattern || `${negate(data.delimiter, backtrack)}+`,
643+
post,
644+
modifier,
645+
);
638646
}
639647

640-
backtrack = pre || pattern;
648+
backtrack = prefix;
641649
} else {
642650
sources[i] = `(?:${pre}${post})${modifier}`;
643-
backtrack = `${pre}${post}`;
651+
backtrack = `${prefix}${suffix}`;
644652
}
645653
}
646654

647655
return sources;
648656
}
649657

650658
function negate(...args: string[]) {
651-
const values = Array.from(new Set(args)).filter(Boolean);
652-
return `(?:(?!${values.join("|")}).)`;
659+
const values = args.sort().filter((value, index, array) => {
660+
for (let i = 0; i < index; i++) {
661+
const v = array[i];
662+
if (v.length && value.startsWith(v)) return false;
663+
}
664+
return value.length > 0;
665+
});
666+
667+
const isSimple = values.every((value) => value.length === 1);
668+
if (isSimple) return `[^${escape(values.join(""))}]`;
669+
670+
return `(?:(?!${values.map(escape).join("|")}).)`;
671+
}
672+
673+
function wrap(pre: string, pattern: string, post: string, modifier: string) {
674+
if (pre || post) {
675+
return `(?:${pre}(${pattern})${post})${modifier}`;
676+
}
677+
678+
return `(${pattern})${modifier}`;
653679
}

0 commit comments

Comments
 (0)