0
# Regular Expression Validation
1
2
The RegExpValidator class provides syntax validation with optional detailed callbacks for each regex component during validation, without generating an AST.
3
4
## Capabilities
5
6
### RegExp Validator Class
7
8
Creates a validator instance with configurable options and optional callbacks for validation events.
9
10
```typescript { .api }
11
/**
12
* Regular expression validator that checks regex syntax compliance
13
*/
14
class RegExpValidator {
15
/**
16
* Initialize the validator with options
17
* @param options - Validator configuration options and callbacks
18
*/
19
constructor(options?: RegExpValidator.Options);
20
}
21
22
interface RegExpValidator.Options {
23
/** The flag to disable Annex B syntax. Default is false */
24
strict?: boolean;
25
/** ECMAScript version. Default is 2022 */
26
ecmaVersion?: EcmaVersion;
27
28
// Validation event callbacks (all optional)
29
onLiteralEnter?(start: number): void;
30
onLiteralLeave?(start: number, end: number): void;
31
onFlags?(start: number, end: number, global: boolean, ignoreCase: boolean, multiline: boolean, unicode: boolean, sticky: boolean, dotAll: boolean, hasIndices: boolean): void;
32
onPatternEnter?(start: number): void;
33
onPatternLeave?(start: number, end: number): void;
34
onDisjunctionEnter?(start: number): void;
35
onDisjunctionLeave?(start: number, end: number): void;
36
onAlternativeEnter?(start: number, index: number): void;
37
onAlternativeLeave?(start: number, end: number, index: number): void;
38
onGroupEnter?(start: number): void;
39
onGroupLeave?(start: number, end: number): void;
40
onCapturingGroupEnter?(start: number, name: string | null): void;
41
onCapturingGroupLeave?(start: number, end: number, name: string | null): void;
42
onQuantifier?(start: number, end: number, min: number, max: number, greedy: boolean): void;
43
onLookaroundAssertionEnter?(start: number, kind: "lookahead" | "lookbehind", negate: boolean): void;
44
onLookaroundAssertionLeave?(start: number, end: number, kind: "lookahead" | "lookbehind", negate: boolean): void;
45
onEdgeAssertion?(start: number, end: number, kind: "start" | "end"): void;
46
onWordBoundaryAssertion?(start: number, end: number, kind: "word", negate: boolean): void;
47
onAnyCharacterSet?(start: number, end: number, kind: "any"): void;
48
onEscapeCharacterSet?(start: number, end: number, kind: "digit" | "space" | "word", negate: boolean): void;
49
onUnicodePropertyCharacterSet?(start: number, end: number, kind: "property", key: string, value: string | null, negate: boolean): void;
50
onCharacter?(start: number, end: number, value: number): void;
51
onBackreference?(start: number, end: number, ref: number | string): void;
52
onCharacterClassEnter?(start: number, negate: boolean): void;
53
onCharacterClassLeave?(start: number, end: number, negate: boolean): void;
54
onCharacterClassRange?(start: number, end: number, min: number, max: number): void;
55
}
56
```
57
58
**Usage Examples:**
59
60
```typescript
61
import { RegExpValidator } from "regexpp";
62
63
// Create basic validator
64
const validator = new RegExpValidator();
65
66
// Create validator with callbacks to analyze regex structure
67
const analyticValidator = new RegExpValidator({
68
ecmaVersion: 2022,
69
onCapturingGroupEnter(start, name) {
70
console.log(`Found capturing group: ${name || 'unnamed'} at position ${start}`);
71
},
72
onQuantifier(start, end, min, max, greedy) {
73
console.log(`Found quantifier {${min},${max}} (greedy: ${greedy}) at ${start}-${end}`);
74
},
75
onUnicodePropertyCharacterSet(start, end, kind, key, value, negate) {
76
console.log(`Unicode property: \\${negate ? 'P' : 'p'}{${key}${value ? '=' + value : ''}} at ${start}-${end}`);
77
}
78
});
79
80
// Create strict validator (disables Annex B features)
81
const strictValidator = new RegExpValidator({
82
strict: true,
83
ecmaVersion: 2022
84
});
85
```
86
87
### Validate Literal
88
89
Validates a complete regular expression literal including pattern and flags.
90
91
```typescript { .api }
92
/**
93
* Validate a regular expression literal
94
* @param source - The source code to validate (e.g., "/abc/g")
95
* @param start - The start index in the source code. Default is 0
96
* @param end - The end index in the source code. Default is source.length
97
* @throws {RegExpSyntaxError} If the regex has invalid syntax
98
*/
99
validateLiteral(source: string, start?: number, end?: number): void;
100
```
101
102
**Usage Examples:**
103
104
```typescript
105
import { RegExpValidator } from "regexpp";
106
107
const validator = new RegExpValidator();
108
109
// Basic validation
110
try {
111
validator.validateLiteral("/[a-z]+/gi");
112
console.log("Valid regex literal");
113
} catch (error) {
114
console.log("Invalid regex:", error.message);
115
}
116
117
// Validate with callbacks
118
const callbackValidator = new RegExpValidator({
119
onFlags(start, end, global, ignoreCase, multiline, unicode, sticky, dotAll, hasIndices) {
120
console.log(`Flags: g=${global}, i=${ignoreCase}, m=${multiline}, u=${unicode}, y=${sticky}, s=${dotAll}, d=${hasIndices}`);
121
}
122
});
123
124
callbackValidator.validateLiteral("/test/gimsuyd");
125
126
// Validate substring
127
const source = "const regex = /\\d{2,4}/g;";
128
try {
129
validator.validateLiteral(source, 14, 24); // Just the "/\\d{2,4}/g" part
130
console.log("Extracted regex is valid");
131
} catch (error) {
132
console.log("Invalid regex syntax");
133
}
134
```
135
136
### Validate Pattern
137
138
Validates just the pattern part of a regular expression (without delimiters and flags).
139
140
```typescript { .api }
141
/**
142
* Validate a regular expression pattern
143
* @param source - The source code to validate (e.g., "abc")
144
* @param start - The start index in the source code. Default is 0
145
* @param end - The end index in the source code. Default is source.length
146
* @param uFlag - The flag to enable Unicode mode
147
* @throws {RegExpSyntaxError} If the pattern has invalid syntax
148
*/
149
validatePattern(source: string, start?: number, end?: number, uFlag?: boolean): void;
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import { RegExpValidator } from "regexpp";
156
157
const validator = new RegExpValidator();
158
159
// Validate simple pattern
160
try {
161
validator.validatePattern("abc");
162
console.log("Valid pattern");
163
} catch (error) {
164
console.log("Invalid pattern:", error.message);
165
}
166
167
// Validate with Unicode flag
168
try {
169
validator.validatePattern("\\p{Letter}+", 0, undefined, true);
170
console.log("Valid Unicode pattern");
171
} catch (error) {
172
console.log("Invalid Unicode pattern");
173
}
174
175
// Validate with callbacks
176
const patternValidator = new RegExpValidator({
177
onCharacterClassEnter(start, negate) {
178
console.log(`Character class ${negate ? 'negated' : 'normal'} at position ${start}`);
179
}
180
});
181
182
patternValidator.validatePattern("[^a-z]+");
183
```
184
185
### Validate Flags
186
187
Validates just the flags part of a regular expression.
188
189
```typescript { .api }
190
/**
191
* Validate regular expression flags
192
* @param source - The source code to validate (e.g., "gim")
193
* @param start - The start index in the source code. Default is 0
194
* @param end - The end index in the source code. Default is source.length
195
* @throws {RegExpSyntaxError} If the flags are invalid
196
*/
197
validateFlags(source: string, start?: number, end?: number): void;
198
```
199
200
**Usage Examples:**
201
202
```typescript
203
import { RegExpValidator } from "regexpp";
204
205
const validator = new RegExpValidator();
206
207
// Validate flags
208
try {
209
validator.validateFlags("gim");
210
console.log("Valid flags");
211
} catch (error) {
212
console.log("Invalid flags:", error.message);
213
}
214
215
// Invalid flags will throw
216
try {
217
validator.validateFlags("gix"); // 'x' is not a valid flag
218
} catch (error) {
219
console.log("Error:", error.message);
220
}
221
222
// Duplicate flags will throw
223
try {
224
validator.validateFlags("gg"); // Duplicate 'g' flag
225
} catch (error) {
226
console.log("Error:", error.message);
227
}
228
```
229
230
## Advanced Validation with Callbacks
231
232
The validator's callback system allows detailed analysis during validation:
233
234
```typescript
235
import { RegExpValidator } from "regexpp";
236
237
// Comprehensive analysis validator
238
const analyzer = new RegExpValidator({
239
ecmaVersion: 2022,
240
241
onPatternEnter(start) {
242
console.log(`Starting pattern analysis at position ${start}`);
243
},
244
245
onCapturingGroupEnter(start, name) {
246
if (name) {
247
console.log(`Named capture group '${name}' at ${start}`);
248
} else {
249
console.log(`Unnamed capture group at ${start}`);
250
}
251
},
252
253
onQuantifier(start, end, min, max, greedy) {
254
const type = min === max ? 'exact' : min === 0 && max === Infinity ? 'zero-or-more' : 'range';
255
console.log(`Quantifier: ${type} {${min},${max === Infinity ? '∞' : max}} ${greedy ? 'greedy' : 'lazy'}`);
256
},
257
258
onBackreference(start, end, ref) {
259
console.log(`Backreference to ${typeof ref === 'string' ? `'${ref}'` : `group ${ref}`} at ${start}-${end}`);
260
}
261
});
262
263
// Analyze a complex regex
264
analyzer.validateLiteral("/(?<year>\\d{4})-(?<month>\\d{2})\\k<year>/g");
265
```
266
267
## Error Handling
268
269
All validation methods may throw RegExpSyntaxError for invalid syntax:
270
271
```typescript
272
import { RegExpValidator } from "regexpp";
273
274
const validator = new RegExpValidator();
275
276
try {
277
validator.validateLiteral("/[z-a]/"); // Invalid character class range
278
} catch (error) {
279
console.log(error.name); // "RegExpSyntaxError"
280
console.log(error.message); // Detailed error message
281
console.log(error.index); // Position where error occurred
282
}
283
284
// Version-specific validation
285
const es2017Validator = new RegExpValidator({ ecmaVersion: 2017 });
286
287
try {
288
es2017Validator.validatePattern("(?<=\\w)\\d+"); // Lookbehind requires ES2018+
289
} catch (error) {
290
console.log("Feature not supported in ES2017");
291
}
292
```
293
294
## Types
295
296
```typescript { .api }
297
type EcmaVersion = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022;
298
299
// Callback function types
300
type ValidationCallback = (start: number, end: number, ...args: any[]) => void;
301
302
// Error type thrown by validation methods
303
class RegExpSyntaxError extends SyntaxError {
304
index: number;
305
constructor(source: string, uFlag: boolean, index: number, message: string);
306
}
307
```