Skip to content

Commit 66e0c25

Browse files
committed
Pull capturing group logic into balanced utility
1 parent 943c907 commit 66e0c25

File tree

3 files changed

+42
-43
lines changed

3 files changed

+42
-43
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"size-limit": [
3535
{
3636
"path": "dist/index.js",
37-
"limit": "1.6 kB"
37+
"limit": "1.7 kB"
3838
}
3939
],
4040
"jest": {

src/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2554,7 +2554,7 @@ const TESTS: Test[] = [
25542554
],
25552555

25562556
/**
2557-
* Nested parenthesis.
2557+
* Nested parentheses.
25582558
*/
25592559
[
25602560
"/:test(\\d+(?:\\.\\d+)?)",

src/index.ts

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,35 @@ export interface ParseOptions {
1414
whitelist?: string | string[];
1515
}
1616

17+
/**
18+
* Balanced bracket helper function.
19+
*/
20+
function balanced(open: string, close: string, str: string, index: number) {
21+
let count = 0;
22+
let i = index;
23+
24+
while (i < str.length) {
25+
if (str[i] === "\\") {
26+
i += 2;
27+
continue;
28+
}
29+
30+
if (str[i] === close) {
31+
count--;
32+
33+
if (count === 0) return i + 1;
34+
}
35+
36+
if (str[i] === open) {
37+
count++;
38+
}
39+
40+
i++;
41+
}
42+
43+
return -1;
44+
}
45+
1746
/**
1847
* Parse a string for the raw tokens.
1948
*/
@@ -66,54 +95,24 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
6695
}
6796

6897
if (str[i] === "(") {
69-
const prev = i;
70-
let balanced = 1;
71-
let invalidGroup = false;
72-
73-
if (str[i + 1] === "?") {
74-
throw new TypeError("Path pattern must be a capturing group");
75-
}
76-
77-
while (++i < str.length) {
78-
if (str[i] === "\\") {
79-
pattern += str.substr(i, 2);
80-
i++;
81-
continue;
82-
}
98+
const end = balanced("(", ")", str, i);
8399

84-
if (str[i] === ")") {
85-
balanced--;
100+
if (end > -1) {
101+
pattern = str.slice(i + 1, end - 1);
102+
i = end;
86103

87-
if (balanced === 0) {
88-
i++;
89-
break;
90-
}
104+
if (pattern[0] === "?") {
105+
throw new TypeError("Path pattern must be a capturing group");
91106
}
92107

93-
pattern += str[i];
108+
if (/\((?=[^?])/.test(pattern)) {
109+
const validPattern = pattern.replace(/\((?=[^?])/, "(?:");
94110

95-
if (str[i] === "(") {
96-
balanced++;
97-
98-
// Better errors on nested capturing groups.
99-
if (str[i + 1] !== "?") {
100-
pattern += "?:";
101-
invalidGroup = true;
102-
}
111+
throw new TypeError(
112+
`Capturing groups are not allowed in pattern, use a non-capturing group: (${validPattern})`
113+
);
103114
}
104115
}
105-
106-
if (invalidGroup) {
107-
throw new TypeError(
108-
`Capturing groups are not allowed in pattern, use a non-capturing group: (${pattern})`
109-
);
110-
}
111-
112-
// False positive.
113-
if (balanced > 0) {
114-
i = prev;
115-
pattern = "";
116-
}
117116
}
118117

119118
// Add regular characters to the path string.

0 commit comments

Comments
 (0)