Skip to content

Commit f71baf2

Browse files
committed
## 1.5.0 - February 2021
* Adds capturing groups to replacement functions for invalid patterns.
1 parent 260bf18 commit f71baf2

File tree

7 files changed

+195
-34
lines changed

7 files changed

+195
-34
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# ESLint Plugin Regex Change Log
22

3+
## 1.5.0 - February 2021
4+
5+
* Adds capturing groups to replacement functions for invalid patterns.
6+
37
## 1.4.0 - February 2021
48

59
* Adds replacements with functions for invalid patterns.

README.md

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ __________________
2323
..
2424
"devDependencies": {
2525
"eslint": ">=4.0.0",
26-
"eslint-plugin-regex": "1.4.0",
26+
"eslint-plugin-regex": "1.5.0",
2727
..
2828
```
2929

@@ -215,7 +215,7 @@ It is specified by an `object`, with the following fields:
215215
* An optional `string` used to replace the **invalid** found pattern, or
216216
* An optional `object` that establish how the **invalid** found pattern will be replaced:
217217
* `function`: used to replace the **invalid** found pattern.
218-
* It will receive only 1 parameter with the name `text`.
218+
* It will receive 2 parameters: `text` and `captured`.
219219
* It must return a `string` value, if not, return value will be ignored.
220220
* Its definition must be only the body of the function.
221221
* [More Information](#definition-of-the-function-used-to-replace-invalid-found-pattern).
@@ -245,21 +245,24 @@ It is specified by an `object`, with the following fields:
245245

246246
> [1] In order to fix issues `eslint` must be run with `--fix` option.
247247

248-
##### Definition of the Function used to replace **invalid** found pattern
248+
##### Definition of the Function used to replace the *invalid* found pattern
249249

250250
Definition of the function must be done as a `string` in 1 line, and the following rules apply:
251251

252252
* It must return a `string` value, if not, return value will be ignored, i.e. it will silently fail.
253-
* Its definition must be only the body of the function.
253+
* Its definition must be **only the body of the function**.
254254
* If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
255255

256-
Function will receive only 1 parameter with the name `text`, with the value of the invalid text found.
256+
Function will receive 2 parameters:
257257

258-
e.g.
258+
* `text`: a `string` with the value of the invalid text found.
259+
* `captured`: an `array` of strings with the values of the captured groups for the regex.
259260

260-
`"return text.trim()"` => only the body of the function + return a `string` value
261+
e.g. Using parameter `text`
261262

262-
`.eslintrc.json`:`
263+
`"return text.trim()"` => only the body of the function + returns a `string` value based on `text`
264+
265+
Having the following rule in `.eslintrc.json`:
263266

264267
```json
265268
{
@@ -285,6 +288,49 @@ when linting with fix, the result will be:
285288
const exception = "error19"
286289
```
287290

291+
e.g. Using parameter `captured`
292+
293+
`"return captured[0]"` => only the body of the function + returns a `string` value based on `captured`
294+
295+
Having the following rule in `.eslintrc.json`:
296+
297+
```json
298+
{
299+
"id": "regexIdN",
300+
"regex": "\\serror(\\w*)\\s",
301+
"replacement": {
302+
"function": "return captured[0]"
303+
}
304+
}
305+
```
306+
307+
then, given:
308+
309+
`example.js`
310+
311+
```js
312+
const exception = " error19 "
313+
```
314+
315+
when linting with fix, the result will be:
316+
317+
```js
318+
const exception = "19"
319+
```
320+
321+
###### Debugging of the Replacement Function for *invalid* found pattern
322+
323+
* It is possible to add `console` statements to print some information in the Replacement Function.
324+
325+
```json
326+
{
327+
regex: '\\serror(\\w*)\\s',
328+
replacement: {
329+
function: 'const extract = captured[0]; console.log(extract); return extract'
330+
}
331+
}
332+
```
333+
288334
#### Mixing definitions
289335

290336
It is possible to use both type of definitions, 'Short pattern definition' with 'Detailed pattern definition', in the array of patterns.

docs/rules/invalid-regex-rule.md

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ It is specified by an `object`, with the following fields:
6161
* An optional `string` used to replace the invalid found pattern, or
6262
* An optional `object` that establish how the invalid found pattern will be replaced:
6363
* `function`: used to replace the invalid found pattern.
64-
* It will receive only 1 parameter with the name `text`.
64+
* It will receive 2 parameters: `text` and `captured`.
6565
* It must return a `string` value, if not, return value will be ignored.
6666
* Its definition must be only the body of the function.
6767
* [More Information](#definition-of-the-function-used-to-replace-invalid-found-pattern).
@@ -105,21 +105,24 @@ It is specified by an `object`, with the following fields:
105105
}
106106
```
107107

108-
#### Definition of the Function used to replace **invalid** found pattern
108+
#### Definition of the Function used to replace the invalid found pattern
109109

110110
Definition of the function must be done as a `string` in 1 line, and the following rules apply:
111111

112112
* It must return a `string` value, if not, return value will be ignored, i.e. it will silently fail.
113-
* Its definition must be only the body of the function.
113+
* Its definition must be **only the body of the function**.
114114
* If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
115115

116-
Function will receive only 1 parameter with the name `text`, with the value of the invalid text found.
116+
Function will receive 2 parameters:
117117

118-
e.g.
118+
* `text`: a `string` with the value of the invalid text found.
119+
* `captured`: an `array` of strings with the values of the captured groups for the regex.
119120

120-
`"return text.trim()"` => only the body of the function + return a `string` value
121+
e.g. Using parameter `text`
121122

122-
`.eslintrc.json`:`
123+
`"return text.trim()"` => only the body of the function + returns a `string` value based on `text`
124+
125+
Having the following rule in `.eslintrc.json`:
123126

124127
```json
125128
{
@@ -145,6 +148,49 @@ when linting with fix, the result will be:
145148
const exception = "error19"
146149
```
147150

151+
e.g. Using parameter `captured`
152+
153+
`"return captured[0]"` => only the body of the function + returns a `string` value based on `captured`
154+
155+
Having the following rule in `.eslintrc.json`:
156+
157+
```json
158+
{
159+
"id": "regexIdN",
160+
"regex": "\\serror(\\w*)\\s",
161+
"replacement": {
162+
"function": "return captured[0]"
163+
}
164+
}
165+
```
166+
167+
then, given:
168+
169+
`example.js`
170+
171+
```js
172+
const exception = " error19 "
173+
```
174+
175+
when linting with fix, the result will be:
176+
177+
```js
178+
const exception = "19"
179+
```
180+
181+
##### Debugging of the Replacement Function for invalid found pattern
182+
183+
* It is possible to add `console` statements to print some information in the Replacement Function.
184+
185+
```json
186+
{
187+
regex: '\\serror(\\w*)\\s',
188+
replacement: {
189+
function: 'const extract = captured[0]; console.log(extract); return extract'
190+
}
191+
}
192+
```
193+
148194
### String to Regular expression conversion
149195

150196
Internally, each string from the array will be converted into a Regular Expression with `global` and `multiline` options, e.g.:

lib/rules/invalid-regex-rule.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ function checkRegexInSource(source, regex) {
3636
const foundDetail = regex.exec(source)
3737
return !!foundDetail
3838
? {
39-
pattern: foundDetail[0],
39+
pattern: foundDetail.shift(),
40+
captured: foundDetail,
4041
patternIndex: foundDetail.index,
4142
nextChar: regex.lastIndex
4243
}
@@ -73,7 +74,7 @@ function checkRegex(source, sourceLines, nextLine, nextChar, rootChar, pattern,
7374
pattern,
7475
from => `Invalid regular expression ${from} found`
7576
),
76-
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar, sourceDetail.pattern)
77+
fix: replace(rootChar, sourceDetail)
7778
})
7879
checkRegex(remainingSource, sourceLines, foundDetail.nextLine, foundDetail.nextChar, rootChar + remainingSourceStart,
7980
pattern, replace, report)
@@ -85,7 +86,7 @@ function checkRegex(source, sourceLines, nextLine, nextChar, rootChar, pattern,
8586
pattern,
8687
from => `Invalid regular expression ${from} found`
8788
),
88-
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar, sourceDetail.pattern)
89+
fix: replace(rootChar, sourceDetail)
8990
})
9091
checkRegex(remainingSource, sourceLines, nextLine + 1, nextChar, rootChar + remainingSourceStart, pattern, replace, report)
9192
}
@@ -96,18 +97,16 @@ function checkRegex(source, sourceLines, nextLine, nextChar, rootChar, pattern,
9697
function buildReplacementFunction(replacement) {
9798
if (/\breturn\b/.test(replacement)) {
9899
try {
99-
const replacementFunction = new Function('text', replacement) // eslint-disable-line no-new-func
100-
if(typeof replacementFunction('text') === 'string') {
101-
return text => {
102-
try {
103-
const replacement = replacementFunction(text)
104-
if (typeof replacement === 'string') {
105-
return replacement
106-
}
100+
const replacementFunction = new Function('text', 'captured', replacement) // eslint-disable-line no-new-func
101+
return (text, captured) => {
102+
try {
103+
const replacement = replacementFunction(text, captured)
104+
if (typeof replacement === 'string') {
105+
return replacement
107106
}
108-
catch(e) { }
109-
return text
110107
}
108+
catch(e) {}
109+
return text
111110
}
112111
}
113112
catch(e) {}
@@ -118,13 +117,16 @@ function buildReplacementFunction(replacement) {
118117
function createReplacement(replacement) {
119118
switch (typeof replacement) {
120119
case 'string':
121-
return (from, deltaStart, deltaEnd) => fixer => fixer.replaceTextRange([from + deltaStart, from + deltaEnd], replacement)
120+
return (from, found) => fixer => fixer.replaceTextRange([from + found.patternIndex, from + found.nextChar], replacement)
122121
case 'object':
123122
if (typeof replacement.function === 'string') {
124123
const replacementFunction = buildReplacementFunction(replacement.function)
125124
if (typeof replacementFunction === 'function') {
126-
return (from, deltaStart, deltaEnd, text) =>
127-
fixer => fixer.replaceTextRange([from + deltaStart, from + deltaEnd], replacementFunction(text))
125+
return (from, found) =>
126+
fixer => fixer.replaceTextRange(
127+
[from + found.patternIndex, from + found.nextChar],
128+
replacementFunction(found.pattern, found.captured)
129+
)
128130
}
129131
}
130132
}

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "eslint-plugin-regex",
33
"description": "ESLint rules using Regular Expression",
4-
"version": "1.4.0",
4+
"version": "1.5.0",
55
"license": "MIT",
66
"author": "Gonzalo Müller Bravo",
77
"main": "lib/index.js",

tests/lib/rules/invalid-regex-detailed-rule.e2e-test.js

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,66 @@ const shouldNotReplaceWithInvalidFunctionCase07 = {
402402
output: 'var z = 1\nvar x = "invalid"'
403403
}
404404

405+
const shouldReplaceWithCapturingGroupsWithoutCaptured = {
406+
code: 'var z = 1\nvar x = "invalid"',
407+
filename: 'some.js',
408+
options: [
409+
[{
410+
regex: 'inval(\\w+)',
411+
replacement: 'valid'
412+
}]
413+
],
414+
errors: [{
415+
message: 'Invalid regular expression /inval(\\w+)/gm found',
416+
line: 2,
417+
column: 10
418+
}],
419+
output: 'var z = 1\nvar x = "valid"'
420+
}
421+
422+
const shouldReplaceWithFunctionWithCaptured = {
423+
code: 'var z = 1\nvar x = "invalid"',
424+
filename: 'some.js',
425+
options: [
426+
[{
427+
regex: 'inval(\\w*)',
428+
message: 'don`t use inval',
429+
replacement: {
430+
function: 'return captured[0]'
431+
}
432+
}]
433+
],
434+
errors: [{
435+
message: 'don`t use inval',
436+
line: 2,
437+
column: 10
438+
}],
439+
output: 'var z = 1\nvar x = "id"'
440+
}
441+
442+
const shouldReplaceWithFunctionWithCapturedWithLog = {
443+
code: 'var z = 1\nvar x = "invalid"',
444+
filename: 'some.js',
445+
options: [
446+
[{
447+
regex: 'inval(\\w*)',
448+
message: 'don`t use inval',
449+
replacement: {
450+
/**
451+
* This will generate a output to the console when testing
452+
*/
453+
function: 'console.log(text); console.log(captured[0]); return captured[0]'
454+
}
455+
}]
456+
],
457+
errors: [{
458+
message: 'don`t use inval',
459+
line: 2,
460+
column: 10
461+
}],
462+
output: 'var z = 1\nvar x = "id"'
463+
}
464+
405465
ruleTester.run(
406466
'invalid',
407467
invalidRegexRule, {
@@ -429,7 +489,10 @@ ruleTester.run(
429489
shouldNotReplaceWithInvalidFunctionCase04,
430490
shouldNotReplaceWithInvalidFunctionCase05,
431491
shouldNotReplaceWithInvalidFunctionCase06,
432-
shouldNotReplaceWithInvalidFunctionCase07
492+
shouldNotReplaceWithInvalidFunctionCase07,
493+
shouldReplaceWithCapturingGroupsWithoutCaptured,
494+
shouldReplaceWithFunctionWithCaptured,
495+
shouldReplaceWithFunctionWithCapturedWithLog
433496
]
434497
}
435498
)

0 commit comments

Comments
 (0)