Skip to content

Commit 260bf18

Browse files
committed
## 1.4.0 - February 2021
* Adds replacements with functions for invalid patterns.
1 parent d320e11 commit 260bf18

File tree

11 files changed

+390
-33
lines changed

11 files changed

+390
-33
lines changed

.eslintrc.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
}
2424
],
2525
"array-bracket-spacing": "off",
26-
"indent": ["error", 2],
2726
"quotes": [
2827
"error",
2928
"single"

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.4.0 - February 2021
4+
5+
* Adds replacements with functions for invalid patterns.
6+
37
## 1.3.0 - January 2021
48

59
* Adds replacements for invalid patterns.

README.md

Lines changed: 58 additions & 3 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.2.1",
26+
"eslint-plugin-regex": "1.4.0",
2727
..
2828
```
2929

@@ -211,7 +211,14 @@ It is specified by just a regular expression `string`, i.e. `"regex"`
211211
It is specified by an `object`, with the following fields:
212212

213213
* `regex`: A **required** `string` representing the **Regular expression to look for**.
214-
* `replacement`: If present this `string` will use to replace **invalid** found pattern.
214+
* `replacement` [1]:
215+
* An optional `string` used to replace the **invalid** found pattern, or
216+
* An optional `object` that establish how the **invalid** found pattern will be replaced:
217+
* `function`: used to replace the **invalid** found pattern.
218+
* It will receive only 1 parameter with the name `text`.
219+
* It must return a `string` value, if not, return value will be ignored.
220+
* Its definition must be only the body of the function.
221+
* [More Information](#definition-of-the-function-used-to-replace-invalid-found-pattern).
215222
* `id`: An optional `string` representing the **Pattern Id**.
216223
* `message`: An optional `string` specifying the **Message to be shown when an error happens** (invalid `regex` is found or required `regex` is not found).
217224
* `files`: An optional `object` specifying which files to analyze:
@@ -236,6 +243,48 @@ It is specified by an `object`, with the following fields:
236243
> * When `ignore` and `inspect` are present, `ignore` takes precedence.
237244
> * Global ignore file pattern, takes precedence over `files` patterns.
238245

246+
> [1] In order to fix issues `eslint` must be run with `--fix` option.
247+
248+
##### Definition of the Function used to replace **invalid** found pattern
249+
250+
Definition of the function must be done as a `string` in 1 line, and the following rules apply:
251+
252+
* 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.
254+
* If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
255+
256+
Function will receive only 1 parameter with the name `text`, with the value of the invalid text found.
257+
258+
e.g.
259+
260+
`"return text.trim()"` => only the body of the function + return a `string` value
261+
262+
`.eslintrc.json`:`
263+
264+
```json
265+
{
266+
"id": "regexIdN",
267+
"regex": "\\serror\\w*\\s",
268+
"replacement": {
269+
"function": "return text.trim()"
270+
}
271+
}
272+
```
273+
274+
then, given:
275+
276+
`example.js`
277+
278+
```js
279+
const exception = " error19 "
280+
```
281+
282+
when linting with fix, the result will be:
283+
284+
```js
285+
const exception = "error19"
286+
```
287+
239288
#### Mixing definitions
240289

241290
It is possible to use both type of definitions, 'Short pattern definition' with 'Detailed pattern definition', in the array of patterns.
@@ -335,7 +384,13 @@ Array of patterns represent different logical operation for each rule:
335384

336385
### Examples
337386

338-
[For a set of Regex Rules examples check `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
387+
Check:
388+
389+
* [invalid-regex Basic rule tests](tests/lib/rules/invalid-regex-rule.e2e-test.js)
390+
* [invalid-regex Detailed rule tests](tests/lib/rules/invalid-regex-detailed-rule.e2e-test.js)
391+
* [required-regex Basic rule tests](tests/lib/rules/required-regex-rule.e2e-test.js)
392+
* [required-regex Detailed rule tests](tests/lib/rules/required-regex-detailed-rule.e2e-test.js)
393+
* [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
339394

340395
__________________
341396

docs/rules/invalid-regex-rule.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,14 @@ It is specified by just a regular expression `string`, i.e. `"regex"`
5757
It is specified by an `object`, with the following fields:
5858

5959
* `regex`: A **required** `string` representing the **Regular expression to look for**.
60-
* `replacement`: An optional `string` used to replace invalid found pattern.
60+
* `replacement` [1]:
61+
* An optional `string` used to replace the invalid found pattern, or
62+
* An optional `object` that establish how the invalid found pattern will be replaced:
63+
* `function`: used to replace the invalid found pattern.
64+
* It will receive only 1 parameter with the name `text`.
65+
* It must return a `string` value, if not, return value will be ignored.
66+
* Its definition must be only the body of the function.
67+
* [More Information](#definition-of-the-function-used-to-replace-invalid-found-pattern).
6168
* `id`: An optional `string` representing the **Pattern Id**.
6269
* `message`: An optional `string` specifying the **Message to be shown when an invalid `regex` is found**.
6370
* `files`: An optional `object` specifying which files to analyze:
@@ -70,6 +77,8 @@ It is specified by an `object`, with the following fields:
7077
> * When `ignore` and `inspect` are present, `ignore` takes precedence.
7178
> * Global ignore file pattern, takes precedence over `files` patterns.
7279
80+
> [1] In order to fix issue eslint must be run with `--fix` option.
81+
7382
`.eslintrc.json`:
7483

7584
```json
@@ -96,6 +105,46 @@ It is specified by an `object`, with the following fields:
96105
}
97106
```
98107

108+
#### Definition of the Function used to replace **invalid** found pattern
109+
110+
Definition of the function must be done as a `string` in 1 line, and the following rules apply:
111+
112+
* 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.
114+
* If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
115+
116+
Function will receive only 1 parameter with the name `text`, with the value of the invalid text found.
117+
118+
e.g.
119+
120+
`"return text.trim()"` => only the body of the function + return a `string` value
121+
122+
`.eslintrc.json`:`
123+
124+
```json
125+
{
126+
"id": "regexIdN",
127+
"regex": "\\serror\\w*\\s",
128+
"replacement": {
129+
"function": "return text.trim()"
130+
}
131+
}
132+
```
133+
134+
then, given:
135+
136+
`example.js`
137+
138+
```js
139+
const exception = " error19 "
140+
```
141+
142+
when linting with fix, the result will be:
143+
144+
```js
145+
const exception = "error19"
146+
```
147+
99148
### String to Regular expression conversion
100149

101150
Internally, each string from the array will be converted into a Regular Expression with `global` and `multiline` options, e.g.:
@@ -108,6 +157,14 @@ When the pattern is found, the error message will reflect the exact location, e.
108157
34:25 error Invalid regular expression /invalidRegex1/gm found regex/invalid
109158
```
110159

160+
### Examples
161+
162+
Check:
163+
164+
* [invalid-regex Basic rule tests](tests/lib/rules/invalid-regex-rule.e2e-test.js)
165+
* [invalid-regex Detailed rule tests](tests/lib/rules/invalid-regex-detailed-rule.e2e-test.js)
166+
* [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
167+
111168
## Related Rules
112169

113170
* [`regex/required`](docs/rules/required-regex-rule.md).

docs/rules/required-regex-rule.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ Internally, each string from the array will be converted into a Regular Expressi
105105

106106
`"requiredRegex1"` will be transformed into `/requiredRegex1/gm`
107107

108+
### Examples
109+
110+
Check:
111+
112+
* [required-regex Basic rule tests](tests/lib/rules/required-regex-rule.e2e-test.js)
113+
* [required-regex Detailed rule tests](tests/lib/rules/required-regex-detailed-rule.e2e-test.js)
114+
* [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
115+
108116
## Related Rules
109117

110118
* [`regex/invalid`](docs/rules/invalid-regex-rule.md).

lib/rules/invalid-regex-rule.js

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ function checkRegexInSource(source, regex) {
3636
const foundDetail = regex.exec(source)
3737
return !!foundDetail
3838
? {
39-
patternIndex: foundDetail.index,
40-
nextChar: regex.lastIndex
41-
}
39+
pattern: foundDetail[0],
40+
patternIndex: foundDetail.index,
41+
nextChar: regex.lastIndex
42+
}
4243
: {
43-
patternIndex: -1,
44-
nextChar: -1
45-
}
44+
patternIndex: -1,
45+
nextChar: -1
46+
}
4647
}
4748

4849
function foundStart(source, nextLine, foundAt) {
@@ -72,7 +73,7 @@ function checkRegex(source, sourceLines, nextLine, nextChar, rootChar, pattern,
7273
pattern,
7374
from => `Invalid regular expression ${from} found`
7475
),
75-
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar)
76+
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar, sourceDetail.pattern)
7677
})
7778
checkRegex(remainingSource, sourceLines, foundDetail.nextLine, foundDetail.nextChar, rootChar + remainingSourceStart,
7879
pattern, replace, report)
@@ -84,14 +85,52 @@ function checkRegex(source, sourceLines, nextLine, nextChar, rootChar, pattern,
8485
pattern,
8586
from => `Invalid regular expression ${from} found`
8687
),
87-
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar)
88+
fix: replace(rootChar, sourceDetail.patternIndex, sourceDetail.nextChar, sourceDetail.pattern)
8889
})
8990
checkRegex(remainingSource, sourceLines, nextLine + 1, nextChar, rootChar + remainingSourceStart, pattern, replace, report)
9091
}
9192
}
9293
}
9394
}
9495

96+
function buildReplacementFunction(replacement) {
97+
if (/\breturn\b/.test(replacement)) {
98+
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+
}
107+
}
108+
catch(e) { }
109+
return text
110+
}
111+
}
112+
}
113+
catch(e) {}
114+
}
115+
return null
116+
}
117+
118+
function createReplacement(replacement) {
119+
switch (typeof replacement) {
120+
case 'string':
121+
return (from, deltaStart, deltaEnd) => fixer => fixer.replaceTextRange([from + deltaStart, from + deltaEnd], replacement)
122+
case 'object':
123+
if (typeof replacement.function === 'string') {
124+
const replacementFunction = buildReplacementFunction(replacement.function)
125+
if (typeof replacementFunction === 'function') {
126+
return (from, deltaStart, deltaEnd, text) =>
127+
fixer => fixer.replaceTextRange([from + deltaStart, from + deltaEnd], replacementFunction(text))
128+
}
129+
}
130+
}
131+
return () => undefined
132+
}
133+
95134
function checkPatterns(fileName, source, patterns, report) {
96135
const sourceLines = source.split('\n')
97136
patterns.forEach(pattern => shouldCheck(pattern.details, fileName) && checkRegex(
@@ -101,9 +140,7 @@ function checkPatterns(fileName, source, patterns, report) {
101140
0,
102141
0,
103142
pattern,
104-
typeof pattern.details.replacement === 'string'
105-
? (from, deltaStart, deltaEnd) => fixer => fixer.replaceTextRange([from + deltaStart, from + deltaEnd], pattern.details.replacement)
106-
: () => undefined,
143+
createReplacement(pattern.details.replacement),
107144
report
108145
))
109146
}
@@ -144,10 +181,26 @@ module.exports = {
144181
type: 'string',
145182
minLength: 1
146183
},
147-
replacement: {
148-
title: 'Replacement',
149-
description: 'Replacement for invalid pattern',
150-
type: 'string'
184+
replacement: {
185+
oneOf:[{
186+
title: 'Replacement',
187+
description: 'Replacement for invalid pattern',
188+
type: 'string'
189+
}, {
190+
title: 'Detailed replacement',
191+
description: 'Detailed replacements for invalid patterns',
192+
type: 'object',
193+
properties: {
194+
function: {
195+
title: 'Replacement function',
196+
description: 'Function used to replace the found pattern. It receives the found text and must return the replacement text',
197+
type: 'string',
198+
minLength: 1
199+
}
200+
},
201+
minProperties: 1,
202+
maxProperties: 1
203+
}]
151204
},
152205
message: {
153206
title: 'Invalid message',

lib/utils/options-utils.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ function fitDetails(details) {
1616
function extractPattern(regexSource) {
1717
return typeof regexSource !== 'string'
1818
? {
19-
source: regexSource.regex,
20-
regex: new RegExp(regexSource.regex, 'gm'),
21-
details: fitDetails(regexSource)
22-
}
19+
source: regexSource.regex,
20+
regex: new RegExp(regexSource.regex, 'gm'),
21+
details: fitDetails(regexSource)
22+
}
2323
: {
24-
source: regexSource,
25-
regex: new RegExp(regexSource, 'gm'),
26-
details: fitDetails({})
27-
}
24+
source: regexSource,
25+
regex: new RegExp(regexSource, 'gm'),
26+
details: fitDetails({})
27+
}
2828
}
2929

3030
module.exports.fromOptions = function (options) {

lib/utils/report-utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ module.exports.formatReportMessage = function (pattern, createMsg) {
55
return !!pattern.details.message
66
? pattern.details.message
77
: !!pattern.details.id
8-
? createMsg(`'${pattern.details.id}'`)
9-
: createMsg(`/${pattern.source}/gm`)
8+
? createMsg(`'${pattern.details.id}'`)
9+
: createMsg(`/${pattern.source}/gm`)
1010
}

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.3.0",
4+
"version": "1.4.0",
55
"license": "MIT",
66
"author": "Gonzalo Müller Bravo",
77
"main": "lib/index.js",

0 commit comments

Comments
 (0)