Skip to content

Commit 0211e2c

Browse files
feat: escape special characters (#20)
1 parent b40c925 commit 0211e2c

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ buck-out/
3232
lib/
3333

3434
/.idea
35+
coverage/

src/__tests__/compiler.test.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { buildPattern, buildRegex } from '../compiler';
2-
import { oneOrMore, optionally, one, zeroOrMore } from '../quantifiers/base';
2+
import { one, oneOrMore, optionally, zeroOrMore } from '../quantifiers/base';
33
import { repeat } from '../quantifiers/repeat';
44

55
test('basic quantifies', () => {
@@ -28,3 +28,31 @@ test('regex constructor', () => {
2828
expect(buildRegex('a').test('a')).toBeTruthy();
2929
expect(buildRegex('a').test('b')).toBeFalsy();
3030
});
31+
32+
test('"buildPattern" escapes special characters', () => {
33+
expect(buildPattern('.')).toBe('\\.');
34+
expect(buildPattern('*')).toBe('\\*');
35+
expect(buildPattern('+')).toBe('\\+');
36+
expect(buildPattern('?')).toBe('\\?');
37+
expect(buildPattern('^')).toBe('\\^');
38+
expect(buildPattern('$')).toBe('\\$');
39+
expect(buildPattern('{')).toBe('\\{');
40+
expect(buildPattern('}')).toBe('\\}');
41+
expect(buildPattern('|')).toBe('\\|');
42+
expect(buildPattern('[')).toBe('\\[');
43+
expect(buildPattern(']')).toBe('\\]');
44+
expect(buildPattern('\\')).toBe('\\\\');
45+
46+
expect(buildPattern('*.*')).toBe('\\*\\.\\*');
47+
48+
expect(buildPattern(oneOrMore('.*'), zeroOrMore('[]{}'))).toBe(
49+
'(?:\\.\\*)+(?:\\[\\]\\{\\})*'
50+
);
51+
});
52+
53+
test('buildRegex throws error on unknown element', () => {
54+
expect(() =>
55+
// @ts-expect-error intentionally passing incorrect object
56+
buildRegex({ type: 'unknown' })
57+
).toThrowErrorMatchingInlineSnapshot(`"Unknown elements type unknown"`);
58+
});

src/compiler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { RegexElement } from './types';
22
import { characterClasses, isCharacterClass } from './character-classes';
33
import { baseQuantifiers, isBaseQuantifier } from './quantifiers/base';
44
import { compileRepeat } from './quantifiers/repeat';
5+
import { escapeText } from './utils';
56

67
/**
78
* Generate RegExp object for elements.
@@ -31,20 +32,20 @@ function compileList(elements: RegexElement[]): string {
3132

3233
function compileSingle(element: RegexElement): string {
3334
if (typeof element === 'string') {
34-
return element;
35+
return escapeText(element);
3536
}
3637

3738
if (isCharacterClass(element)) {
3839
return characterClasses[element.type];
3940
}
4041

41-
const compiledChildren = compileList(element.children);
42-
4342
if (element.type === 'repeat') {
43+
const compiledChildren = compileList(element.children);
4444
return compileRepeat(element.config, compiledChildren);
4545
}
4646

4747
if (isBaseQuantifier(element)) {
48+
const compiledChildren = compileList(element.children);
4849
const compiler = baseQuantifiers[element.type];
4950
return compiler(compiledChildren);
5051
}

src/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
export function wrapGroup(regex: string): string {
88
return regex.length === 1 ? regex : `(?:${regex})`;
99
}
10+
11+
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
12+
export function escapeText(text: string) {
13+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
14+
}

0 commit comments

Comments
 (0)