Skip to content

Commit a2bc6cd

Browse files
feat: support core regex flags (#26)
1 parent 27f68ed commit a2bc6cd

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

src/__tests__/builder.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { buildRegex } from '../builders';
2+
3+
test('"regexBuilder" flags', () => {
4+
expect(buildRegex('a').flags).toBe('');
5+
expect(buildRegex({}, 'a').flags).toBe('');
6+
7+
expect(buildRegex({ global: true }, 'a').flags).toBe('g');
8+
expect(buildRegex({ global: false }, 'a').flags).toBe('');
9+
10+
expect(buildRegex({ ignoreCase: true }, 'a').flags).toBe('i');
11+
expect(buildRegex({ ignoreCase: false }, 'a').flags).toBe('');
12+
13+
expect(buildRegex({ multiline: true }, 'a').flags).toBe('m');
14+
expect(buildRegex({ multiline: false }, 'a').flags).toBe('');
15+
16+
expect(buildRegex({ hasIndices: true }, 'a').flags).toBe('d');
17+
expect(buildRegex({ hasIndices: false }, 'a').flags).toBe('');
18+
19+
expect(buildRegex({ sticky: true }, 'a').flags).toBe('y');
20+
expect(buildRegex({ sticky: false }, 'a').flags).toBe('');
21+
22+
expect(
23+
buildRegex({ global: true, ignoreCase: true, multiline: false }, 'a').flags
24+
).toBe('gi');
25+
});

src/builders.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,42 @@
11
import type { RegexElement } from './components/types';
22
import { encodeSequence } from './encoder/encoder';
3+
import { isRegexElement } from './utils';
4+
5+
export interface RegexFlags {
6+
/** Global search. */
7+
global?: boolean;
8+
/** Case-insensitive search. */
9+
ignoreCase?: boolean;
10+
/** Allows ^ and $ to match newline characters. */
11+
multiline?: boolean;
12+
/** Generate indices for substring matches. */
13+
hasIndices?: boolean;
14+
/** Perform a "sticky" search that matches starting at the current position in the target string. */
15+
sticky?: boolean;
16+
}
317

418
/**
519
* Generate RegExp object for elements.
620
*
721
* @param elements
822
* @returns
923
*/
10-
export function buildRegex(...elements: Array<RegexElement | string>): RegExp {
11-
const pattern = encodeSequence(elements).pattern;
12-
return new RegExp(pattern);
24+
export function buildRegex(...elements: Array<RegexElement | string>): RegExp;
25+
export function buildRegex(
26+
flags: RegexFlags,
27+
...elements: Array<RegexElement | string>
28+
): RegExp;
29+
export function buildRegex(
30+
first: RegexFlags | RegexElement | string,
31+
...rest: Array<RegexElement | string>
32+
): RegExp {
33+
if (typeof first === 'string' || isRegexElement(first)) {
34+
return buildRegex({}, first, ...rest);
35+
}
36+
37+
const pattern = encodeSequence(rest).pattern;
38+
const flags = encodeFlags(first);
39+
return new RegExp(pattern, flags);
1340
}
1441

1542
/**
@@ -22,3 +49,24 @@ export function buildPattern(
2249
): string {
2350
return encodeSequence(elements).pattern;
2451
}
52+
53+
function encodeFlags(flags: RegexFlags): string {
54+
let result = '';
55+
if (flags.global) {
56+
result += 'g';
57+
}
58+
if (flags.ignoreCase) {
59+
result += 'i';
60+
}
61+
if (flags.multiline) {
62+
result += 'm';
63+
}
64+
if (flags.hasIndices) {
65+
result += 'd';
66+
}
67+
if (flags.sticky) {
68+
result += 'y';
69+
}
70+
71+
return result;
72+
}

src/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { RegexElement } from './components/types';
12
import { type EncoderNode, EncoderPrecedence } from './encoder/types';
23

34
/**
@@ -29,6 +30,10 @@ export function concatNodes(nodes: EncoderNode[]): EncoderNode {
2930
};
3031
}
3132

33+
export function isRegexElement(element: unknown): element is RegexElement {
34+
return typeof element === 'object' && element !== null && 'type' in element;
35+
}
36+
3237
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
3338
export function escapeText(text: string) {
3439
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string

0 commit comments

Comments
 (0)