1 . Add dependencies:
package.json
:
"engines" : {
"node" : ">=6.0.0"
},
"devDependencies": {
"eslint": ">=4.0.0",
"eslint-plugin-regex": "1.10.0",
2 . Configure eslint:
Short configuration:
.eslintrc.json
:
{
"plugins": [
"regex"
],
"rules": {
"regex/invalid": [
"error", [
"invalidRegex1",
"invalidRegexN"
]
],
"regex/required": [
"error", [
"requiredRegex1",
"requiredRegexN"
],
"ignoreFilesRegex"
]
}
}
Files will be checked for the absence of invalidRegex1
and invalidRegexN
, and for the presence of requiredRegex1
and requiredRegexN
, and files with name matching ignoreFilesRegex
will not be checked.
Detailed configuration:
.eslintrc.json
:
{
"plugins": [
"regex"
],
"rules": {
"regex/invalid": [
"error", [{
"regex": "invalidRegex1",
"replacement": "newValue"
}, {
"id": "regexIdN",
"message": "errorMessageN",
"regex": "invalidRegexN",
"files": {
"ignore": "ignoreFilesRegexN"
}
}
]
],
"regex/required": [
"error", [{
"id": "regexId1",
"regex": "requiredRegex1",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegex1"
}
}, {
"regex": "requiredRegexN",
"files": {
"ignore": "ignoreFilesRegexA",
"inspect": "inspectFilesRegexZ"
}
}
]
]
}
}
Files will be checked for:
invalidRegex1
but if found it will be replaced with newValue
.invalidRegexN
only in files with name not matching ignoreFilesRegexN
, but if found errorMessageN
will be shown.requiredRegex1
only in files with name matching inspectFilesRegex1
, but if not found errorMessage1
will be shown.requiredRegexN
only in files with name matching inspectFilesRegexA
and not matching ignoreFilesRegexZ
.The idea is to allow the creation of different eslint rules based on Regular Expressions in order to have some “freedom” to create quick ESLint custom rules.
Name | Fixable | Description |
---|---|---|
regex/invalid |
Yes | checks that specified patterns are not found |
regex/required |
No | checks that specified patterns are found |
Each rule defines a set of patterns:
regex/invalid
This rule checks that specified patterns are not found in files, i.e. Invalid patterns.
✏ Example of incorrect code for this rule:
/* eslint regex/invalid: ['error', ['"']] */
const text = 'Hello "My Friend"'
The error message will reflect the exact location, e.g.:
/path/to/some.js
34:25 error Invalid regular expression /"/gm found regex/invalid
✏ Example of correct code for this rule:
/* eslint regex/invalid: ['error', ['"']] */
const text = 'Hello \'My Friend\''
regex/required
This rule looks for specific patterns that must be present in each file, i.e. Required patterns.
✏ Example of incorrect code for this rule:
/* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
const text = 'Hello "My Friend"'
The error message will point to the beginning of the file, e.g.:
/path/to/some.js
1:1 error Required regular expression /^\/\/ Copyright My Friend/gm not found in file regex/required
✏ Example of correct code for this rule:
/* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
// Copyright My Friend
const text = 'Hello "My Friend"'
Both rule has two options:
[
"error",
[
"regex1",
"regexN"
],
"ignoreFilesRegex"
]
Remember, Slashes (/
) are not required in the string that defines the regex,
e.g. To get the following regex /^(test|spec)$/
, define:
"^(test|spec)$"
, when using .eslintrc.js
or .eslintrc.json
.e.g. To get the following regex /\bhttp:/
, define:
"\bhttp:"
, when using .eslintrc.js
, or"\\bhttp:"
, when using .eslintrc.json
. (backslash needs to be double in a json file)e.g. To get the following regex /.*test\.js/
, define:
".*test\.js"
, when using .eslintrc.js
, or".*test\\.js"
, when using .eslintrc.json
. (backslash needs to be double in a json file)Each pattern is specified by just a string
representing the regular expression, i.e. "regex"
{
"regex/invalid": [
"error",
[
"invalidRegex1",
"invalidRegexN"
]
],
"regex/required": [
"error",
[
"requiredRegex1",
"requiredRegexN"
]
]
}
It is specified by an object
, with the following fields:
regex
: A required string
for regex/required
and regex/invalid
representing the Regular expression to look for. [REQUIRED]flags
: A combination of flags, i
, s
and/or u
, to be used by the Regular Expression. [OPTIONAL]replacement
for regex/invalid
[1]: [OPTIONAL]
string
used to replace the invalid found pattern, orobject
that establish how the invalid found pattern will be replaced:
function
: used to replace the invalid found pattern.
text
, captured
and $
, that can be used as desired.string
value, if not, return value will be ignored.string
or function
.id
: An optional string
representing the Pattern Id. [OPTIONAL]message
: An optional string
specifying the Message to be shown when an error happens (invalid regex
is found or required regex
is not found). [OPTIONAL]files
: An optional object
specifying which files to analyze: [OPTIONAL]
ignore
: A string
representing Regular expression of the files to be ignored when validating this specific pattern.inspect
: A string
representing Regular expression of the files to be inspected when validating this specific pattern.{
"id": "regexId",
"regex": "regex",
"flags": "isu",
"replacement": "replacementString",
"message": "errorMessage",
"files": {
"ignore": "ignoreFilesRegex",
"inspect": "inspectFilesRegex"
}
}
regex
is the only Required field.- When
ignore
andinspect
are present,ignore
takes precedence.- Global ignore file pattern, takes precedence over
files
patterns.[1] In order to fix issues
eslint
must be run with--fix
option.
Using message
is pretty useful since it will give a better understanding to the developer when an error happens:
e.g. Given the following definition:
{
"regex": "someRegex",
"message": "The Useful Error MessagE"
}
then shown error will be similar to:
/path/to/some.js
1:1 error The Useful Error MessagE regex/required
or
/path/to/some.js
34:25 error The Useful Error MessagE regex/invalid
instead of
/path/to/some.js
1:1 error Required regular expression /someRegex/gm not found in file regex/required
or
/path/to/some.js
34:25 error Invalid regular expression /someRegex/gm found regex/invalid
Definition of the function must be done as a string
in 1 line, and the following rules apply:
string
value, if not, return value will be ignored, i.e. it will silently fail.return
is found at the beginning of the body of the function and the exact word return is not present, return
can be omitted.Function will receive 3 parameters, to be used as desired:
text
: a string
with the value of the invalid text found.captured
: an array
of strings with the values of the captured groups for the regex.$
: an array
of strings, with the value of the invalid text found plus the values of the captured groups for the regex, i.e.
$[0]
= text
: a string
with the value of the invalid text found.$[1..]
= captured
: an array
of strings with the values of the captured groups for the regex.
$[1]
= captured[0]
and so on.Using parameter text
e.g.
function(text, captured, $) {
return text.trim()
}
"return text.trim()"
=> only the body of the function + returns a string
value based on text
Having the following rule in .eslintrc.json
:
{
"id": "regexIdN",
"regex": "\\serror\\w*\\s",
"replacement": {
"function": "return text.trim()"
}
}
or using $
:
{
"id": "regexIdN",
"regex": "\\serror\\w*\\s",
"replacement": {
"function": "return $[0].trim()"
}
}
then, given:
example.js
const exception = " error19 "
when linting with fix, the result will be:
const exception = "error19"
As the body of the function is “simple”, i.e. the return
is found at the beginning of the body of the function, and besides, the word return is not present, then the definition could be done as:
{
"id": "regexIdN",
"regex": "\\serror\\w*\\s",
"replacement": {
"function": "text.trim()"
}
}
or
{
"id": "regexIdN",
"regex": "\\serror\\w*\\s",
"replacement": {
"function": "$[0].trim()"
}
}
Using parameter captured
e.g.
"return captured[0]"
=> only the body of the function + returns a string
value based on captured
Having the following rule in .eslintrc.json
:
{
"id": "regexIdN",
"regex": "\\serror(\\w*)\\s",
"replacement": {
"function": "return captured[0]"
}
}
or using $
:
{
"id": "regexIdN",
"regex": "\\serror(\\w*)\\s",
"replacement": {
"function": "return $[1]"
}
}
then, given:
example.js
const exception = " error19 "
when linting with fix, the result will be:
const exception = "19"
As the body of the function is “simple”, i.e. the return
is found at the beginning of the body of the function, and besides, the word return is not present, then the definition could be done as:
{
"id": "regexIdN",
"regex": "\\serror(\\w*)\\s",
"replacement": {
"function": "captured[0]"
}
}
or
{
"id": "regexIdN",
"regex": "\\serror(\\w*)\\s",
"replacement": {
"function": "$[1]"
}
}
Using parameters text
and captured
e.g.
"return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"
=> only the body of the function + returns a string
value based on text
and captured
Having the following rule in .eslintrc.json
:
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"
}
}
or using $
:
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "return $[0] + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
}
}
or :
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "return text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
}
}
or :
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "return `${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
}
}
then, given:
example.js
const sum = "4+5"
when linting with fix, the result will be:
const sum = "4+5 = 4 + 5 = 9"
As the body of the function is “simple”, i.e. the return
is found at the beginning of the body of the function, and besides, the word return is not present, then the definition could be done as:
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
}
}
or :
{
"id": "regexIdN",
"regex": "(\\d+)\\+(\\d+)",
"replacement": {
"function": "`${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
}
}
When return
keyword is required
e.g.
e.g. const result = text === 'superb' ? 'Superb' : text; return result
=> only the body of the function + returns a string
value based on text
.
Since the return
is not found at the beginning of the body of the function, return
cannot be omitted, then rule definition will be as usual:
{
"id": "regexIdN",
"regex": "\\w+",
"replacement": {
"function": "const result = text === 'superb' ? 'Superb' : text; return result"
}
}
Some cases may use Comma operator, e.g.
"function": "result = text === 'superb' ? 'Superb' : text, result"
e.g. return text === 'return' ? 'Return' : text
=> only the body of the function + returns a string
value based on text
.
Since the exact word return is present, this will required return
, then rule definition will be as usual:
{
"id": "regexIdN",
"regex": "\\w+",
"replacement": {
"function": "return text === 'return' ? 'Return' : text"
}
}
Following case does not required return
:
e.g. return text === 'Return' ? 'RETURN' : text
=> only the body of the function + returns a string
value based on text
.
Since the exact word return is not present, this will allow the following rule definition to be:
{
"id": "regexIdN",
"regex": "\\w+",
"replacement": {
"function": "text === 'Return' ? 'RETURN' : text"
}
}
console
statements to print some information in the Replacement Function.{
"regex": "\\serror(\\w*)\\s",
"replacement": {
"function": "const extract = captured[0]; console.log(extract); return extract"
}
}
The following flags can be add to the regex:
i
: For case insensitive search.s
: To allow .
to match newline characters.u
: To treat the regex as a sequence of unicode code points.To define the flags to be used, employ the field flags
in the detailed pattern:
"is"
.
"si"
."iS"
, "Is"
and "IS"
are the same.By default,
"gm"
is always added by the engine (since It’s required).
e.g.
Having the following detailed pattern:
{
"regex": "invalid",
"flags": "i"
}
Invalid
, inValid
, INvalid
or INVALID
will match.
Internally, each string from the array will be converted into a Regular Expression with global
and multiline
options, e.g.:
"someRegex"
will be transformed into /someRegex/gm
Remember that backslash needs to be double in strings of a json file, e.g. To get the following regex
/\bhttp:/
define the following string"\\bhttp:"
.
For some special cases when using meta characters that may result in an empty match, e.g. ^
, eslint-plugin-regex will report only the first case found, and after that case is fixed, the following will be report, if present.
e.g.
{
"regex": "^(?!(?:(feature|fix|docs|config|refactor|revert|test).*[\\.:]$)|(\\*\\s\\w.*\\.$)|$)"
}
/path/to/some.js
:
config(ALL):
* Use eslint-plugin-regex for commit message linting
* Use eslint-plugin-regex for commit message linting
When linting, eslint-plugin-regex
will only report the first case:
/path/to/some.js
3:1 error Invalid regular expression /^(?!(?:(feature|fix|docs|config|refactor|revert|test).*[\\.:]$)|(\\*\\s\\w.*\\.$)|$)/gm found regex/invalid
4:1 error will not be reported until 3:1 is fixed.
The issue is that having an empty match does not allow the regex engine to move forward.
The ‘Short pattern definition’ errors are reported with the following structure:
Given someRegex
, the following message will be shown on error:
Invalid regular expression /someRegex/gm found
or
Required regular expression /someRegex/gm not found in file
The ‘Detailed pattern definition’ errors are reported with the following rules:
A . If message
is present then that exact message is reported.
B . If id
is present then:
Given "id": "someRegexId"
, the following message will be shown on error:
Invalid regular expression 'someRegexId' found
or
Required regular expression 'someRegexId' not found in file
C . If neither message
nor id
is present then the ‘Short pattern definition’ error message is shown.
message
takes precedence overid
.- Although
id
is a quick solution (and useful when creating and testing a rule), usingmessage
will give more information to the team about the issue.
It is possible to use both type of definitions, ‘Short pattern definition’ with ‘Detailed pattern definition’, in the array of patterns.
.eslintrc.json
:
{
"plugins": [
"regex"
],
"rules": {
"regex/invalid": [
"error", [
"invalidRegex1",
"invalidRegex2",
{
"regex": "invalidRegex3",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegex1"
}
},
{
"id": "regexIdN",
"regex": "invalidRegexN",
"files": {
"ignore": "ignoreFilesRegexN"
}
}
]
]
}
}
invalidRegex1
and invalidRegex2
are ‘Short pattern definition’.invalidRegex3
and invalidRegexN
are ‘Detailed pattern definition’.Rules names have synonyms:
regex/invalid
= regex/invalid-warn
= regex/invalid-error
= regex/another-invalid
= regex/other-invalid
.
regex/required
= regex/required-warn
= regex/required-error
= regex/another-required
= regex/other-required
.
regex/invalid-warn
does not imply warn
level.It is possible to set different error level: error
, warn
and off
. For this use a synonym for the regex rule name:
.eslintrc.json
:
{
"plugins": [
"regex"
],
"rules": {
"regex/invalid": [
"error",
[
"invalidRegex1",
"invalidRegexN"
]
],
"regex/required": [
"error",
[
"requiredRegex1",
"requiredRegexN"
]
],
"regex/invalid-error": [
"error", [
"invalidRegexA1",
"invalidRegexA2",
{
"regex": "invalidRegexA3",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegexA1"
}
},
{
"id": "regexIdN",
"regex": "invalidRegexN",
"files": {
"ignore": "ignoreFilesRegexAN"
}
}
],
],
"regex/invalid-warn": [
"warn", [
"invalidRegexB1",
"invalidRegexB2",
{
"regex": "invalidRegexB3",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegex1"
}
},
{
"id": "regexIdN",
"regex": "invalidRegexBN",
"files": {
"ignore": "ignoreFilesRegexN"
}
}
],
],
"regex/other-invalid": [
"off", [
"invalidRegexC1",
"invalidRegexC2",
{
"regex": "invalidRegexB3",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegexC1"
}
},
{
"id": "regexIdN",
"regex": "invalidRegexBN",
"files": {
"ignore": "ignoreFilesRegexCN"
}
}
],
],
"regex/required-warn": [
"warn",
[
"requiredRegexA1",
"requiredRegexAN"
]
]
}
}
error
level: regex/invalid
and regex/invalid-error
.off
level: regex/other-invalid
.error
level: regex/required
.warn
level: regex/required-warn
.Creating and Using a Custom Set of regex rules requires using js
files.
A regex rule can be named with a custom name. The Rule name can be anything that includes invalid
, disuse
, avoid
, required
or use
, ignoring letter case, and with the restrictions of predefined names (invalid
, disuse
, avoid
, invalid-warn
, invalid-error
, another-invalid
, other-invalid
, required
, use
, required-warn
, required-error
, another-required
and other-required
).
regex/*invalid*
, regex/*disuse*
or regex/*avoid*
for invalid patterns.regex/*required*
or regex/*use*
for required patterns.In the name
invalid
,disuse
andavoid
will take precedence overrequired
anduse
, e.g. If custom regex rule name has bothavoid
anduse
in the name, then the respective regex patterns will be consider invalid patterns.
addRegexRuleName
must be used to add the custom regex rule name to the set of eslint-plugin-regex
rules.
const { addRegexRuleName } = require('eslint-plugin-regex')
addRegexRuleName('*invalid*')
addRegexRuleName('*required*')
Error: Cannot read config file: /path/to/.eslintrc.js
Error: "SomeRuleName" already defined as eslint-plugin-regex rule name
Local Custom Regex rules
Create a local .eslintrc.js
:
1 . Add rule name using addRegexRuleName
.
2 . Define eslint-plugin-regex
custom regex rule.
const { addRegexRuleName } = require('eslint-plugin-regex')
addRegexRuleName('invalid-custom-890')
module.exports = {
plugins: [ 'regex' ],
rules: {
'regex/invalid-custom-890': [
'error', [
{
regex: 'invalidRegexBN',
files: {
ignore: 'ignoreFilesRegexCN'
}
}
]
]
}
}
Custom Regex rules package
Create a custom ESLint package and add the custom regex rules with a “unique” name for each regex rule defined in the package, so it can be use with other package of regex rules or local regex rules.
Custom package index.js
:
const { addRegexRuleName } = require('eslint-plugin-regex')
addRegexRuleName('invalid-custom-890')
module.exports = {
configs: {
'someRegexRule1': {
plugins: [ 'regex' ],
rules: {
'regex/invalid-custom-890': [
'error', [
{
regex: 'invalidRegexBN',
files: {
ignore: 'ignoreFilesRegexCN'
}
}
]
]
}
}
}
}
regex/invalid-custom-890
with only 1 invalid pattern with error
as a default error level.An online example can be checked at
eslint-plugin-base-style-config
.
For more information on how to create a custom ESLint package check ESLint official documentation: Working with Plugins
then use it,
Some project .eslintrc.json
:
{ "extends": [ "plugin:the-eslint-plugin/someRegexRule1",
to change the default error level set by the package:
{
"extends": [ "plugin:the-eslint-plugin/someRegexRule1" ],
"rules": {
"regex/invalid-custom-890": "warn"
mixing with other regex rules:
{
"extends": [ "plugin:the-eslint-plugin/someRegexRule1" ],
"rules": {
"regex/invalid-custom-890": "warn",
"regex/required": [
"error",
[
"requiredRegex1",
"requiredRegexN"
]
],
"regex/invalid-error": [
"error", [
"invalidRegexA1",
"invalidRegexA2",
{
"regex": "invalidRegexA3",
"message": "errorMessage1",
"files": {
"inspect": "inspectFilesRegexA1"
}
},
{
"id": "regexIdN",
"regex": "invalidRegexN",
"files": {
"ignore": "ignoreFilesRegexAN"
}
}
],
],
Advantages
const { addRegexRuleName } = require('eslint-plugin-regex')
addRegexRuleName('required-custom-896')
then, if an error happens, the output will be something similar to:
/path/to/some.js
1:1 error Required regular expression /requiredRegex/gm not found in file regex/required-custom-896
instead of
/path/to/some.js
1:1 error Required regular expression /requiredRegex/gm not found in file regex/required
Create a custom npm package using either with json
or js
files and add the custom regex rules.
Custom package index.js
:
with complete rule definition:
module.exports = {
regex: 'invalidRegexBN',
files: {
ignore: 'ignoreFilesRegexCN'
}
}
or
module.exports = {
regex: 'invalidRegexBN',
}
or with only regex definition:
module.exports = 'invalidRegexBN'
or with multiple complete rule definition:
module.exports = {
ruleName1: {
regex: 'invalidRegex1',
files: {
ignore: 'ignoreFilesRegex1'
}
},
ruleNameN: {
regex: 'invalidRegexN',
files: {
ignore: 'ignoreFilesRegexN'
}
}
}
or
module.exports = {
ruleName1: {
regex: 'invalidRegex1',
},
ruleNameN: {
regex: 'invalidRegexN',
}
}
or with multiple only regex definition:
module.exports = {
ruleName1: 'invalidRegex1',
ruleNameN: 'invalidRegexN'
}
or using json
files:
{
"regex": "invalidRegexBN",
"files": {
"ignore": "ignoreFilesRegexCN"
}
}
or
{
"regex": "invalidRegexBN",
}
or
{
"ruleName1": {
"regex": "invalidRegex1",
"files": {
"ignore": "ignoreFilesRegex1"
}
},
"ruleNameN": {
"regex": "invalidRegexN",
"files": {
"ignore": "ignoreFilesRegexN"
}
}
}
or
{
"ruleName1": {
"regex": "invalidRegex1",
},
"ruleNameN": {
"regex": "invalidRegexN",
}
}
or
{
"ruleName1": "invalidRegex1",
"ruleNameN": "invalidRegexN"
}
Different approaches can be defined, these are only a glance. For more information on how to create a custom npm package check Contributing packages to the registry
Then use the custom package:
Some project .eslintrc.js
:
import * as SomeESLintSetOfRegexRulesPackage1 from 'the-custom-package1'
import * as SomeESLintSetOfRegexRulesPackage2 from 'the-custom-package2'
module.exports = {
plugins: ["regex"],
rules: {
"regex/invalid": [
'error', [
SomeESLintSetOfRegexRulesPackage1.ruleName1,
SomeESLintSetOfRegexRulesPackage1.ruleNameN,
SomeESLintSetOfRegexRulesPackage2.ruleName1,
SomeESLintSetOfRegexRulesPackage2.ruleNameN
]
],
or using synonyms to mix error levels:
import * as SomeESLintSetOfRegexRulesPackage1 from 'the-custom-package1'
import * as SomeESLintSetOfRegexRulesPackage2 from 'the-custom-package2'
module.exports = {
plugins: ["regex"],
rules: {
'regex/invalid-error': [
'error', [
SomeESLintSetOfRegexRulesPackage1.ruleNameN,
SomeESLintSetOfRegexRulesPackage2.ruleName1
]
],
'regex/invalid-warn': [
'warn', [
SomeESLintSetOfRegexRulesPackage1.ruleName1,
SomeESLintSetOfRegexRulesPackage2.ruleNameN
]
]
regex/invalid
vs regex/required
Both rule were design with binary approach:
regex/invalid
: pattern is not present => any presence of the specific pattern in a file is invalid.regex/required
: pattern is present => only 1 presence of the specific pattern in a file is required.Array of patterns represent different logical operation for each rule:
regex/invalid
: OR => the presence in a file of any of the patterns defined in the array is invalid.regex/required
: AND => the presence in file of all of the patterns defined in the array is required.Check:
eslint-plugin-base-style-config
CHANGELOG.md
: contains the information about changes in each version, chronologically ordered (Keep a Changelog).
Don’t forget:
At life:
At work: