std/regexp/escape.ts

98 lines
2.4 KiB
TypeScript
Raw Normal View History

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
// For future forward-compatibility with regexp `v` flag, `RESERVED_CHARS` is
// autogenerated from the `ClassSetReservedDoublePunctuator`,
// `ClassSetSyntaxCharacter`, and `ClassSetReservedPunctuator` categories in the
// {@link https://github.com/tc39/proposal-regexp-v-flag#how-is-the-v-flag-different-from-the-u-flag | draft spec}.
// ```ts
// const reservedChars = [
// ...new Set(
// [
// "ClassSetReservedDoublePunctuator",
// "ClassSetSyntaxCharacter",
// "ClassSetReservedPunctuator",
// ].map((n) =>
// document.querySelector(`[name=${n}] emu-rhs`).textContent.replaceAll(
// /\s/g,
// "",
// )
// ).join(""),
// ),
// ];
// const RESERVED_CHARS = Object.fromEntries(reservedChars
// .map((x) => {
// try {
// for (const flag of "gimsuy") {
// new RegExp(`\\${x}`, flag);
// new RegExp(`[\\${x}]`, flag);
// }
// return [x, `\\${x}`];
// } catch (e) {
// return [x, `\\x${x.codePointAt(0).toString(16).padStart(2, "0")}`];
// }
// }));
// ```
const RESERVED_CHARS = {
"&": "\\x26",
"!": "\\x21",
"#": "\\x23",
$: "\\$",
"%": "\\x25",
"*": "\\*",
"+": "\\+",
",": "\\x2c",
".": "\\.",
":": "\\x3a",
";": "\\x3b",
"<": "\\x3c",
"=": "\\x3d",
">": "\\x3e",
"?": "\\?",
"@": "\\x40",
"^": "\\^",
"`": "\\x60",
"~": "\\x7e",
"(": "\\(",
")": "\\)",
"[": "\\[",
"]": "\\]",
"{": "\\{",
"}": "\\}",
"/": "\\/",
"-": "\\x2d",
"\\": "\\\\",
"|": "\\|",
} as const;
const RX_REGEXP_ESCAPE = new RegExp(
`[${Object.values(RESERVED_CHARS).join("")}]`,
"gu",
);
/**
* Escapes arbitrary text for interpolation into a regexp, such that it will
* match exactly that text and nothing else.
*
* @example Usage
* ```ts
* import { escape } from "@std/regexp";
* import { assertEquals, assertMatch, assertNotMatch } from "@std/assert";
*
* const re = new RegExp(`^${escape(".")}$`, "u");
*
* assertEquals("^\\.$", re.source);
* assertMatch(".", re);
* assertNotMatch("a", re);
* ```
*
* @param str The string to escape.
* @returns The escaped string.
*/
export function escape(str: string): string {
return str.replaceAll(
RX_REGEXP_ESCAPE,
(m) => RESERVED_CHARS[m as keyof typeof RESERVED_CHARS],
);
}