diff --git a/library/helpers/escapeStringRegexp.test.ts b/library/helpers/escapeStringRegexp.test.ts index dd916fcf1..add41e180 100644 --- a/library/helpers/escapeStringRegexp.test.ts +++ b/library/helpers/escapeStringRegexp.test.ts @@ -1,17 +1,48 @@ import * as t from "tap"; import { escapeStringRegexp } from "./escapeStringRegexp"; -t.test("main", async (t) => { +const isUsingBuiltIn = typeof (globalThis as any).RegExp?.escape === "function"; + +if (!isUsingBuiltIn) { + t.test("main", async (t) => { + t.same( + escapeStringRegexp("\\ ^ $ * + ? . ( ) | { } [ ]"), + "\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]" + ); + t.same(escapeStringRegexp("hello world"), "hello world"); + }); + + t.test("escapes `-` in a way compatible with PCRE", async (t) => { + t.same(escapeStringRegexp("foo - bar"), "foo \\x2d bar"); + }); + + t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => { + t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-")); + }); +} else { t.same( escapeStringRegexp("\\ ^ $ * + ? . ( ) | { } [ ]"), - "\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]" + "\\\\\\x20\\^\\x20\\$\\x20\\*\\x20\\+\\x20\\?\\x20\\.\\x20\\(\\x20\\)\\x20\\|\\x20\\{\\x20\\}\\x20\\[\\x20\\]" ); -}); -t.test("escapes `-` in a way compatible with PCRE", async (t) => { - t.same(escapeStringRegexp("foo - bar"), "foo \\x2d bar"); -}); + t.test("escapes `-` in a way compatible with PCRE", async (t) => { + t.same(escapeStringRegexp("foo - bar"), "\\x66oo\\x20\\x2d\\x20bar"); + }); -t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => { - t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-")); + t.test("escapes `-` in a way compatible with the Unicode flag", async (t) => { + t.ok(new RegExp(escapeStringRegexp("-"), "u").test("-")); + }); +} + +t.test("escaped regex matches correctly", async (t) => { + t.ok(new RegExp(escapeStringRegexp("hello world")).test("hello world")); + t.ok( + new RegExp(escapeStringRegexp("hello world \\d")).test("hello world \\d") + ); + t.ok(new RegExp(escapeStringRegexp("hello\\sworld")).test("hello\\sworld")); + + t.notOk( + new RegExp(escapeStringRegexp("hello world \\d")).test("hello world 1") + ); + t.notOk(new RegExp(escapeStringRegexp("hello\\sworld")).test("hello sworld")); }); diff --git a/library/helpers/escapeStringRegexp.ts b/library/helpers/escapeStringRegexp.ts index 2e68ed0cb..2c17da488 100644 --- a/library/helpers/escapeStringRegexp.ts +++ b/library/helpers/escapeStringRegexp.ts @@ -1,3 +1,5 @@ +let funcToExport: (string: string) => string; + /** * Escape characters with special meaning either inside or outside character sets. * @@ -5,6 +7,18 @@ * * Taken from https://github.com/sindresorhus/escape-string-regexp/ */ -export function escapeStringRegexp(string: string) { +function escapeStringRegexpFallback(string: string) { return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); } + +// Use native implementation if available (Node.js 24+) +// A benchmark showed that it is up to 50% faster than the polyfill +// @ts-expect-error Outdated Node.js types +if (typeof RegExp?.escape === "function") { + // @ts-expect-error Outdated Node.js types + funcToExport = RegExp.escape; +} else { + funcToExport = escapeStringRegexpFallback; +} + +export { funcToExport as escapeStringRegexp };