Skip to content

Commit 9a2f6dd

Browse files
authored
Merge pull request #839 from AikidoSec/regexp-escape
Use built-in RegExp.escape if available
2 parents a9c6324 + 902cb27 commit 9a2f6dd

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed
Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,48 @@
11
import * as t from "tap";
22
import { escapeStringRegexp } from "./escapeStringRegexp";
33

4-
t.test("main", async (t) => {
4+
const isUsingBuiltIn = typeof (globalThis as any).RegExp?.escape === "function";
5+
6+
if (!isUsingBuiltIn) {
7+
t.test("main", async (t) => {
8+
t.same(
9+
escapeStringRegexp("\\ ^ $ * + ? . ( ) | { } [ ]"),
10+
"\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]"
11+
);
12+
t.same(escapeStringRegexp("hello world"), "hello world");
13+
});
14+
15+
t.test("escapes `-` in a way compatible with PCRE", async (t) => {
16+
t.same(escapeStringRegexp("foo - bar"), "foo \\x2d bar");
17+
});
18+
19+
t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => {
20+
t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-"));
21+
});
22+
} else {
523
t.same(
624
escapeStringRegexp("\\ ^ $ * + ? . ( ) | { } [ ]"),
7-
"\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]"
25+
"\\\\\\x20\\^\\x20\\$\\x20\\*\\x20\\+\\x20\\?\\x20\\.\\x20\\(\\x20\\)\\x20\\|\\x20\\{\\x20\\}\\x20\\[\\x20\\]"
826
);
9-
});
1027

11-
t.test("escapes `-` in a way compatible with PCRE", async (t) => {
12-
t.same(escapeStringRegexp("foo - bar"), "foo \\x2d bar");
13-
});
28+
t.test("escapes `-` in a way compatible with PCRE", async (t) => {
29+
t.same(escapeStringRegexp("foo - bar"), "\\x66oo\\x20\\x2d\\x20bar");
30+
});
1431

15-
t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => {
16-
t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-"));
32+
t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => {
33+
t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-"));
34+
});
35+
}
36+
37+
t.test("escaped regex matches correctly", async (t) => {
38+
t.ok(new RegExp(escapeStringRegexp("hello world")).test("hello world"));
39+
t.ok(
40+
new RegExp(escapeStringRegexp("hello world \\d")).test("hello world \\d")
41+
);
42+
t.ok(new RegExp(escapeStringRegexp("hello\\sworld")).test("hello\\sworld"));
43+
44+
t.notOk(
45+
new RegExp(escapeStringRegexp("hello world \\d")).test("hello world 1")
46+
);
47+
t.notOk(new RegExp(escapeStringRegexp("hello\\sworld")).test("hello sworld"));
1748
});
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1+
let funcToExport: (string: string) => string;
2+
13
/**
24
* Escape characters with special meaning either inside or outside character sets.
35
*
46
* Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
57
*
68
* Taken from https://github.com/sindresorhus/escape-string-regexp/
79
*/
8-
export function escapeStringRegexp(string: string) {
10+
function escapeStringRegexpFallback(string: string) {
911
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
1012
}
13+
14+
// Use native implementation if available (Node.js 24+)
15+
// A benchmark showed that it is up to 50% faster than the polyfill
16+
// @ts-expect-error Outdated Node.js types
17+
if (typeof RegExp?.escape === "function") {
18+
// @ts-expect-error Outdated Node.js types
19+
funcToExport = RegExp.escape;
20+
} else {
21+
funcToExport = escapeStringRegexpFallback;
22+
}
23+
24+
export { funcToExport as escapeStringRegexp };

0 commit comments

Comments
 (0)