Skip to content

Commit 57f7265

Browse files
authoredApr 3, 2024··
feat: named capture groups and reference (#66)
1 parent 65022ee commit 57f7265

21 files changed

+386
-62
lines changed
 

‎.github/ISSUE_TEMPLATE/bug_report.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
---
22
name: Bug report
33
about: Create a report to help us improve
4-
title: "[Bug]"
4+
title: '[Bug]'
55
labels: ''
66
assignees: ''
7-
87
---
98

109
**Describe the bug**
1110
A clear and concise description of what the bug is.
1211

1312
**To Reproduce**
1413
Steps to reproduce the behavior:
14+
1515
1.
1616
2.
1717
3.

‎.github/ISSUE_TEMPLATE/feature_request.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
---
22
name: Feature request
33
about: Suggest an idea for this project
4-
title: "[Feature]"
4+
title: '[Feature]'
55
labels: enhancement
66
assignees: ''
7-
87
---
98

109
**Is your feature request related to a problem? Please describe.**
@@ -17,10 +16,11 @@ A clear and concise description of what you want to happen.
1716
A clear and concise description of any alternative solutions or features you've considered.
1817

1918
**Checklist**
19+
2020
- [ ] Implementation
2121
- [ ] Tests
22-
- [ ] API docs
23-
- [ ] README docs (if relevant)
22+
- [ ] API docs
23+
- [ ] README docs (if relevant)
2424
- [ ] Example docs & tests (if relevant)
2525

2626
**Additional context**

‎.watchmanconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{}
1+
{}

‎.yarnrc.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ nmHoistingLimits: workspaces
33

44
plugins:
55
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
6-
spec: "@yarnpkg/plugin-interactive-tools"
6+
spec: '@yarnpkg/plugin-interactive-tools'
77
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
8-
spec: "@yarnpkg/plugin-workspace-tools"
8+
spec: '@yarnpkg/plugin-workspace-tools'
99

1010
yarnPath: .yarn/releases/yarn-3.6.1.cjs

‎CODE_OF_CONDUCT.md

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Contributor Covenant Code of Conduct
32

43
## Our Pledge
@@ -18,23 +17,23 @@ diverse, inclusive, and healthy community.
1817
Examples of behavior that contributes to a positive environment for our
1918
community include:
2019

21-
* Demonstrating empathy and kindness toward other people
22-
* Being respectful of differing opinions, viewpoints, and experiences
23-
* Giving and gracefully accepting constructive feedback
24-
* Accepting responsibility and apologizing to those affected by our mistakes,
20+
- Demonstrating empathy and kindness toward other people
21+
- Being respectful of differing opinions, viewpoints, and experiences
22+
- Giving and gracefully accepting constructive feedback
23+
- Accepting responsibility and apologizing to those affected by our mistakes,
2524
and learning from the experience
26-
* Focusing on what is best not just for us as individuals, but for the overall
25+
- Focusing on what is best not just for us as individuals, but for the overall
2726
community
2827

2928
Examples of unacceptable behavior include:
3029

31-
* The use of sexualized language or imagery, and sexual attention or advances of
30+
- The use of sexualized language or imagery, and sexual attention or advances of
3231
any kind
33-
* Trolling, insulting or derogatory comments, and personal or political attacks
34-
* Public or private harassment
35-
* Publishing others' private information, such as a physical or email address,
32+
- Trolling, insulting or derogatory comments, and personal or political attacks
33+
- Public or private harassment
34+
- Publishing others' private information, such as a physical or email address,
3635
without their explicit permission
37-
* Other conduct which could reasonably be considered inappropriate in a
36+
- Other conduct which could reasonably be considered inappropriate in a
3837
professional setting
3938

4039
## Enforcement Responsibilities

‎README.md

-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ See [Character Classes API doc](https://callstack.github.io/ts-regex-builder/api
152152
| `lookbehind(...)` | `(?<=...)` | Match preceding text without consuming it |
153153
| `negativeLookbehind(...)` | `(?<!...)` | Reject preceding text without consuming it |
154154

155-
156155
See [Assertions API doc](https://callstack.github.io/ts-regex-builder/api/assertions) for more info.
157156

158157
## Examples

‎jest-setup.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import './test-utils/to-equal-regex';
22
import './test-utils/to-match-groups';
33
import './test-utils/to-match-all-groups';
4+
import './test-utils/to-match-named-groups';
5+
import './test-utils/to-match-all-named-groups';
46
import './test-utils/to-match-string';

‎lefthook.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ pre-commit:
22
parallel: true
33
commands:
44
lint:
5-
glob: "*.{js,ts,jsx,tsx}"
5+
glob: '*.{js,ts,jsx,tsx}'
66
run: npx eslint {staged_files}
77
types:
8-
glob: "*.{js,ts, jsx, tsx}"
8+
glob: '*.{js,ts, jsx, tsx}'
99
run: npx tsc --noEmit
1010
commit-msg:
1111
parallel: true

‎src/__tests__/example-html-tags.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
any,
3+
buildRegExp,
4+
capture,
5+
charClass,
6+
charRange,
7+
digit,
8+
oneOrMore,
9+
ref,
10+
zeroOrMore,
11+
} from '..';
12+
13+
test('example: html tag matching', () => {
14+
const tagName = oneOrMore(charClass(charRange('a', 'z'), digit));
15+
const tagContent = zeroOrMore(any, { greedy: false });
16+
17+
const tagMatcher = buildRegExp(
18+
[
19+
'<',
20+
capture(tagName, { name: 'tag' }),
21+
'>',
22+
capture(tagContent, { name: 'content' }),
23+
'</',
24+
ref('tag'),
25+
'>',
26+
],
27+
{ ignoreCase: true, global: true },
28+
);
29+
30+
expect(tagMatcher).toMatchAllNamedGroups('<a>abc</a>', [{ tag: 'a', content: 'abc' }]);
31+
expect(tagMatcher).toMatchAllNamedGroups('<a><b>abc</b></a>', [
32+
{ tag: 'a', content: '<b>abc</b>' },
33+
]);
34+
expect(tagMatcher).toMatchAllNamedGroups('<a>abc1</a><b>abc2</b>', [
35+
{ tag: 'a', content: 'abc1' },
36+
{ tag: 'b', content: 'abc2' },
37+
]);
38+
39+
expect(tagMatcher).not.toMatchString('<a>abc</b>');
40+
41+
expect(tagMatcher).toEqualRegex('<(?<tag>[a-z\\d]+)>(?<content>.*?)<\\/\\k<tag>>');
42+
});

‎src/constructs/__tests__/capture.test.tsx

+109-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { capture, oneOrMore } from '../..';
1+
import {
2+
any,
3+
anyOf,
4+
buildRegExp,
5+
capture,
6+
digit,
7+
negated,
8+
oneOrMore,
9+
ref,
10+
word,
11+
wordBoundary,
12+
} from '../..';
213

314
test('`capture` pattern', () => {
415
expect(capture('a')).toEqualRegex(/(a)/);
@@ -12,3 +23,100 @@ test('`capture` matching', () => {
1223
expect(['a', capture('b')]).toMatchGroups('ab', ['ab', 'b']);
1324
expect(['a', capture('b'), capture('c')]).toMatchGroups('abc', ['abc', 'b', 'c']);
1425
});
26+
27+
test('named `capture` pattern', () => {
28+
expect(capture('a', { name: 'xyz' })).toEqualRegex('(?<xyz>a)');
29+
expect(capture('abc', { name: 'xyz' })).toEqualRegex('(?<xyz>abc)');
30+
expect(capture(oneOrMore('abc'), { name: 'xyz' })).toEqualRegex('(?<xyz>(?:abc)+)');
31+
expect(oneOrMore(capture('abc', { name: 'xyz' }))).toEqualRegex('(?<xyz>abc)+');
32+
});
33+
34+
test('named `capture` matching', () => {
35+
expect(capture('b', { name: 'x1' })).toMatchGroups('ab', ['b', 'b']);
36+
expect(capture('b', { name: 'x1' })).toMatchNamedGroups('ab', { x1: 'b' });
37+
38+
expect(['a', capture('b', { name: 'x1' })]).toMatchGroups('ab', ['ab', 'b']);
39+
expect(['a', capture('b', { name: 'x1' })]).toMatchNamedGroups('ab', { x1: 'b' });
40+
41+
expect([capture('a'), capture('b', { name: 'x1' }), capture('c', { name: 'x2' })]).toMatchGroups(
42+
'abc',
43+
['abc', 'a', 'b', 'c'],
44+
);
45+
expect([
46+
capture('a'),
47+
capture('b', { name: 'x1' }),
48+
capture('c', { name: 'x2' }),
49+
]).toMatchNamedGroups('abc', { x1: 'b', x2: 'c' });
50+
});
51+
52+
test('`reference` pattern', () => {
53+
expect([ref('ref0')]).toEqualRegex(/\k<ref0>/);
54+
expect([ref('xyz')]).toEqualRegex(/\k<xyz>/);
55+
expect([capture(any, { name: 'ref0' }), ' ', ref('ref0')]).toEqualRegex('(?<ref0>.) \\k<ref0>');
56+
57+
expect(['xx', capture(any, { name: 'r123' }), ' ', ref('r123'), 'xx']).toEqualRegex(
58+
'xx(?<r123>.) \\k<r123>xx',
59+
);
60+
});
61+
62+
test('`reference` matching basic case', () => {
63+
expect([capture(word, { name: 'a' }), ref('a')]).toMatchString('aa');
64+
expect([capture(digit, { name: 'a' }), ref('a')]).toMatchString('11');
65+
66+
expect([capture(any, { name: 'a' }), ref('a')]).not.toMatchString('ab');
67+
expect([capture(digit, { name: 'a' }), ref('a')]).not.toMatchString('1a');
68+
expect([capture(digit, { name: 'a' }), ref('a')]).not.toMatchString('a1');
69+
});
70+
71+
test('`reference` matching variable case', () => {
72+
const someRef = ref('test');
73+
expect([capture(word, { name: someRef.name }), someRef]).toMatchString('aa');
74+
expect([capture(digit, { name: someRef.name }), someRef]).toMatchString('11');
75+
76+
expect([capture(any, { name: someRef.name }), someRef]).not.toMatchString('ab');
77+
expect([capture(digit, { name: someRef.name }), someRef]).not.toMatchString('1a');
78+
expect([capture(digit, { name: someRef.name }), someRef]).not.toMatchString('a1');
79+
});
80+
81+
test('`reference` matching HTML attributes', () => {
82+
const quoteChars = anyOf('"\'');
83+
const htmlAttributeRegex = buildRegExp([
84+
wordBoundary,
85+
capture(oneOrMore(word), { name: 'name' }),
86+
'=',
87+
capture(quoteChars, { name: 'quote' }),
88+
capture(oneOrMore(negated(quoteChars)), { name: 'value' }),
89+
ref('quote'),
90+
]);
91+
92+
expect(htmlAttributeRegex).toMatchNamedGroups('a="b"', {
93+
name: 'a',
94+
quote: '"',
95+
value: 'b',
96+
});
97+
expect(htmlAttributeRegex).toMatchNamedGroups('aa="bbb"', {
98+
name: 'aa',
99+
quote: '"',
100+
value: 'bbb',
101+
});
102+
expect(htmlAttributeRegex).toMatchNamedGroups(`aa='bbb'`, {
103+
name: 'aa',
104+
quote: `'`,
105+
value: 'bbb',
106+
});
107+
expect(htmlAttributeRegex).toMatchNamedGroups('<input type="number" />', {
108+
quote: '"',
109+
name: 'type',
110+
value: 'number',
111+
});
112+
expect(htmlAttributeRegex).toMatchNamedGroups(`<input type='number' />`, {
113+
quote: "'",
114+
name: 'type',
115+
value: 'number',
116+
});
117+
118+
expect(htmlAttributeRegex).not.toMatchString(`aa="bbb'`);
119+
expect(htmlAttributeRegex).not.toMatchString(`aa='bbb"`);
120+
expect(htmlAttributeRegex).not.toMatchString(`<input type='number" />`);
121+
expect(htmlAttributeRegex).not.toMatchString(`<input type="number' />`);
122+
});

‎src/constructs/capture.ts

+52-1
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,70 @@ import type { RegexConstruct, RegexElement, RegexSequence } from '../types';
66
export interface Capture extends RegexConstruct {
77
type: 'capture';
88
children: RegexElement[];
9+
options?: CaptureOptions;
910
}
1011

11-
export function capture(sequence: RegexSequence): Capture {
12+
export type CaptureOptions = {
13+
/**
14+
* Name to be given to the capturing group.
15+
*/
16+
name?: string;
17+
};
18+
19+
export interface Reference extends RegexConstruct {
20+
type: 'reference';
21+
name: string;
22+
}
23+
24+
/**
25+
* Creates a capturing group which allows the matched pattern to be available:
26+
* - in the match results (`String.match`, `String.matchAll`, or `RegExp.exec`)
27+
* - in the regex itself, through {@link ref}
28+
*/
29+
export function capture(sequence: RegexSequence, options?: CaptureOptions): Capture {
1230
return {
1331
type: 'capture',
1432
children: ensureArray(sequence),
33+
options,
1534
encode: encodeCapture,
1635
};
1736
}
1837

38+
/**
39+
* Creates a reference, also known as backreference, which allows matching
40+
* again the exact text that a capturing group previously matched.
41+
*
42+
* In order to form a valid regex, the reference must use the same name as
43+
* a capturing group earlier in the expression.
44+
*
45+
* @param name - Name of the capturing group to reference.
46+
*/
47+
export function ref(name: string): Reference {
48+
return {
49+
type: 'reference',
50+
name,
51+
encode: encodeReference,
52+
};
53+
}
54+
1955
function encodeCapture(this: Capture): EncodeResult {
56+
const name = this.options?.name;
57+
if (name) {
58+
return {
59+
precedence: 'atom',
60+
pattern: `(?<${name}>${encodeSequence(this.children).pattern})`,
61+
};
62+
}
63+
2064
return {
2165
precedence: 'atom',
2266
pattern: `(${encodeSequence(this.children).pattern})`,
2367
};
2468
}
69+
70+
function encodeReference(this: Reference): EncodeResult {
71+
return {
72+
precedence: 'atom',
73+
pattern: `\\k<${this.name}>`,
74+
};
75+
}

‎src/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1+
// Types
12
export type * from './types';
3+
export type { CaptureOptions } from './constructs/capture';
4+
export type { QuantifierOptions } from './constructs/quantifiers';
5+
export type { RepeatOptions } from './constructs/repeat';
26

7+
// Builders
38
export { buildPattern, buildRegExp } from './builders';
49

10+
// Constructs
511
export {
612
endOfString,
713
nonWordBoundary,
814
notWordBoundary,
915
startOfString,
1016
wordBoundary,
1117
} from './constructs/anchors';
12-
export { capture } from './constructs/capture';
18+
export { capture, ref } from './constructs/capture';
1319
export {
1420
any,
1521
anyOf,

‎test-utils/to-equal-regex.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ import { wrapRegExp } from './utils';
44
export function toEqualRegex(
55
this: jest.MatcherContext,
66
received: RegExp | RegexSequence,
7-
expected: RegExp,
7+
expected: RegExp | string,
88
) {
99
received = wrapRegExp(received);
1010

1111
const options = {
1212
isNot: this.isNot,
1313
};
1414

15+
const expectedSource = typeof expected === 'string' ? expected : expected.source;
16+
const expectedFlags = typeof expected === 'string' ? undefined : expected.flags;
17+
1518
return {
16-
pass: expected.source === received.source && expected.flags === received.flags,
19+
pass:
20+
expectedSource === received.source &&
21+
(expectedFlags === undefined || expectedFlags === received.flags),
1722
message: () =>
18-
this.utils.matcherHint('toHavePattern', undefined, undefined, options) +
23+
this.utils.matcherHint('toEqualRegex', undefined, undefined, options) +
1924
'\n\n' +
2025
`Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expected)}\n` +
2126
`Received: ${this.utils.printReceived(received)}`,
@@ -28,7 +33,7 @@ declare global {
2833
namespace jest {
2934
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3035
interface Matchers<R, T = {}> {
31-
toEqualRegex(expected: RegExp): R;
36+
toEqualRegex(expected: RegExp | string): R;
3237
}
3338
}
3439
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { RegexSequence } from '../src/types';
2+
import { wrapRegExp } from './utils';
3+
4+
export function toMatchAllNamedGroups(
5+
this: jest.MatcherContext,
6+
received: RegExp | RegexSequence,
7+
inputText: string,
8+
expectedGroups: Array<Record<string, string>>,
9+
) {
10+
const receivedRegex = wrapRegExp(received);
11+
const matchResult = inputText.matchAll(receivedRegex);
12+
const receivedGroups = matchResult ? [...matchResult].map((r) => r.groups) : null;
13+
const options = {
14+
isNot: this.isNot,
15+
};
16+
17+
return {
18+
pass: this.equals(receivedGroups, expectedGroups),
19+
message: () =>
20+
this.utils.matcherHint('toMatchGroups', undefined, undefined, options) +
21+
'\n\n' +
22+
`Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expectedGroups)}\n` +
23+
`Received: ${this.utils.printReceived(receivedGroups)}`,
24+
};
25+
}
26+
27+
expect.extend({ toMatchAllNamedGroups });
28+
29+
declare global {
30+
namespace jest {
31+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
32+
interface Matchers<R, T = {}> {
33+
toMatchAllNamedGroups(inputText: string, expectedGroups: Array<Record<string, string>>): R;
34+
}
35+
}
36+
}

‎test-utils/to-match-groups.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { wrapRegExp } from './utils';
44
export function toMatchGroups(
55
this: jest.MatcherContext,
66
received: RegExp | RegexSequence,
7-
expectedString: string,
7+
inputText: string,
88
expectedGroups: string[],
99
) {
1010
const receivedRegex = wrapRegExp(received);
11-
const matchResult = expectedString.match(receivedRegex);
11+
const matchResult = inputText.match(receivedRegex);
1212
const receivedGroups = matchResult ? [...matchResult] : null;
1313
const options = {
1414
isNot: this.isNot,
@@ -30,7 +30,7 @@ declare global {
3030
namespace jest {
3131
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3232
interface Matchers<R, T = {}> {
33-
toMatchGroups(input: string, expected: string[]): R;
33+
toMatchGroups(inputText: string, expectedGroups: string[]): R;
3434
}
3535
}
3636
}

‎test-utils/to-match-named-groups.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { RegexSequence } from '../src/types';
2+
import { wrapRegExp } from './utils';
3+
4+
export function toMatchNamedGroups(
5+
this: jest.MatcherContext,
6+
received: RegExp | RegexSequence,
7+
inputText: string,
8+
expectedGroups: Record<string, string>,
9+
) {
10+
const receivedRegex = wrapRegExp(received);
11+
const matchResult = inputText.match(receivedRegex);
12+
const receivedGroups = matchResult ? matchResult.groups : null;
13+
const options = {
14+
isNot: this.isNot,
15+
};
16+
17+
return {
18+
pass: this.equals(receivedGroups, expectedGroups),
19+
message: () =>
20+
this.utils.matcherHint('toMatchGroups', undefined, undefined, options) +
21+
'\n\n' +
22+
`Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expectedGroups)}\n` +
23+
`Received: ${this.utils.printReceived(receivedGroups)}`,
24+
};
25+
}
26+
27+
expect.extend({ toMatchNamedGroups });
28+
29+
declare global {
30+
namespace jest {
31+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
32+
interface Matchers<R, T = {}> {
33+
toMatchNamedGroups(inputText: string, expectedGroups: Record<string, string>): R;
34+
}
35+
}
36+
}

‎website/docs/api/captures.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
id: captures
3+
title: Captures
4+
---
5+
6+
### `capture()`
7+
8+
```ts
9+
function capture(
10+
sequence: RegexSequence,
11+
options?: {
12+
name?: string;
13+
},
14+
): Capture;
15+
```
16+
17+
Regex syntax:
18+
19+
- `(...)` for capturing groups (no `name` option)
20+
- `(?<name>...)` for named capturing groups (`name` option)
21+
22+
Captures, also known as capturing groups, extract and store parts of the matched string for later use.
23+
24+
Capture results are available using array-like [`match()` result object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#using_match).
25+
26+
#### Named groups
27+
28+
When using `name` options, the group becomes a [named capturing group](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) allowing to refer to it using name instead of index.
29+
30+
Named capture results are available using [`groups`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#using_named_capturing_groups) property on `match()` result.
31+
32+
:::note
33+
34+
TS Regex Builder does not have a construct for non-capturing groups. Such groups are implicitly added when required. E.g., `zeroOrMore("abc")` is encoded as `(?:abc)+`.
35+
36+
:::
37+
38+
### `ref()`
39+
40+
```ts
41+
function ref(name: string): Reference;
42+
```
43+
44+
Regex syntax: `\k<...>`.
45+
46+
Creates a reference, also known as a backreference, which allows matching again the exact text that a capturing group previously matched. The reference must use the same name as some capturing group earlier in the expression to form a valid regex.
47+
48+
Usage with `capture()`:
49+
50+
```ts
51+
const regex = buildRegExp([
52+
// Create a named capture using name from `someKey`.
53+
capture(..., { name: 'someKey' }),
54+
// ... some other elements ...
55+
56+
// Match the same text as matched by `capture` with the same name.
57+
ref('someKey'),
58+
])
59+
```
60+
61+
:::note
62+
63+
TS Regex Builder doesn't support using ordinal backreferences (`\1`, `\2`, etc) because in complex regex patterns, these references are difficult to accurately use.
64+
65+
:::

‎website/docs/api/constructs.md

+1-21
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,10 @@ The `choiceOf` (disjunction) construct matches one out of several possible seque
1919
2020
Example: `choiceOf("color", "colour")` matches either `color` or `colour` pattern.
2121
22-
### `capture()`
23-
24-
```ts
25-
function capture(
26-
sequence: RegexSequence,
27-
): Capture;
28-
```
29-
30-
Regex syntax: `(...)`.
31-
32-
Captures, also known as capturing groups, extract and store parts of the matched string for later use.
33-
34-
:::note
35-
36-
TS Regex Builder does not have a construct for non-capturing groups. Such groups are implicitly added when required. E.g., `zeroOrMore("abc")` is encoded as `(?:abc)+`.
37-
38-
:::
39-
4022
### `regex()`
4123
4224
```ts
43-
function regex(
44-
sequence: RegexSequence,
45-
): Regex;
25+
function regex(sequence: RegexSequence): Regex;
4626
```
4727
4828
Regex syntax: the pattern remains unchanged when wrapped by this construct.

‎website/docs/api/overview.md

-1
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,4 @@ See [Character Classes](./api/character-classes) for more info.
100100
| `lookbehind(...)` | `(?<=...)` | Match preceding text without consuming it |
101101
| `negativeLookbehind(...)` | `(?<!...)` | Reject preceding text without consuming it |
102102

103-
104103
See [Assertions](./api/assertions) for more info.

‎website/docs/api/types.md

+2-7
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,15 @@ title: Types
66
### `RegexSequence`
77

88
```ts
9-
type RegexSequence =
10-
| RegexElement[]
11-
| RegexElement;
9+
type RegexSequence = RegexElement[] | RegexElement;
1210
```
1311

1412
The sequence of regex elements forming a regular expression. For developer convenience, it also accepts a single element instead of an array.
1513

1614
### `RegexElement`
1715

1816
```ts
19-
type RegexElement =
20-
| RegexConstruct
21-
| RegExp
22-
| string;
17+
type RegexElement = RegexConstruct | RegExp | string;
2318
```
2419

2520
Regex elements are fundamental building blocks of a regular expression. These can be either further regex constructs, regular strings to be matched literally or `RegExp` literals (`/.../`) for including simple regexes as part of a larger structure.

‎website/sidebars.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default {
2929
'api/types',
3030
'api/builder',
3131
'api/constructs',
32+
'api/captures',
3233
'api/quantifiers',
3334
'api/character-classes',
3435
'api/assertions',

0 commit comments

Comments
 (0)
Please sign in to comment.